1. 程式人生 > >C++ 實現 傳送HTTP Get/Post請求

C++ 實現 傳送HTTP Get/Post請求

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;
}