// HttpClient.h: HTTP协议客户端的实现类 // //////////////////////////////////////////////////////////////////////
#if !defined(_HTTPCLIENT_H__) #define _HTTPCLIENT_H__
#include "../comm/tcp.h"
#pragma warning(disable : 4786)
#include <map> #include <string> using namespace std;
#define BUFFER_SIZE 8192 #define HEADS_BUFFER_SIZE 2048 #define PEEK_SIZE 500 #define TCP_TIMEOUT 5
class CHttpClient { public: void Cancel(); const char* GetStatusString(); CHttpClient(); virtual ~CHttpClient();
bool Download(char* url,char* fname); //直接下载到文件 bool Download(char* url,HWND hParent,long msg = WM_USER+1); //消息方式下载数据流
inline long Instr(char* from,char* find); virtual void OnDataRecived(char* data,long datasize,int first = false ); long GetHostName(char *url, char *buf); int GetStatus(); long GetHeader(char* headname,char* buf); long GetHeader(char* from,char* searchfor,char* buf); bool Reset(); private: long m_Message; HWND m_hParent; bool m_noCancel; char m_fname[BUFFER_SIZE]; int m_status; char m_heads_buffer[HEADS_BUFFER_SIZE]; char m_buffer[BUFFER_SIZE+1]; CTcp tcp;
map<const int,string> m_map; };
#endif
//---------------------------------------------------------------- // HttpClient.cpp: implementation of the CHttpClient class. // //////////////////////////////////////////////////////////////////////
#include "stdafx.h" #include "HttpClient.h"
#ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif
////////////////////////////////////////////////////////////////////// // Construction/Destruction //////////////////////////////////////////////////////////////////////
CHttpClient::CHttpClient() { Reset(); memset(m_fname,0,BUFFER_SIZE); tcp.init(); m_map[100]= "Continue"; m_map[101]= "Switching Protocols"; m_map[200]= "OK"; m_map[201]= "Created"; m_map[202]= "Accepted"; m_map[203]= "Non-Authoritative Information"; m_map[204]= "No Content"; m_map[205]= "Reset Content"; m_map[206]= "Partial Content"; m_map[300]= "Multiple Choices"; m_map[301]= "Moved Permanently"; m_map[302]= "Found"; m_map[303]= "See Other"; m_map[304]= "Not Modified"; m_map[305]= "Use Proxy"; m_map[307]= "Temporary Redirect"; m_map[400]= "Bad Request"; m_map[401]= "Unauthorized"; m_map[402]= "Payment Required"; m_map[403]= "Forbidden"; m_map[404]= "Not Found"; m_map[405]= "Method Not Allowed"; m_map[406]= "Not Acceptable"; m_map[407]= "Proxy Authentication Required"; m_map[408]= "Request Time-out"; m_map[409]= "Conflict"; m_map[410]= "Gone "; m_map[411]= "Length Required"; m_map[412]= "Precondition Failed"; m_map[413]= "Request Entity Too Large"; m_map[414]= "Request-URI Too Large"; m_map[415]= "Unsupported Media Type"; m_map[416]= "Requested range not satisfiabl"; m_map[417]= "Expectation Failed"; m_map[500]= "Internal Server Error"; m_map[501]= "Not Implemented"; m_map[502]= "Bad Gateway"; m_map[503]= "Service Unavailable"; m_map[504]= "Gateway Time-out"; m_map[505]= "HTTP Version not supported"; }
CHttpClient::~CHttpClient() {
}
bool CHttpClient::Reset() { m_noCancel=true; m_hParent=NULL; m_Message=0; memset(m_buffer,0,BUFFER_SIZE); memset(m_heads_buffer,0,HEADS_BUFFER_SIZE); return 1; }
//HTTP下载主程序 bool CHttpClient::Download(char *url, HWND hParent,long msg) { m_Message=m_Message; m_hParent=hParent;
//状态为忙
Reset(); char hostname[256]; memset(hostname,0,256); GetHostName(url,hostname);
long AllSize=0; long ReadSize=0; bool headFinished=false;
int s; s=tcp.connect(hostname,80,TCP_TIMEOUT,1); if(s<0) return false;
sprintf(m_buffer,"GET %s HTTP/1.0 \r\nHost:%s\r\nAccept:*/*\r\nUser-Agent: AutoUpdate/1.0 \r\n\r\n",url,hostname); tcp.send(s,m_buffer,strlen(m_buffer),TCP_TIMEOUT);
//先取PEEK_SIZE个字节,这样可以确保取到Content-Length ReadSize=tcp.recv(s,m_buffer,PEEK_SIZE,TCP_TIMEOUT); if(ReadSize==-1) return false; m_buffer[ReadSize]=0;
memset(hostname,0,256);
int m,n; m=Instr(m_buffer," "); n=Instr(m_buffer+m+1," "); memcpy(hostname,m_buffer+m+1,n);
m_status=atoi(hostname); if(m_status!=200)return false;
memset(hostname,0,256); if(GetHeader(m_buffer,"Content-Length",hostname)<0) return false;
//取得内容总长度 AllSize=atol(hostname); memset(hostname,0,256);
//寻找HTTP数据区表示 int i; i=Instr(m_buffer,"\r\n\r\n"); if(i>0) { memcpy(m_heads_buffer,m_buffer,i+4); headFinished=true; ReadSize=ReadSize-i-4; OnDataRecived(m_buffer+i+4,ReadSize,true); }
while(ReadSize<AllSize && m_noCancel) { int nSize; if(headFinished) { nSize=(AllSize-ReadSize)>BUFFER_SIZE?BUFFER_SIZE:(AllSize-ReadSize); memset(m_buffer,0,BUFFER_SIZE); if(tcp.recv(s,m_buffer,nSize,TCP_TIMEOUT)!=nSize) return false; ReadSize+=nSize; m_buffer[nSize]=0;
OnDataRecived(m_buffer,nSize); } else { nSize=(AllSize-ReadSize)>(BUFFER_SIZE-ReadSize)?(BUFFER_SIZE-ReadSize):(AllSize-ReadSize); if(tcp.recv(s,m_buffer+ReadSize,nSize,TCP_TIMEOUT)!=nSize) return false; ReadSize+=nSize; m_buffer[nSize]=0; //寻找HTTP数据区表示 i=Instr(m_buffer,"\r\n\r\n"); if(i>0) { memcpy(m_heads_buffer,m_buffer,i+4); headFinished=true; ReadSize=ReadSize-i-4; OnDataRecived(m_buffer+i+4,ReadSize,true); } } } if(!m_noCancel)remove(m_fname);
tcp.close(s); return true; }
int CHttpClient::GetStatus() { return m_status; }
//从URL中取出主机名 long CHttpClient::GetHostName(char *url, char *buf) { int i,j; char* lpsz = _tcsstr(url, "//"); if(!lpsz)return -1; i=lpsz-url+2;
lpsz= _tcsstr(url+i, "/"); if(!lpsz)return -1; j=lpsz-url; memcpy(buf,url+i,j-i); return j-i; }
long CHttpClient::GetHeader(char *headname, char *buf) { return GetHeader(m_heads_buffer,headname,buf); }
long CHttpClient::GetHeader(char *from, char *headname, char *buf) { char *p; char* lpsz = _tcsstr(from, headname); if(!lpsz)return -1;
//lpsz++; p=lpsz+strlen(headname)+2; lpsz = _tcsstr(p, "\r\n");
if(!lpsz)return -1;
memcpy(buf,p,lpsz-p);
return strlen(buf); }
void CHttpClient::OnDataRecived(char* data,long datasize ,int first) {
if(m_hParent) ::SendMessage(m_hParent,m_Message,(unsigned int)data,datasize); else { if(first)remove(m_fname); FILE* f=fopen(m_fname,"ab"); if(f){ fwrite(data,1,datasize,f); fclose(f); } } //处理数据,一定要拷贝走哦,缓冲区可是循环使用的 }
inline long CHttpClient::Instr(char *from, char *find) { char* lpsz = _tcsstr(from, find); return (!lpsz)?-1:lpsz-from; }
bool CHttpClient::Download(char *url, char *fname) { strcpy(m_fname,fname); return Download(url,NULL,NULL); }
const char* CHttpClient::GetStatusString() { return m_map[m_status].c_str(); }
void CHttpClient::Cancel() { m_noCancel=false; }

|