You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Winsocket获取数据时正文前后出现异常字符问题求助

问题原因:HTTP分块传输编码(Chunked Transfer Encoding)

你看到的9、0还有a这些字符并不是奇怪的乱码,而是HTTP分块传输编码的标记。你的服务器响应头里明确写了Transfer-Encoding: chunked,这意味着服务器会把响应内容拆分成多个块来发送,每个块的格式是:

  • 一行十六进制数字,表示当前块的字节长度
  • 紧接着是块的实际内容
  • 最后用\r\n结束这个块
  • 当块长度为0时,表示所有内容传输完成

比如你收到的响应:

HTTP/1.1 200 OK Date: Tue, 09 Jan 2018 15:02:01 GMT Server: Apache Connection: close Transfer-Encoding: chunked Content-Type: text/html 9 hello PHP 0

拆解一下:

  • 9 是十六进制的块大小,对应十进制9,正好是hello PHP的字符长度(数一下:h e l l o 空格 P H P,共9个字符)
  • hello PHP 是实际的响应内容
  • 0 表示分块传输结束

当你把PHP输出改成hello2 PHP时,内容长度变成了10,十六进制就是a,所以开头的标记变成了a,这完全符合分块编码的规则,不是服务器的问题,也不是你的Winsock代码的错误,只是你没有处理这种传输格式而已。


解决方法:处理分块编码的响应

你需要修改接收响应的逻辑,解析分块编码的内容,提取出真正的正文。下面是修改后的request函数中接收响应部分的代码示例:

// 接收响应(替换原来的接收逻辑)
string full_response;
char buffer[4096];
int recv_size;

// 先循环读取所有响应内容到full_response(TCP是流式传输,数据可能分多次到达)
while ((recv_size = recv(s, buffer, sizeof(buffer)-1, 0)) > 0) {
    buffer[recv_size] = '\0';
    full_response += buffer;
}

// 分离HTTP响应头和正文
size_t header_end = full_response.find("\r\n\r\n");
if (header_end == string::npos) {
    write("log.txt", "Invalid HTTP response - no header separator");
    closesocket(s);
    WSACleanup();
    return false;
}

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

// 检查是否使用分块编码
if (header.find("Transfer-Encoding: chunked") != string::npos) {
    string decoded_body;
    size_t pos = 0;

    while (pos < body.size()) {
        // 找到块大小行的结束位置
        size_t chunk_size_end = body.find("\r\n", pos);
        if (chunk_size_end == string::npos) break;

        // 提取块大小的十六进制字符串,忽略分块扩展信息(如果有)
        string chunk_size_str = body.substr(pos, chunk_size_end - pos);
        size_t semicolon_pos = chunk_size_str.find(';');
        if (semicolon_pos != string::npos) {
            chunk_size_str = chunk_size_str.substr(0, semicolon_pos);
        }

        // 将十六进制字符串转为整数
        int chunk_size = strtol(chunk_size_str.c_str(), nullptr, 16);
        if (chunk_size == 0) break; // 遇到结束块,停止解析

        // 移动到块内容起始位置,提取内容
        pos = chunk_size_end + 2;
        decoded_body += body.substr(pos, chunk_size);
        // 移动到下一个块的起始位置(跳过块内容后的换行符)
        pos += chunk_size + 2;
    }

    // 写入解析后的真实正文
    write("log.txt", decoded_body);
} else {
    // 如果不是分块编码,直接写入正文
    write("log.txt", body);
}

额外提示
  • 你的原始代码只调用了一次recv,这可能会导致接收不完整的响应(因为TCP是流式协议,数据可能分多次发送),上面的代码用循环读取解决了这个问题。
  • 实际场景中还可以优化细节,比如处理响应头的大小写(比如transfer-encoding小写形式)、十六进制大小写兼容等,但上面的代码已经能解决你当前的核心问题。

内容的提问来源于stack exchange,提问作者vlz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:23:45