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

多线程TCP大文件传输进度显示异常问题求助

多线程TCP文件传输大文件异常排查

客户端发送代码

// Opened a file using fopen function.
int SendPercent = 0; // Value that shows the current progress
while (1)
{
    len = fread(buf, 1, BUFSIZE, fp);
    if (len)
    {
        retval = send(sock, buf, len, 0);
        if (retval == SOCKET_ERROR) {
            err_display("send()");
            break;
        }

        SendPercent += len;
        printf("percent : %2.2f%%\r", (float)SendPercent / (float)AllLen * 100.0f);
    }
    else if (SendPercent == AllLen)
    {
        printf("\nFinished.\n");
        break;
    }
    else {
        printf("\nIt did not end successfully.\n");
        break;
    }
}
fclose(fp);
closesocket(sock);
WSACleanup();
return 0;

服务器接收代码

FILE* fp = fopen(filename, "wb");

while (1)
{
    retval = recv(client_sock, buf, sizeof(buf), MSG_WAITALL);
    if (retval == SOCKET_ERROR) {
        err_display("recv()");
        break;
    }
    else if (retval == 0) {
        printf("\ncurLen : %f, allLen : %f\n", (float)curLen, (float)allLen);
        break;
    }
    curLen += fwrite(buf, 1, retval, fp);
    memset(&buf, 0, sizeof(buf));
    printf("percent : %2.2f%%\r", (float)curLen / (float)allLen * 100.0f);
    if (curLen == allLen)
    {
        printf("\nSuccessfully Received.\n");
        break;
    }
}
fclose(fp);

异常现象

  • 4KB小文件传输正常;
  • 77000KB以上大文件传输时:客户端显示进度100%并完成断开,服务器进度停在99.92%,curLen未触发等于allLen的判断就因连接断开退出循环,但实际接收的文件完整;
  • 单线程模式运行正常,多线程(通过CreateThread实现)下出现该问题。

问题根源分析

  1. 浮点数精度丢失:
    curLen和AllLen为32位整数时,大文件大小(如77000KB=78848000字节)超过float类型的有效精度(约6-7位),转换为float计算百分比时会丢失精度,导致显示的进度未达100%,但实际curLen已经等于allLen。

  2. 服务器接收逻辑顺序问题:
    客户端发送完所有数据后会关闭socket,服务器后续的recv调用会返回0(连接关闭)。此时代码直接触发break退出循环,未先检查curLen是否等于allLen,因此不会打印成功提示,造成“未完成”的假象。

  3. 多线程下的时序差异:
    单线程模式下,最后一次recv可能刚好接收完最后一段数据,触发curLen == allLen的判断并正常退出;多线程模式下,recv可能在连接关闭后才返回0,跳过了成功判断的逻辑。

修复方案

  1. 使用高精度数值类型:
    将curLen、SendPercent、AllLen改为long long类型,用double计算百分比,避免精度丢失:

    printf("percent : %2.2f%%\r", (double)curLen / (double)allLen * 100.0);
    
  2. 调整服务器接收逻辑:
    在recv返回0时,先检查curLen是否等于allLen,再决定是否提示成功:

    else if (retval == 0) {
        if (curLen == allLen) {
            printf("\nSuccessfully Received.\n");
        } else {
            printf("\ncurLen : %lld, allLen : %lld\n", curLen, allLen);
        }
        break;
    }
    
  3. 谨慎使用MSG_WAITALL:
    该标志会让recv等待直到接收满指定字节,但若连接提前关闭,可能导致最后一段数据的接收逻辑异常。可考虑去掉该标志,改为循环接收直到获取足够数据,更适配TCP流式传输的特性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 11:55:18