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
相关产品推荐
相关产品推荐

