我们面临一个问题,一段时间以后,特定的套接字连接被阻塞,客户端的tcp内核持续重传[ACK]报文。
拓扑stream程如下:
Client A ←→ Switch A ← Router A:NAT ← .. Internet .. → Router B:NAT → Switch B ←→ Server B
这里是WireShark捕获的数据包:
A)服务器
1. 8013 > 6757 [PSH,ACK] Seq=56 Ack=132 Win=5840 Len=55 2. 6757 > 8013 [ACK] Seq=132 Ack=111 Win=65425 Len=0
B)客户
SleepEx是否保证在超时之前调用所有挂起的完成callback?
有人可以给我一个例子,说明如何select()提醒fd变成“ready”
为什么Windows Java不能绑定到已绑定的端口?
11001返回getaddrinfo()的所有调用
uwsgi协议比http协议快吗?
//lines 3 and 4 are exactly the same as line 1 and 2 3. 8013 > 13000 [PSH,ACK] Seq=56 Ack=132 Win=5840 Len=55 4. 13000 > 8013 [ACK] Seq=132 Ack=111 Win=65425 Len=0 5. 13000 > 8013 [PSH,ACK] Seq=132 Ack=111 Win=65425 Len=17 [TCP Retransmission] 6. 13000 > 8013 [PSH,ACK] Seq=132 Ack=111 Win=65425 Len=17
8013是服务器端口,6757是客户端NAT端口。
为什么即使服务器已经收到一个[ACK]包(见包2),TCP内核为了告诉客户端它接收到包1(见包4,5和6),继续发送[ACK]包? 发生问题时,连接的任何一侧都不会closuressockets。
数据包6后,连接丢失,我们不能通过该套接字发送任何东西到服务器了。
psuedocode: //client serverAddr.port =htons(8013) ; serverAddr.ip = inet_addr(publicIPB); connect(fdA,serverAddr,...); //server listenfd = socket(,SO_STREAM,); localAddr.port = htons(8013); localAddr.ip = inet_addr(INADDR_ANY); bind(localAddr...) listen(listenfd,100); ... //using select model select(fdSet,NULL,NULL); for(...) { if (FD_ISSET(listenfd)) { ... } ... }
UPDATE
UP1。 以下是重现问题的具体步骤
给定三台电脑是PC1,PC2和PC3。 所有三个都在Routera后面,而服务器在RouterB后面。
给定两个用户是U1和U2。 U1从PC1login,U2从PC3login。 U1和U2都会在自己和服务器之间build立一个tcp连接。 现在U1能够通过tcp连接发送数据到服务器,然后服务器将所有数据中继到U2。 一切正常,直到这一刻。
表示与U1和服务器之间的TCP连接的服务器端点对应的套接字号:U1-OldSocketFd
不要注销U1,拔下PC1的电缆。 然后U1从PC2login,现在它build立一个新的TCP连接到服务器。
表示与U1和服务器之间的TCP连接的服务器端点对应的套接字号:U1-NewSocketFd
从服务器端,当它更新与U1会话时,它调用close(U1-OldSocketFd) 。
4.1。 在步骤3之后约30秒,我们发现U1不能通过其新的TCP连接向服务器发送任何数据。
4.2。 在步骤3中,如果服务器没有立即调用close(U1-OldSocketFd) (U1和Server之间build立了同样的第二个新的连接),服务器调用close(U1-OldSocketFd)超过70-80秒,一切正常。
UP2。 路由器B使用端口8013上的端口转发。
UP3。 服务器上运行的Linux操作系统的一些参数。
net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1
套接字对,Perl,KEEPALIVE和轮询
Python套接字只接受本地连接
I / O的最新Windows线程池API使用情况
Linux RAW套接字错误 – 发送失败后如何获得正确的MTU?
在数据包1(与3相同)和2(与4相同)之后,客户端似乎正在向服务器发送17个字节的数据(数据包5)。 我不知道在第一次交换数据包后有多少包5,所以我不知道发生了多少时间。 你的伪代码没有说明它,因为它只是显示了套接字的初始化,它并没有显示哪一方尝试在什么时候传输什么数据。 在这种情况下, 梯形图可能有助于表示您的协议交换。
在任何情况下,服务器显然不承认17字节的数据,所以他们再次传输(数据包6)。
除非网络有问题,或者有防火墙或NAT路由器或其他丢弃数据包的问题,否则应该没有任何理由让服务器能够接收TCP交换的早期部分,但显然不能接收数据包5或6再次说明,在事先交换数据和数据包5(例如NAT路由器,防火墙或负载均衡器有足够的时间使连接到期)之间是否存在大量的时间?
根据你的步骤重现问题和UPD3,这可能是由于
net.ipv4.tcp_tw_recycle = 1
原因是内核试图在到期之前回收一个TIME_WAIT连接(感谢tw_recycle)。
这个答案解释了tw_reuse和tw_recycle的行为(NAT部分在这里很有用)。
根据复制和观察4-1和4-2的步骤,当你立即调用fclose()时,连接进入TIME_WAIT状态,从tw_recycle可以接受的地方开始,并且假定由于这边已经关闭连接,套接字可以回收。 由于从服务器的角度来看,连接来自同一个主机,所以tw_recycle开始了。
当你在调用fclose()之前等待,因为没有从服务器的POV触发断开连接,它将假定连接仍然活着,这防止了tw_recycle踢入,可能/可能迫使创建一个全新的连接。
根据1 ,从协议POV安全,你有2个案例:
禁用tw_reuse和tw_recycle
启用tw_reuse,启用TCP时间戳,禁用tw_recycle
考虑到您的网络拓扑结构,tw_recycle可能会始终触发无连接条件。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。