しばらくしてから、特定のソケット接続がブロックされ、クライアント側の 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 ポートです。
サーバーがすでに 1 つの [ACK] パケット (パケット 2 を参照) を受信しているにもかかわらず、TCP カーネルがクライアントにパケット 1 (パケット 4、5、および 6 を参照) を受信したことを伝えるために [ACK] パケットを送信し続けるのはなぜですか? 問題が発生した場合、接続のどちらの側もソケットを閉じません。
パケット 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 という 3 台のコンピューターが与えられます。3 つすべてが RouterA の背後にありますが、Server は RouterB の背後にあります。
U1 と U2 という 2 人のユーザーがいるとします。U1 は PC1 からログインし、U2 は PC3 からログインします。U1 と U2 の両方が、それ自体とサーバーの間に tcp 接続を構築します。これで、U1 は tcp 接続を介してサーバーにデータを送信できるようになり、サーバーはすべてのデータを U2 に中継します。この瞬間まで、すべてが正常に機能します。
U1 とサーバー間の TCP 接続のサーバー エンドポイントに対応するソケット番号を示します: U1-OldSocketFd
U1 をログアウトせず、PC1 のケーブルを抜きます。次に、U1 は PC2 からログインし、サーバーへの新しい TCP 接続を確立します。
U1 とサーバー間の TCP 接続のサーバー エンドポイントに対応するソケット番号を示します: U1-NewSocketFd
サーバー側から、U1 とのセッションを更新するときに、 を呼び出します
close(U1-OldSocketFd)
。
4.1. ステップ 3 の約 30 秒後、U1 が新しい TCP 接続を介してサーバーにデータを送信できないことがわかりました。
4.2. ステップ 3 で、サーバーがすぐに呼び出さない場合close(U1-OldSocketFd)
(U1 とサーバーの間で同じ 2 番目の新しい接続が確立された場合)、代わりに、サーバーが
close(U1-OldSocketFd)
70 ~ 80 秒以上かけて呼び出した場合、すべて正常に動作します。
UP2. ルーター B は、ポート 8013 でポート フォワーディングを使用します
。UP3。サーバーが実行される Linux OS のいくつかのパラメーター。
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1