1、簡述
最近簡單看了一下關於HTTP請求方面的知識,之前一直用Qt來實現,有專門HTTP請求的QNetworkAccessManager類來處理,實現也比較簡單,這里主要講解一下用C++代碼來實現HTTP 的Get/Post請求。
一個HTTP請求報文由請求行(request line)、請求頭(header)、和請求數據*3個部分組成,注意請求頭部分和請求數據中間需要加上“\r\n”*。下圖給出了請求報文的一般格式。
(1)請求行
請求行包括請求方法、URL、和HTTP協議版本三個部分。
HTTP協議的請求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。這里介紹最常用的GET方法和POST方法。
GET:當客戶端要從服務器中讀取文檔時,使用GET方法。GET方法要求服務器將URL定位的資源放在響應報文的數據部分,回送給客戶端。使用GET方法時,請求參數和對應的值附加在URL后面,利用一個問號(“?”)代表URL的結尾與請求參數的開始,傳遞參數長度受限制。例如,/index.jsp?id=100&op=bind。
POST:當客戶端給服務器提供信息較多時可以使用POST方法。POST方法將請求參數封裝在HTTP請求數據中,以名稱/值的形式出現,可以傳輸大量數據,可用來傳送文件。
(2)請求頭部
請求頭部由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號“:”分隔。請求頭部通知服務器有關於客戶端請求的信息,典型的請求頭有:
User-Agent:產生請求的瀏覽器類型。
Accept:客戶端可識別的內容類型列表。
Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機。
(3)關於請求頭與請求數據中間的空行
請求頭之后是一個空行,需要添加 回車符和換行符——“\r\n”,通知服務器以下不再有請求頭。
對於一個完整的http請求來說空行是必須的,否則服務器會認為本次請求的數據尚未完全發送到服務器,處於等待狀態。
(4)請求數據
請求數據用於Post方法中。與請求數據相關的最常使用的請求頭是Content-Type和Content-Length。
2、代碼之路
發送Get請求
BOOL GetIpByDomainName(char *szHost, char* szIp) { WSADATA wsaData; HOSTENT *pHostEnt; int nAdapter = 0; struct sockaddr_in sAddr; if (WSAStartup(0x0101, &wsaData)) { printf(" gethostbyname error for host:\n"); return FALSE; } pHostEnt = gethostbyname(szHost); if (pHostEnt) { if (pHostEnt->h_addr_list[nAdapter]) { memcpy(&sAddr.sin_addr.s_addr, pHostEnt->h_addr_list[nAdapter], pHostEnt->h_length); sprintf(szIp, "%s", inet_ntoa(sAddr.sin_addr)); } } else { // DWORD dwError = GetLastError(); // CString csError; // csError.Format("%d", dwError); } WSACleanup(); return TRUE; } void sendGetRequest() { //開始進行socket初始化; WSADATA wData; ::WSAStartup(MAKEWORD(2,2),&wData); SOCKET clientSocket = socket(AF_INET,1,0); struct sockaddr_in ServerAddr = {0}; int Ret=0; int AddrLen=0; HANDLE hThread=0; char *bufSend = "Get /check?+參數 HTTP/1.1\r\n" "Connection:Keep-Alive\r\n" "Accept-Encoding:gzip, deflate\r\n" "Accept-Language:zh-CN,en,*\r\n" "host:www.baidu.com\r\n" "User-Agent:Mozilla/5.0\r\n\r\n"; char addIp[256] = {0}; GetIpByDomainName("www.baidu.com" , addIp); ServerAddr.sin_addr.s_addr = inet_addr(addIp); ServerAddr.sin_port = htons(80);; ServerAddr.sin_family = AF_INET; char bufRecv[3069] = {0}; int errNo = 0; errNo = connect(clientSocket,(sockaddr*)&ServerAddr,sizeof(ServerAddr)); if(errNo==0) { //如果發送成功,則返回發送成功的字節數; if(send(clientSocket,bufSend ,strlen(bufData),0)>0) { cout<<"發送成功\n";; } //如果接受成功,則返回接受的字節數; if(recv(clientSocket,bufRecv,3069,0)>0) { cout<<"接受的數據:"<<bufRecv<<endl; } } else { errNo=WSAGetLastError(); } //socket環境清理; ::WSACleanup(); }
發送Post請求
// post請求只需將上面的代碼替換一下就可以使用
char *bufSend = "POST /check HTTP/1.1\r\n" "Connection:Keep-Alive\r\n" "Accept-Encoding:gzip, deflate\r\n" "Accept-Language:zh-CN,en,*\r\n" "Content-Length:114\r\n" "Content-Type:application/x-www-form-urlencoded; charset=UTF-8\r\n" "host:tmalarm.vemic.com\r\n" "User-Agent:Mozilla/5.0\r\n\r\n" "請求數據\r\n\r\n";
Post 請求也可以將請求數據寫在請求行中,跟Get請求一樣。
關於是否成功發送 Get/Post 請求
我們可以借助抓包工具看 我們發送的請求是否成功 ,可以去網上下載 HttpAnalyzerStdV7軟件進行抓包,由返回的結果得出是否請求成功。
關於發送請求中 請求數據或者請求參數帶 中文字符 出現亂碼
我們程序中編碼格式一般為Unicode編碼,與HTTP服務器(UTF-8)所用編碼不一樣,這里就需要給中文字符轉換編碼格式。關於Unicode 編碼與 UTF-8編碼問題 可以看一下這篇文章 C++中 Unicode 與 UTF-8 編碼互轉 。
char *bufSend = "Get /check?&name=%s&password=%s HTTP/1.1\r\n" "Connection:Keep-Alive\r\n" "Accept-Encoding:gzip, deflate\r\n" "Accept-Language:zh-CN,en,*\r\n" "host:www.baidu.com\r\n" "User-Agent:Mozilla/5.0\r\n\r\n"; CString cStrName = L"前行中的小豬"; const char* cName; // UnicodeToUtf8方法將Unicode編碼轉為UTF-8格式。 cName = UnicodeToUtf8(cStrName); char* passWord = "123456"; char bufData[400] = {0}; sprintf_s(bufData , 400 , bufSend , cName , passWord); //這里最終將中文轉為UTF-8格式的結果保存在 bufData 數組中。
// Unicode 轉 UTF-8 char* UnicodeToUtf8(const wchar_t* unicode) { int len; len = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL); char *szUtf8 = (char*)malloc(len + 1); memset(szUtf8, 0, len + 1); WideCharToMultiByte(CP_UTF8, 0, unicode, -1, szUtf8, len, NULL, NULL); return szUtf8; }
http://blog.csdn.net/goforwardtostep/article/details/53318760