为什么套接字连接被阻止,TCP内核保留重传[ACK]数据包

时间:2013-03-03 23:41:50

标签: linux sockets networking tcp nat

我们面临的问题是,从一段时间后,特定套接字连接被阻止,客户端的tcp内核不断重传[ACK]数据包。

拓扑流程如下:

   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)客户

//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端口。

为什么TCP内核会继续传输[ACK]数据包 告诉客户端它收到数据包1(见数据包4,5和6),甚至 当服务器已经收到一个[ACK]数据包时(参见数据包2)? 当问题发生时,连接的任何一端都不会关闭套接字。

在数据包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, NULL);
         for(...)
         {
         if (FD_ISSET(listenfd))
            {
            ...
              }
         ...
         }

更新
UP1。以下是重现问题的具体步骤

  1. 鉴于三台计算机是PC1,PC2和PC3。        当Server落后时,这三个都落后于RouterA        RouterB的。

  2. 鉴于两个用户是U1和U2。        U1从PC1登录,U2从PC3登录。都        U1和U2将在它们之间建立一个tcp连接        和服务器。现在U1能够通过它发送数据        tcp连接到Server,然后Server中继所有数据        到U2。到目前为止,一切正常。

    表示与Server对应的套接字号        U1和Server之间TCP连接的端点:        U1-OldSocketFd

  3. 请勿注销U1,并拔下PC1的电缆。        然后U1从PC2登录,现在它建立一个新的        TCP连接到服务器。

    表示与Server对应的套接字号        U1和Server之间TCP连接的端点:        U1-NewSocketFd

    从服务器端,更新其会话时        使用U1,它会调用close(U1-OldSocketFd)

  4. 4.1。在步骤3之后大约30秒,我们发现了U1 IS              无法通过新的TCP向服务器发送任何数据              连接。

    4.2。在步骤3中,如果服务器不调用close(U1-OldSocketFd)              立即(U1之间的第二个新连接相同              而服务器已建立),而不是服务器调用              然后,close(U1-OldSocketFd)超过70-80秒              一切正常。

    UP2。路由器B在端口8013上使用端口转发 UP3。服务器运行的Linux OS的一些参数。

        net.ipv4.tcp_tw_reuse = 1
        net.ipv4.tcp_tw_recycle = 1
    

2 个答案:

答案 0 :(得分:1)

在数据包1(与3相同)和2(与4相同)过后,您的客户端似乎正在向服务器传输17个字节的数据(数据包5)。我不知道在第一次交换数据包之后数据包5有多少,所以我不知道这发生了多少时间。您的伪代码不会澄清它,因为它只显示套接字初始化,它没有显示哪一方尝试在什么时间传输什么数据。在此实例中,ladder diagram可能有助于表示您的协议交换。

在任何情况下,服务器显然都不会确认17字节的数据,因此会再次传输(数据包6)。

除非您在网络或防火墙或NAT路由器或其他丢弃数据包时出现问题,否则服务器无法接收TCP交换机的早期部分,但显然无法接收数据包再次,在先前的数据交换和数据包5之间是否经过了大量时间(例如,NAT路由器,防火墙或负载均衡器有足够的时间使连接到期)?

答案 1 :(得分:1)

根据您重现问题和UPD3的步骤,可能是由于

  

net.ipv4.tcp_tw_recycle = 1

原因是内核正在尝试在适当的时间之前回收TIME_WAIT连接(感谢tw_recycle)。

This answer解释了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可能始终会触发无连接状态。