用C++實現http代理
為了幫網友些個用http下載動畫的程序,臨時在網上翻了翻,看看有沒有利用http代理來下載的例子。結果,似乎很多人都愿意去轉載一個有頭無尾的例子,還美其名曰“我在查閱RFC文檔和相關資料后,特總結一些TCP協議穿透代理服務器的程序片斷,希望對大家有所幫助。”如果真的想幫助大家,為什么不說的詳細一些?
無奈之下,自己去翻rfc文檔,找了些資料,寫了這個利用http代理來下載文件的資料
代碼如下:
(1)一些基本變量
SOCKET HTTPSocket; // 主socket
strUCt sockaddr_in SocketAddr; // address socket
struct sockaddr_in BindSocket; // for bind
int m_nRecvTimeout; // recieve timeout
int m_nSendTimeout; // send timeout
WSADATA wsaData;
// 要下載文件部分。好像在BindSocket.sin_addr.s_addr = inet_addr (strHost);時,只能使用ip地址,所以了。。。
// 如果誰知道更好的方法,別忘了告訴我一下。
CString strHost="111.111.111.111 ";
CString DownLoadAddress="http://www.aitenshi.com/bbs/images/";
CString hostFile="logo.gif";
int HttpPort=80;
(2)一些函數,用來取得http頭,和獲取文件大小
int GetFileLength(char *httpHeader)
{
CString strHeader;
int local;
strHeader=(CString)httpHeader;
local=strHeader.Find("Content-Length",0);
local+=16;
strHeader.Delete(0,local);
local=strHeader.Find("\r");
strHeader.SetAt(local,'\0');
char temp;
strcpy(temp,strHeader.GetBuffer(strHeader.GetLength()));
return atoi(temp);
}
int GetHttpHeader(SOCKET sckDest,char *str)
{
BOOL m_bResponsed=0;
int m_nResponseHeaderSize;
if(!m_bResponsed)
{
char c = 0;
int nIndex = 0;
BOOL bEndResponse = FALSE;
while(!bEndResponse && nIndex < 1024)
{
recv(sckDest,&c,1,0);
str = c;
if(nIndex >= 4)
{
if(str == '\r' && str == ''
&& str == '\r' && str == '')
bEndResponse = TRUE;
}
}
m_nResponseHeaderSize = nIndex;
m_bResponsed = TRUE;
}
return m_nResponseHeaderSize;
}
(3)用來發送的部分
void szcopy(char* dest,const char* src,int nMaxBytes)
{
int i_cntr=0;
while ((src!='\0') (i_cntr dest=src;
dest='\0';
}
BOOL SocketSend(SOCKET sckDest,const char* szHttp)
{
char szSendHeader;
int iLen=strlen(szHttp);
szcopy(szSendHeader,szHttp,iLen);
if(send (sckDest ,(const char FAR *)szSendHeader ,iLen ,0)==SOCKET_ERROR)
{
closesocket(sckDest);
AfxMessageBox("Error when send");
return FALSE;
}
return TRUE;
}
BOOL SocketSend(SOCKET sckDest,CString szHttp)
{
int iLen=szHttp.GetLength();
if(send (sckDest,szHttp,iLen,0)==SOCKET_ERROR)
{
closesocket(sckDest);
AfxMessageBox("Error when send");
return FALSE;
}
return TRUE;
}
(4)用于連接的函數
這里是做了一些連接用的操作,分了兩種情況
1)如果沒有使用代理,則直接連到你指定的計算機
2)如果使用了代理,則直接連到代理
BOOL CDLAngelDlg::ConnectHttp()
{
message="正在建立連接";
UpdateData(TRUE);
if(m_combo=="HTTP") // m_combo 一個下拉條
{
HTTPSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
SocketAddr.sin_addr.s_addr = inet_addr (m_ProxyAddr);
SocketAddr.sin_family=AF_INET;
SocketAddr.sin_port=htons(atoi(m_Port));
struct fd_set fdSet;
struct timeval tmvTimeout={0L,0L};
FD_ZERO(&fdSet);
FD_SET(HTTPSocket, &fdSet);
if (select(0,&fdSet,NULL,NULL,&tmvTimeout)==SOCKET_ERROR)
{
closesocket(HTTPSocket);
AfxMessageBox("Error when select.");
return 0;
}
if (connect(HTTPSocket, (const struct sockaddr *)&SocketAddr, sizeof(SocketAddr))==SOCKET_ERROR)
{
message="代理連接失敗";
m_message.CleanText();
m_message.AddText(message);
return 0;
}
// 發送CONNCET請求令到代理服務器,用于和代理建立連接
//代理服務器的地址和端口放在m_ProxyAddr,m_Port 里面
CString temp;
char tmpBuffer;
temp.Format("CONNECT %s:%s HTTP/1.1\rUser-Agent: MyApp/0.1\r\r",m_ProxyAddr,m_Port);
if(!SocketSend(HTTPSocket,temp))
{
message="連接代理失敗";
return 0;
}
// 取得代理響應,如果連接代理成功,代理服務器將返回200 Connection established
GetHttpHeader(HTTPSocket,tmpBuffer);
temp=tmpBuffer;
if(temp.Find("HTTP/1.0 200 Connection established",0)==-1)
{
message="連接代理失敗";
return 0;
}
message="代理連接完成";
m_message.AddText("代理連接完成");
return 1; // ----------〉這里是應該注意的,連接到代理后,就可以返回了,不需要再連接網上的另外一臺機,代理服務器會自動轉發數據,所以,連接完代理就像連接到網上另外一臺機一樣
}
// 這個,是為了給其他代理做準備
else if(m_combo=="Socks4")
{MessageBox("請注意,現在無法使用代理功能!");}
else if(m_combo=="Socks5")
{MessageBox("請注意,現在無法使用代理功能!");}
// 如果沒有使用代理,就要連接到網上的另一臺機
// 準備socket
HTTPSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (HTTPSocket==INVALID_SOCKET)
{
AfxMessageBox("Error when socket");
return 0;
}
//設置超時
struct linger zeroLinger;
zeroLinger.l_onoff = 1;
zeroLinger.l_linger = 0;
if(setsockopt(HTTPSocket,SOL_SOCKET,SO_LINGER
,(const char *)&zeroLinger
,sizeof(zeroLinger))!=0)
{
closesocket(HTTPSocket);
AfxMessageBox("Error when setscokopt(LINGER)");
return 0;
}
//設置接收超時
if(setsockopt(HTTPSocket,SOL_SOCKET,SO_RCVTIMEO
,(const char *)&m_nRecvTimeout
,sizeof(m_nRecvTimeout))!=0)
{
closesocket(HTTPSocket);
AfxMessageBox("Error when setsockopt(RCVTIME).");
return 0;
}
//設置發送超時
if(setsockopt(HTTPSocket,SOL_SOCKET,SO_SNDTIMEO
,(const char *)&m_nSendTimeout
,sizeof(m_nSendTimeout))!=0)
{
closesocket(HTTPSocket);
AfxMessageBox("Error when setsockopt(SNDTIMEO).");
return 0;
}
SocketAddr.sin_addr.s_addr = htonl (INADDR_ANY);
SocketAddr.sin_family=AF_INET;
// 進行端口綁定
if (bind (HTTPSocket,
(const struct sockaddr FAR *)&SocketAddr,
sizeof(SocketAddr))==SOCKET_ERROR)
{
closesocket(HTTPSocket);
AfxMessageBox("Error when bind socket.");
return 0;
}
//準備連接
/// 準備連接信息
BindSocket.sin_addr.s_addr = inet_addr (strHost);
BindSocket.sin_family=AF_INET;
BindSocket.sin_port=htons(HttpPort);
struct fd_set fdSet;
struct timeval tmvTimeout={0L,0L};
FD_ZERO(&fdSet);
FD_SET(HTTPSocket, &fdSet);
if (select(0,&fdSet,NULL,NULL,&tmvTimeout)==SOCKET_ERROR)
{
closesocket(HTTPSocket);
AfxMessageBox("Error when select.");
return 0;
}
// 連接
if (connect(HTTPSocket, (const struct sockaddr *)&BindSocket, sizeof(BindSocket))==SOCKET_ERROR)
{
AfxMessageBox("第一次連接失敗,準備第二次連接");
if (connect(HTTPSocket
,(const struct sockaddr *)&BindSocket
,sizeof(BindSocket))==SOCKET_ERROR)
{
closesocket(HTTPSocket);
AfxMessageBox("連接失敗");
return 0;
}
}
message="連接完成";
return 1;
}
(5)發送http請求,為下載數據進行準備
int CDLAngelDlg::SendHttpHeader()
{
//進行下載
CString temp;
BOOL bReturn;
char tmpBuffer;
///第1行:方法,請求的路徑,版本
temp="GET "+DownLoadAddress+hostFile+" HTTP/1.0\r";
bReturn=SocketSend(HTTPSocket,temp);
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
///第2行:主機
temp="Host "+strHost+"\r";
bReturn=SocketSend(HTTPSocket,temp);
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
///第3行:接收的數據類型
bReturn=SocketSend(HTTPSocket,"Accept: */*\r");
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
///第4行:
temp=DownLoadAddress;
temp.Insert(0,"Referer ");
temp+="\r";
bReturn=SocketSend(HTTPSocket,temp);
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
///第5行:瀏覽器類型
bReturn=SocketSend(HTTPSocket,"User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt; DTS Agent;)\r");
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
///第6行:連接設置,保持
// SocketSend(HTTPSocket,"Connection:Keep-Alive\r");
///第7行:Cookie.
bReturn=SocketSend(HTTPSocket,"Cache-Control: no-cache\r");
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
bReturn=SocketSend(HTTPSocket,"Proxy-Connection: Keep-Alive\r");
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
/// 續傳
Range是要下載的數據范圍,對續傳很重要
if(continueFlag)
{
temp.Format("Range: bytes=%d- \r",conLength);
bReturn=SocketSend(HTTPSocket,temp);
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
}
///最后一行:空行
bReturn=SocketSend(HTTPSocket,"\r");
if(!bReturn)
{
message="發送請求失敗";
return 0;
}
///取得http頭
int i;
i=GetHttpHeader(HTTPSocket,tmpBuffer);
if(!i)
{
message="獲取HTTP頭出錯";
return 0;
}
//如果取得的http頭含有404等字樣,則表示連接出問題
temp=tmpBuffer;
if(temp.Find("404")!=-1)
{
return 0;
}
// 得到待下載文件的大小
filelength=GetFileLength(tmpBuffer);
return 1;
}
這樣,就連接到網上的另一臺機了,如何下載數據,不用多說了吧
while((num!=SOCKET_ERROR) && (num!=0))
{
num=recv (HTTPSocket
,(char FAR *)tmpBuffer
,(MAXBLOCKSIZE-1)
,0);
file.Write(tmpBuffer,num);
if(ExitFlag)
{
file.Close();
closesocket(HTTPSocket);
DownComplete=1;
m_message.CleanText();
m_message.ShowColorText(RGB(128,128,0),DLCompleteMes);
m_progress.ShowWindow(SW_HIDE);
m_stopDownload.ShowWindow(SW_HIDE);
_endthread();
}
}
基本就是這樣了,本人寫程序水平也不是很高,這個程序還是可以用的。
页:
[1]