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

TCP内核大缓冲引发应用因FIN包处理失败的技术问题求助

TCP半关闭连接超时导致客户端内核缓冲数据丢失问题

我之前的议题被误归类为网络工程问题,经更多测试后确认这是程序员面临的真实场景问题。我的应用从不可修改的服务器流式读取MP3文件,客户端以160kbits/s的速率按需读取数据并喂给DAC,具体问题如下:

以3.5MB的MP3文件为例:

  • 服务器发送完最后一字节后会关闭连接并发送FIN包,属于常规操作
  • Windows内核的TCP缓冲会存储1-3MB数据(推测TCP窗口已完全打开),服务器几秒内即可发送完所有3.5MB数据,此时约3MB数据留在客户端内核缓冲中,且服务器已发送FIN包并收到ACK响应
  • 客户端以20kB为块持续读取数据,需要约150秒才能读到EOF;但Windows服务器的FIN_WAIT_2状态计时器仅120秒,超时后服务器会判定未收到客户端FIN包,发送RST包,导致客户端内核缓冲中的剩余数据被丢弃,应用直接失败

测试代码

int sock = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in addr;

addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_family = AF_INET;
addr.sin_port = htons(80);

int res = connect(sock, (const struct sockaddr*) &addr, sizeof(addr));

char* get = "GET /data-3 HTTP/1.0\r\n"
        "User-Agent: mine\r\n"
        "Host: localhost\r\n"
        "Connection: close\r\n"
        "\r\n";

bytes = send(sock, get, strlen(get), 0);
printf("send %d\n", bytes);

char *buf = malloc(20000);

while (1) {
    int n = recv(sock, buf, 20000, 0);
    if (n == 0) {
        printf("normal eof at %d", bytes);
        close(sock);
        break;
    }
    if (n < 0) {
        printf("error at %d", bytes);
        exit(1);
    }
    bytes += n;
    Sleep(n*1000/(160000/8));
}
free(buf);
closesocket(sock);

该代码可在任意HTTP服务器上复现问题。

抓包数据对比

成功场景

3684    381.383533  192.168.6.15    192.168.6.194   TCP 54  [TCP Retransmission] 9000 → 52422 [FIN, ACK] Seq=9305427 Ack=54 Win=262656 Len=0
3685    381.387417  192.168.6.194   192.168.6.15    TCP 60  52422 → 9000 [ACK] Seq=54 Ack=9305428 Win=131328 Len=0
3686    381.387417  192.168.6.194   192.168.6.15    TCP 60  52422 → 9000 [FIN, ACK] Seq=54 Ack=9305428 Win=131328 Len=0
3687    381.387526  192.168.6.15    192.168.6.194   TCP 54  9000 → 52422 [ACK] Seq=9305428 Ack=55 Win=262656 Len=0

失败场景

5375    508.721495  192.168.6.15    192.168.6.194   TCP 54  [TCP Retransmission] 9000 → 52436 [FIN, ACK] Seq=5584802 Ack=54 Win=262656 Len=0
5376    508.724054  192.168.6.194   192.168.6.15    TCP 60  52436 → 9000 [ACK] Seq=54 Ack=5584803 Win=961024 Len=0
6039    628.728483  192.168.6.15    192.168.6.194   TCP 54  9000 → 52436 [RST, ACK] Seq=5584803 Ack=54 Win=0 Len=0

当前困境与需求

  • 已知可以通过与服务器握手规避,但内核级缓冲的存在导致只要缓冲数据消耗时间超过服务器FIN_WAIT_2超时,就必然失败
  • 客户端能实时处理数据,调整客户端应用层缓冲大小无效,尝试修改SO_RCVBUF也未生效
  • 寻求应用层控制内核缓冲的方法,或在recv()返回EOF前检测到FIN包的方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:25:19