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

Winsock传输二进制文件时接收端缺失前4字节问题排查

Winsock二进制文件接收缺失前4字节问题根因与修复

核心根因

丢失的4字节恰好是客户端发送的网络字节序文件长度字段,问题完全出在服务端接收逻辑的冗余读操作上:

  • 客户端发送流的顺序固定为:先发送4字节ULONG类型的文件总长度(转网络字节序),再按顺序发送完整文件内容。
  • 服务端进入ReceiveDoWorkThenReturnDataToClient函数后,首先执行了一次多余的recv(sd, acReadBuffer, 1024, 0)调用,调试看到首次返回值为4,就是这一步把客户端发的前4字节(文件长度值)读到了acReadBuffer缓冲区中,这部分数据既没有被解析为文件长度,也没有写入目标文件,直接被丢弃。
  • 后续服务端调用readBytes(sd, &FileSize, sizeof(FileSize))尝试读取4字节长度时,TCP流最开头的4字节已经被前面的冗余recv取走,此时读到的是文件内容的前4字节,服务端把这4个字节错误解析为文件长度,再读取后续流内容写入文件,最终保存的文件就刚好少了开头4个字节,文件总大小却和源文件一致——因为错误解析出的长度值刚好和原文件长度偏差4字节,读完剩余流的总写入长度刚好等于原文件大小。

附带的逻辑缺陷

除了核心的冗余recv问题,服务端代码还有两个会导致异常的逻辑错误:

  • 文件打开/删除逻辑放错位置:内层循环每次迭代都会执行DeleteFile删旧文件、重新_wfopen_s开新文件,只要单次recv返回的数据长度大于1,就会反复清空已写入内容,最终只能保留最后一次写入的块。
  • 回包逻辑存在内存越界:send(sd, "1" + nSentBytes, (INT)strlen("1") - nSentBytes, 0)做了错误的指针运算,当nSentBytes大于0时,字符串指针会向后偏移越界,发送的内容是非法内存数据。

修复方案

  1. 删除ReceiveDoWorkThenReturnDataToClient中最外层多余的acReadBuffer预读recv逻辑,连接建立后直接读取4字节长度字段,再按长度读取文件内容即可。
  2. 将删除旧文件、打开目标文件的逻辑移到接收流程最外层,整个文件接收过程只打开一次文件句柄,全部内容写入完成后再关闭句柄。
  3. 修正回包逻辑,文件接收完成后直接给客户端发送固定1字节的确认符即可,不需要循环拼接发送。

修正后的接收函数参考实现:

BOOL ReceiveDoWorkThenReturnDataToClient(CONST SOCKET sd)
{    
    WCHAR sTempFileOut[MAX_PATH] = L"E:\\temp\\newjpg.jpg";
    // 接收前先删旧文件、打开新文件,仅执行一次
    DeleteFile(sTempFileOut);
    FILE* fp = NULL;
    errno_t err = _wfopen_s(&fp, sTempFileOut, L"wb");
    if (fp == NULL)
        return FALSE;

    // 读取4字节文件长度字段
    ULONG FileSize;
    int iResult = readBytes(sd, &FileSize, sizeof(FileSize));
    if (iResult <= 0)
    {
        fclose(fp);
        return FALSE;
    }
    FileSize = ntohl(FileSize);

    // 按长度循环读取文件内容写入磁盘
    CHAR mfcc[1024];
    while (FileSize > 0)
    {
        int Received = readBytes(sd, mfcc, min(sizeof(mfcc), FileSize));
        if (Received <= 0)
        {
            fclose(fp);
            return FALSE;
        }
        if (fwrite(mfcc, 1, Received, fp) != Received)
        {
            fclose(fp);
            return FALSE;
        }
        FileSize -= Received;
    }

    fflush(fp);
    fclose(fp);

    // 向客户端回传1字节确认,修复原指针越界问题
    const char ack = '1';
    send(sd, &ack, 1, 0);

    return TRUE;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:06:30