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
相关产品推荐
相关产品推荐

