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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:35:43