4

しばらくしてから、特定のソケット接続がブロックされ、クライアント側の 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を更新。問題を再現するための具体的な手順は次のとおりです

  1. PC1、PC2、PC3 という 3 台のコンピューターが与えられます。3 つすべてが RouterA の背後にありますが、Server は RouterB の背後にあります。

  2. U1 と U2 という 2 人のユーザーがいるとします。U1 は PC1 からログインし、U2 は PC3 からログインします。U1 と U2 の両方が、それ自体とサーバーの間に tcp 接続を構築します。これで、U1 は tcp 接続を介してサーバーにデータを送信できるようになり、サーバーはすべてのデータを U2 に中継します。この瞬間まで、すべてが正常に機能します。

    U1 とサーバー間の TCP 接続のサーバー エンドポイントに対応するソケット番号を示します: U1-OldSocketFd

  3. 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
4

2 に答える 2

1

問題を再現するための手順と 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 は、ネットワーク トポロジを考えると、おそらく常に非接続状態を引き起こします。

于 2014-01-17T16:01:02.350 に答える
1

パケット 1 (3 と同じ) と 2 (4 と同じ) が通過した後、クライアントは 17 バイトのデータをサーバーに送信しているようです (パケット 5)。最初のパケット交換の後、パケット 5 がどれくらい遅れて到着するかはわかりません。疑似コードは、ソケットの初期化を示しているだけで、どの側がいつどのデータを送信しようとしているのかを示していないため、それを明確にしません。この場合、プロトコル交換を表すラダー ダイアグラムが役立つ場合があります。

いずれにせよ、サーバーは明らかに 17 バイトのデータを認識しないため、再度送信されます (パケット 6)。

ネットワーク、ファイアウォール、NAT ルーター、またはパケットをドロップする他の何かに問題がない限り、サーバーが TCP 交換の初期の部分を受信できるのに、明らかにパケット 5 または 6 を受信できない理由はないはずです。繰り返しますが、前のデータ交換とパケット 5 の間にかなりの時間が経過していますか (NAT ルーター、ファイアウォール、またはロード バランサーが接続を期限切れにするのに十分な時間など)?

于 2013-03-04T01:06:29.897 に答える