サーバー側の `TIME_WAIT` は実際にどのように動作するのでしょうか?

サーバー側の `TIME_WAIT` は実際にどのように動作するのでしょうか?

これに関して SE の質問がかなりあることは知っていますが、この時点に到達する前に、必要な数の質問を読んだと思います。

「サーバー側TIME_WAIT」とは、サーバー側で close() が開始されたサーバー側ソケット ペアの状態を意味します。

矛盾しているように聞こえる次のような発言をよく目にします。

  1. サーバー側TIME_WAITは無害
  2. ネットワークアプリはクライアントがclose()を開始するように設計する必要があり、クライアントがTIME_WAIT

私がこれを矛盾だと思う理由は、TIME_WAITクライアント側で問題が発生する可能性があるからです。つまり、クライアント側で使用可能なポートが不足する可能性があるためです。したがって、本質的には、上記は、TIME_WAIT問題のないサーバー側から、問題が発生する可能性があるクライアント側に負担を移すことを推奨しています。

クライアント側はTIME_WAIT、もちろん、限られた数のユースケースでのみ問題となります。クライアント サーバー ソリューションのほとんどは、1 つのサーバーと多数のクライアントで構成されますが、クライアントは通常、問題になるほど大量の接続を処理しません。また、たとえ大量の接続を処理したとしても、あまりにも多くの接続をあまりにも早く作成しないようにすることで、SO_LINGERクライアント側の問題に「健全に」対処するための推奨事項がいくつかあります (タイムアウトを 0 にしたり、tcp_tw sysctl に干渉したりするのではなく) TIME_WAIT。ただし、これは常に実行可能であるとは限りません。たとえば、次のようなクラスのアプリケーションの場合です。

  • 監視システム
  • 負荷ジェネレータ
  • プロキシ

一方、サーバー側がTIME_WAITどのように役立つのか、私にはまったく理解できません。その理由は、古くなったフラグメントが、もはや属していないストリームにTIME_WAIT挿入されるのを防ぐためです。クライアント側では、この古くなった接続が持つ可能性のある同じペアで接続を作成できないようにするだけで実現されます(使用されたペアは によってロックアウトされます)。しかし、サーバー側では、ローカル アドレスに受け入れポートがあり、常に同じであるため、これを防ぐことはできません。また、着信ピアがソケット テーブルにすでに存在する同じアドレス ペアを作成するという理由だけで、サーバーが接続を拒否することはできません (私の知る限り、経験的な証拠しかありません)。TCPTIME_WAITip:portTIME_WAIT

私は、サーバー側の TIME-WAIT が無視されることを示すプログラムを作成しました。さらに、テストは 127.0.0.1 で行われたため、カーネルには、サーバー側かクライアント側かを伝える特別なビットが必要です (そうでなければ、タプルは同じになります)。

ソース:http://pastebin.com/5PWjkjEfFedora 22、デフォルトのネット設定でテスト済み。

$ gcc -o rtest rtest.c -lpthread
$ ./rtest 44400 s # will do server-side close
Will initiate server close
... iterates ~20 times successfully
^C
$ ss -a|grep 44400
tcp    TIME-WAIT  0      0            127.0.0.1:44400         127.0.0.1:44401   
$ ./rtest 44500 c # will do client-side close
Will initiate client close
... runs once and then
connecting...
connect: Cannot assign requested address

したがって、サーバー側ではTIME_WAIT、まったく同じポートペアの接続が即座に正常に再確立される可能性がありますが、クライアント側ではTIME-WAIT、2回目の反復でconnect()当然のことながら失敗します。

要約すると、質問は次の 2 つになります。

  • サーバー側はTIME_WAIT本当に何もせず、RFC要求されているからそのままになっているのでしょうか?
  • クライアントが close() を開始することを推奨する理由は、サーバーがTIME_WAIT役に立たないからでしょうか?

答え1

TCPここでのサーバー側という用語は、ソケットが LISTEN 状態にあるホストを意味します。

RFC1122TIME-WAIT状態のソケットがいくつかの条件で新しい接続を受け入れることを許可する

        When a connection is closed actively, it MUST linger in
        TIME-WAIT state for a time 2xMSL (Maximum Segment Lifetime).
        However, it MAY accept a new SYN from the remote TCP to
        reopen the connection directly from TIME-WAIT state, if it:

条件の詳細については、RFC1122ソケットには、一致するパッシブ OPEN (LISTEN 状態のソケット) も存在する必要があると予想されます。

アクティブOPEN(クライアント側の接続呼び出し)にはそのような例外はなく、ソケットがTIME-WAITにある場合はエラーを返す必要があります。RFC793

クライアント (TCP 用語では、アクティブな OPEN、つまり接続を実行するホスト) がクローズを開始した場合の推奨事項についての私の推測は、あなたの推測とほぼ同じです。つまり、一般的なケースでは、ソケットのリソースが豊富なホストに TIME-WAIT ソケットが分散されます。一般的なケースでは、クライアントは SYN を送信しないため、サーバー上の TIME-WAIT ソケットが再利用されます。このような推奨事項を適用するかどうかは、ユースケースによって異なることに同意します。

答え2

クライアントは、アルゴリズム (約 4 マイクロ秒ごとに 1 ずつ増加) に基づいて新しい TCP ISN (初期シーケンス番号) を使用します。ISN は基本的に、最後の同じ「ip:port ペア」TCP ソケット FIN で送信されたシーケンス番号よりも常に大きいため、「ip:port ペア」がサーバー上でまだ TIME_WAIT 状態に記録されている場合でも、サーバーは常に新しい SYN を受け入れます。

   RFC 793 [RFC0793] suggests that the choice of the ISN of a connection
   is not arbitrary, but aims to reduce the chances of a stale segment
   from being accepted by a new incarnation of a previous connection.
   RFC 793 [RFC0793] suggests the use of a global 32-bit ISN generator
   that is incremented by 1 roughly every 4 microseconds.

答え3

これこれはおそらく、TIME-WAIT が実際に何を行うか、そしてさらに重要なことに、なぜそれが重要なのかを示す最も明確な例です。また、Linux マシンで TIME-WAIT を「削減」するための「専門家」のヒントのいくつかを避けるべき理由も説明しています。

答え4

信頼性の低いプロトコルでは、ピア デバイスから最後のメッセージを受信したかどうか確信が持てないため、ピアが突然電話を切ったと想定するのは危険です。TCP プロトコルの大きな欠点は、同時に開くことができるポートが 65000 個程度しかないことです。しかし、これを克服する方法は、ポート番号をすぐに再利用するよりも、負荷に合わせて拡張できるサーバー ファームに移行することです。クライアント側が基本的なワークステーションであれば、ポートが不足する可能性はほとんどありません。

関連情報