首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

WinInet HttpSendRequest 實戰:構造標準 HTTP 請求頭避坑指南

     編輯:關於C語言

使用 Windows WinInet API (InternetOpen → InternetConnect → HttpOpenRequest → HttpAddRequestHeaders → HttpSendRequest) 發送 POST 請求時,請求頭構造稍有不慎即導致 400 Bad Request。本文通過完整可編譯代碼,詳解請求頭格式規範、常見錯誤與 Wireshark 抓包對比分析法。

完整發送流程代碼 (VC++ / Unicode)

// 1. 檢查連通性
LPCTSTR lpURL = _T("https://192.168.8.72:8080");
if (!::InternetCheckConnection(lpURL, FLAG_ICC_FORCE_CONNECTION, 0)) return;

// 2. 打開會話
HINTERNET hOpen = ::InternetOpen(_T("client.exe"),
    INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, NULL, NULL, 0);
if (!hOpen) return;

// 3. 連接服務器 (注意端口 8080)
LPCTSTR lpDomain = _T("192.168.8.72");
HINTERNET hConnect = ::InternetConnect(hOpen, lpDomain, 8080,
    NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if (!hConnect) { InternetCloseHandle(hOpen); return; }

// 4. 創建請求句柄
LPCTSTR szAccept[] = { _T("*/*"), NULL };
DWORD dwFlags = INTERNET_FLAG_NO_CACHE_WRITE;
HINTERNET hRequest = ::HttpOpenRequest(hConnect, _T("POST"),
    _T("/oss/client/analysis.g"), _T("HTTP/1.1"),
    lpURL, szAccept, dwFlags, 0);
if (!hRequest) goto CLEANUP_CONNECT;

// 5. 準備 POST 體 (UTF-8)
TCHAR szPostData[] = _T("this is test data from client");
char utf8PostData[1024] = {0};
strcpy(utf8PostData, dataConvert.TCharToUTF8(szPostData)); // 需自行實現轉換

// 6. 構造請求頭 —— 核心避坑區
TCHAR hdrLang[]       = _T("Accept-Language: zh-CN,zh;q=0.8");
TCHAR hdrEnc[]        = _T("Accept-Encoding: gzip,deflate,sdch");
TCHAR hdrCharset[]    = _T("Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3");
TCHAR hdrType[]       = _T("Content-Type: text/xml");
TCHAR hdrHost[]       = _T("Host: 192.168.8.72:8080");
TCHAR hdrOrigin[]     = _T("Origin: https://192.168.8.72:8080");
TCHAR hdrReferer[]    = _T("Referer: https://192.168.8.72:8080/oss/client/create.g");
TCHAR hdrLen[64];
_stprintf(hdrLen, _T("Content-Length: %d\r\n\r\n"), strlen(utf8PostData)); // 雙 CRLF 結尾!

// 7. 逐個添加頭部 (ADD | REPLACE)
auto addHdr = [&](LPCTSTR h) {
    HttpAddRequestHeaders(hRequest, h, -1,
        HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
};
addHdr(hdrLang); addHdr(hdrEnc); addHdr(hdrCharset);
addHdr(hdrType); addHdr(hdrHost); addHdr(hdrOrigin);
addHdr(hdrReferer); addHdr(hdrLen);

// 8. 發送請求
BOOL bRet = ::HttpSendRequest(hRequest, NULL, 0,
    utf8PostData, strlen(utf8PostData));
if (!bRet) { DWORD err = GetLastError(); goto CLEANUP_REQUEST; }

// 9. 讀取狀態碼與響應
TCHAR szBuf[256]; DWORD dwSize = sizeof(szBuf);
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, szBuf, &dwSize, NULL);
int nStatus = _tstoi(szBuf);
if (nStatus >= 200 && nStatus <= 206) {
    // 讀取響應體...
    InternetReadFile(hRequest, szBuf, sizeof(szBuf), &dwSize);
}

CLEANUP_REQUEST:
InternetCloseHandle(hRequest);
CLEANUP_CONNECT:
InternetCloseHandle(hConnect);
InternetCloseHandle(hOpen);

請求頭構造「三大鐵律」

規則錯誤示例正確示例原因
每個頭部單獨調用 HttpAddRequestHeaders拼接成單一大字串一次傳入逐行 addHdr(hdrXxx)WinInet 內部解析器要求獨立行
頭部值不帶 \r\n,最後一個頭部必須以雙 \r\n\r\n 結尾_T("Host: x\r\n")_T("Host: x") … 最後 _T("Content-Length: 123\r\n\r\n")雙 CRLF 標誌頭部結束、體開始
Content-Length 必須為 Body 實際位元組長度 (UTF-8 後)用 _tcslen(szPostData) (字符數)strlen(utf8PostData)HTTP 傳輸按字節計算

Wireshark 抓包對比調試法 (終極武器)

  1. 瀏覽器訪問目標 URL → Wireshark 捕獲 http.request → 導出為 browser.txt。
  2. 程式發送請求 → 捕獲 → 導出 program.txt。
  3. 用 Beyond Compare / diff 對比兩文件:
    ▸ Request Line (方法/路徑/版本)
    ▸ Header 名大小寫、順序、值格式
    ▸ 是否缺 Host/Content-Length/Expect: 100-continue
    ▸ Body 編碼 (UTF-8 vs ANSI) 差異
  4. 逐項修正代碼,直到兩請求在 WireShark 中完全一致 (除動態值如 Cookie/Date)。

常見 400/411/417 錯誤對應表

狀態碼典型原因修正動作
400 Bad Request頭部格式錯、缺雙 CRLF、含不可見字符對照 Wireshark 逐行核對
411 Length RequiredPOST 缺 Content-Length確保最後頭部含 Content-Length: N\r\n\r\n
417 Expectation FailedWinInet 默認加 Expect: 100-continue,服務端不支援顯式添加 Expect: (空值) 覆蓋默認

最佳實踐清單

  • **統一編碼**:內部統一 UTF-8,發送前 WideCharToMultiByte(CP_UTF8)。
  • **封裝類**:將上述流程封裝為 HttpClient 類,暴露 Post(url, headers, body)。
  • **超時控制**:InternetSetOption(hRequest, INTERNET_OPTION_SEND_TIMEOUT, ...) / RECEIVE_TIMEOUT。
  • **HTTPS 支援**:INTERNET_FLAG_SECURE + INTERNET_FLAG_IGNORE_CERT_CN_INVALID (測試環境)。
  • **錯誤碼記錄**:GetLastError() 對照 wininet.h / winerror.h 記錄日誌。

來源:零一小築博客

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved