首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

MFC Socket 編程:HTTP 響應體提取與文件寫入「0KB」問題根因分析

     編輯:程式設計綜合問答

問題現象

使用 `CAsyncSocket`/`CSocket` 接收 HTTP 響應,成功解析出 Body 字符串 `"testtest"`,但寫入文件後大小為 0KB。調試時變量值正確,實際落盤為空。

代碼邏輯回顧

resp_len = recv(sock, (char*)&buffer, BUFFER_SIZE, 0);
npos = string(buffer).find("\r\n\r\n", 0); // 定位 Header 結束

if (resp_len > 0) {
    if (npos >= resp_len - 1 && flag) { /* Header 未接完 */ }
    else if (npos < resp_len - 1 && flag) { /* 首包含 Body 部分 */
        ofstream of("test.txt");
        of << string(buffer).substr(npos + 4, resp_len - npos - 4);
        flag = 0;
    } else { /* 後續包全是 Body */
        of << string(buffer).substr(0, resp_len);
    }
}

核心根因:TCP 粘包/分包導致「單次 recv 不能保證收到完整 Body」

  • HTTP 響應可能被拆分為多個 TCP 段發送
  • 代碼邏輯假設「首包包含 Header 結束符 `\r\n\r\n` 且 Body 完整」,實則 Body 可能跨越多個 `recv`
  • 文件流 `ofstream of("test.txt")` 在首包分支中每次打開都會截斷文件,後續包進入 `else` 分支寫入,但若後續包未及時到達或邏輯漏寫,文件僅含首包 Body 片段甚至為空

正確處理流程:狀態機 + 內容長度驅動

1. 完整接收緩衝區

string total_response;
char buf[4096];
int len;
while ((len = recv(sock, buf, sizeof(buf), 0)) > 0) {
    total_response.append(buf, len);
    // 可選:根據 Content-Length 判斷是否接收完畢
}

2. 解析 Header 獲取 Content-Length

size_t header_end = total_response.find("\r\n\r\n");
if (header_end == string::npos) return; // 不完整

string header = total_response.substr(0, header_end);
string body = total_response.substr(header_end + 4);

// 簡易提取 Content-Length
regex cl_regex("Content-Length:\\s*(\\d+)", regex::icase);
smatch match;
size_t expected_len = 0;
if (regex_search(header, match, cl_regex)) 
    expected_len = stoul(match[1].str());

// 若 body 長度不足,繼續 recv 補齊
while (body.length() < expected_len) {
    len = recv(sock, buf, sizeof(buf), 0);
    if (len <= 0) break;
    body.append(buf, len);
}

3. 一次性寫入文件

ofstream of("test.txt", ios::binary);
of.write(body.c_str(), body.length());
of.close();

關鍵修正點總結

錯誤做法正確做法
邊收邊寫,多次打開文件流全量接收後,單次二進制寫入
假設首包含完整 Body根據 Content-Length 或 Chunked 判斷完整性
忽略 TCP 流無邊界特性應用層協議(HTTP)定義邊界
文本模式寫入二進制數據使用 `ios::binary` 避免換行符轉換

進階建議

  • 生產環境請使用成熟庫(libcurl、cpp-httplib、Boost.Beast)而非手寫 Socket
  • 支持 Chunked Transfer Encoding、Gzip 解壓、HTTPS (OpenSSL)
  • 設置接收超時 `setsockopt(SO_RCVTIMEO)` 防止死循環

總結

「調試時字符串正確,文件卻 0KB」經典現象,本質是TCP 流式傳輸與應用層邏輯不匹配。掌握「緩衝全量接收 → 協議解析長度 → 按長度截取/補齊 → 一次性落盤」這套標準流程,能解決 99% 的 Socket 文件下載問題。

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