基于Winsock的HTTP服务:TTF等二进制资源需手动停止加载才显示
问题根源与解决方案
核心问题分析
文本流处理二进制数据导致截断
std::ostringstream是面向文本的输出流,当执行oss << http_response.content.data()时,它会把二进制数据当作C风格字符串处理——遇到第一个\0(空字符)就停止写入。而TTF、图片等二进制文件几乎必然包含\0,这直接导致实际发送的二进制内容不完整。浏览器收到的字节数远小于Content-Length声明的大小,因此会一直等待剩余数据,直到用户手动停止加载。Content-Length计算错误
文件实际大小为flen(803384字节),但你执行了content.resize(flen + 1),导致http_response.content.size()被错误计算为803385,Content-Length字段也随之报错。即使二进制内容被截断,浏览器仍会等待到声明的字节数才会结束加载。发送逻辑的额外错误
你用output.size() + 1作为发送长度,同时发送output.c_str()——c_str()会自动在字符串末尾添加\0,但output本身已经因二进制截断不完整,这个操作不仅没有解决核心问题,还额外增加了不必要的字节。
修复步骤
1. 修正文件读取逻辑
去掉多余的+1操作,确保读取的二进制数据与文件实际大小完全一致:
// Read file as binary FILE* f; unsigned long flen; f = fopen(c_internal_path, "rb"); if (!f) { fprintf(stderr, "Unable to open file %s", c_internal_path); http_code = 500; content = Util::stringToUCharVector(HTTP_ERROR_500); } else { fseek(f, 0, SEEK_END); flen = ftell(f); fseek(f, 0, SEEK_SET); content.resize(flen); // 直接使用文件实际长度,无需额外+1 std::fread(content.data(), 1, flen, f); // 按字节读取,保证数据完整 fclose(f); }
2. 拆分HTTP头部与二进制内容的构造
避免用文本流拼接二进制数据,单独构造HTTP头部,再与二进制内容分开处理:
// 构造HTTP头部字符串 std::ostringstream header_oss; header_oss << "HTTP/1.1 " << http_response.http_code << " OK\r\n"; header_oss << "Cache-Control: no-cache, private\r\n"; header_oss << "Content-Type: " << FHTTPMIME::MimeToString(mime_type) << "\r\n"; header_oss << "Content-Length: " << flen << "\r\n"; // 使用实际文件长度flen header_oss << "\r\n"; // HTTP头部结束的空行(必须用\r\n) std::string header = header_oss.str();
3. 正确发送完整数据
Winsock的send函数可能无法一次发送全部数据,需要循环发送直到所有字节都完成传输,同时分开发送头部和二进制内容:
// 发送HTTP头部 int total_sent = 0; int header_len = header.size(); while (total_sent < header_len) { int sent = send(clientSocket, header.c_str() + total_sent, header_len - total_sent, 0); if (sent == SOCKET_ERROR) { // 按需处理发送错误 break; } total_sent += sent; } // 发送二进制内容 total_sent = 0; int content_len = content.size(); while (total_sent < content_len) { int sent = send(clientSocket, reinterpret_cast<const char*>(content.data()) + total_sent, content_len - total_sent, 0); if (sent == SOCKET_ERROR) { // 按需处理发送错误 break; } total_sent += sent; }
额外注意事项
- HTTP头部的换行必须使用
\r\n,单个\n不符合HTTP规范,可能导致部分浏览器解析异常。 - 不要给二进制数据添加
\0终止符,二进制文件不需要该标记,只会导致Content-Length不匹配。
内容的提问来源于stack exchange,提问作者Dmytro
相关产品推荐
相关产品推荐

