[英]Why Socket Connection Blocked and TCP Kernel Keeps Retransmitting [ACK] packets
我們面臨的一個問題是,從一段時間以后,特定的套接字連接將被阻止,客戶端的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端口。
為什么即使服務器已經收到一個[ACK]數據包(請參見數據包2),TCP內核為什么仍繼續發送[ACK]數據包以告知客戶端它已接收到數據包1(請參見數據包4、5和6)? 發生問題時,連接雙方均不會關閉套接字。
數據包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。 這是重現該問題的具體步驟
給定三台計算機,分別為PC1,PC2和PC3。 這三個都在RouterA后面,而服務器在RouterB后面。
給定兩個用戶U1和U2。 U1從PC1登錄,U2從PC3登錄。 U1和U2都將在自身和服務器之間建立tcp連接。 現在,U1能夠通過其tcp連接將數據發送到Server,然后Server將所有數據中繼到U2。 到目前為止,一切正常。
表示與U1和服務器之間的TCP連接的服務器端點相對應的套接字號:U1-OldSocketFd
不要注銷U1,然后拔下PC1的電纜。 然后,U1從PC2登錄,現在它與服務器建立了新的TCP連接。
表示與U1和服務器之間的TCP連接的服務器端點相對應的套接字號:U1-NewSocketFd
從服務器端,當它使用U1更新其Session時,它將調用close(U1-OldSocketFd)
。
4.1。 在第3步之后大約30秒,我們發現U1無法通過其新的TCP連接將任何數據發送到服務器。
4.2。 在第3步中,如果服務器沒有立即調用close(U1-OldSocketFd)
(在U1和Server之間建立了相同的第二個新連接),而是服務器在70-80秒內調用了close(U1-OldSocketFd)
,則一切正常。
UP2。 路由器B在端口8013上使用端口轉發。
UP3。 運行服務器的Linux操作系統的一些參數。
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
數據包1(與3相同)和數據包2(與4相同)通過后,您的客戶端似乎正在向服務器(數據包5)傳輸17個字節的數據。 我不知道第一次交換數據包之后會有多少個數據包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_recycle可能總是會觸發無連接性條件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.