TCPオプションSO_LINGER(0)はいつ必要ですか?


95

オプションの正式な意味は理解できたと思います。現在処理しているいくつかのレガシーコードでは、オプションが使用されます。顧客は、RSTについて、その側からの接続に近い側からのFINへの応答として不満を持っています。

いつ使うべきかわからないので、安全に外せるかわかりません。

オプションが必要になる時期の例を教えていただけますか?


1
削除してください。量産コードでは使用しないでください。これが使用されたのを目にしたのは、無効なベンチマークの結果でした。
ローン侯爵2014

回答:


82

SO_LINGERタイムアウトをゼロに設定する一般的な理由は、そのTIME_WAIT状態にある多数の接続を回避し、サーバーで使用可能なすべてのリソースを占有することです。

TCP接続が完全に閉じられると、クローズを開始した側(「アクティブクローズ」)は、接続TIME_WAITが数分間留まることになります。したがって、サーバーが接続をクローズするプロトコルであり、有効期間が短い接続が非常に多数含まれている場合、この問題の影響を受ける可能性があります。

ただし、これは良い考えではありません- TIME_WAIT理由のために存在します(古い接続からの浮遊パケットが新しい接続に干渉しないようにするため)。可能であれば、クライアントが接続を閉じる開始プロトコルにプロトコルを再設計することをお勧めします。


3
全くもって同じ意見です。多くの(X秒ごとに数千の短時間の接続)を開始する監視アプリケーションを見てきましたが、それはより大きなスケール(1000接続以上)の問題がありました。理由はわかりませんが、アプリケーションは応答しませんでした。誰かがSO_LINGER = true、TIME_WAIT = 0でOSリソースをすばやく解放することを提案しました。短い調査の後、このソリューションを試したところ、非常に良い結果が得られました。TIME_WAITは、このアプリの問題ではなくなりました。
bartosz.r 2012年

24
同意しません。TCPの上にあるアプリケーションレベルのプロトコルは、クライアントが常に接続を閉じるように設計する必要があります。そのようにTIME_WAITして、クライアントは害を及ぼすことなく座ります。「UNIXネットワークプログラミング」の第3版(Stevens et al)の203ページにあるとおり、「TIME_WAIT状態はあなたの友人であり、私たちを助けるために存在します。この状態を回避しようとするのではなく、理解する必要があります(セクション2.7)。 」
mgd 2012年

8
クライアントが30秒ごとに4000接続を開きたい場合(この監視アプリケーションはクライアントです!接続を開始するためです)?はい、アプリケーションを更新し、インフラストラクチャにローカルエージェントを追加し、モデルをプッシュに変更できます。しかし、すでにそのようなアプリケーションがあり、それが成長した場合、残りを調整することでそれを機能させることができます。1つのパラメーターを変更すると、新しいアーキテクチャーを実装するための予算を投じることなく、突然アプリケーションが機能します。
bartosz.r 2012年

3
@ bartosz.r:タイムアウト0でSO_LINGERを使用するのは、最後の手段であるべきだとだけ言っています。この場合も、「UNIXネットワークプログラミング」の第3版(Stevensら)の203ページでは、データ破損のリスクがあるとも述べています。RFC 1337を読んで、TIME_WAITが友だちである理由を確認してください。
mgd 2012年

7
@cafいいえ、HTTP 1.1などのすべての強力なTCP APIに見られるように、古典的なソリューションは接続プールです。
ローン侯爵2014

188

私の提案として、最後のセクション「タイムアウト0でSO_LINGERを使用する場合」をお読みください。

その前に、以下について少し講義します。

  • 通常のTCP終了
  • TIME_WAIT
  • FINACKおよびRST

通常のTCP終了

通常のTCP終了シーケンスは次のようになります(簡略化)。

AとBの2つのピアがあります。

  1. A呼び出し close()
    • A FINがBに送信する
    • AがFIN_WAIT_1状態になる
  2. Bが受け取る FIN
    • B ACKがAに送信
    • BはCLOSE_WAIT状態になります
  3. Aが受け取る ACK
    • AがFIN_WAIT_2状態になる
  4. B呼び出し close()
    • B FINがAに送信
    • BはLAST_ACK状態になります
  5. Aが受け取る FIN
    • A ACKがBに送信する
    • AがTIME_WAIT状態になる
  6. Bが受け取る ACK
    • BはCLOSED状態になります–つまり、ソケットテーブルから削除されます

TIME_WAIT

したがって、終了を開始する(つまり、close()最初にコールする)ピアは最終的にそのTIME_WAIT状態になります。

TIME_WAIT州が友だちである理由を理解するには、Stevensらによる「UNIXネットワークプログラミング」第3版(43ページ)のセクション2.7をお読みください。

しかし、それは多くのソケットで問題になる可能性があります TIME_WAIT最終的には新しい接続が受け入れられなくなる可能性があるため、サーバー上の状態ます。

この問題を回避するために、を呼び出す前に、SO_LINGERソケットオプションをタイムアウト0に設定することをお勧めしますclose()。ただし、これはTCP接続がエラーで終了する原因となるため、不適切なソリューションです。

代わりに、接続の終了が常にクライアント側から開始されるようにアプリケーションプロトコルを設計します。クライアントが残りのすべてのデータを読み取ったことを常に認識している場合は、終了シーケンスを開始できます。例として、ブラウザはContent-LengthHTTPヘッダーからすべてのデータを読み取り、クローズを開始できることを認識しています。(私は、HTTP 1.1では、可​​能な再利用のためにしばらく開いたままにし、その後閉じることを知っています。)

サーバーが接続を閉じる必要がある場合は、サーバーがクライアントに呼び出しを要求するようにアプリケーションプロトコルを設計します。 close()

タイムアウト0でSO_LINGERを使用する場合

この場合も、「UNIXネットワークプログラミング」の第3版のページ202-203によると、SO_LINGER呼び出しの前にタイムアウト0を設定close()すると、通常の終了シーケンスが開始されません

代わりに、このオプションを設定して呼び出しをclose()行うピアRSTは、エラー状態を示す(接続リセット)を送信します。これにより、相手側でこの状態が認識されます。通常、「ピアによる接続のリセット」などのエラーが表示されます。

そのため、通常の状況では、それはセットには本当に悪い考えですSO_LINGER呼び出す前にタイムアウト0とclose()今から呼び出さに- 失敗に終わっ近いです -サーバーアプリケーションで。

ただし、特定の状況では、とにかくそうすることが保証されます。

  • サーバーアプリケーションのクライアントが誤動作(タイムアウト、無効なデータを返すなど)する場合、途中で閉じたりCLOSE_WAITTIME_WAIT状態に陥ったりするのを避けるために、中止を行うことは理にかなっています。
  • 現在数千のクライアント接続があるサーバーアプリケーションを再起動する必要がある場合、サーバーが新しいクライアント接続に使用可能なポートを取得できなくなる可能性があるため、(サーバー側からTIME_WAIT呼び出す場合)数千のサーバーソケットを回避するためにこのソケットオプションを設定することを検討してください。close()再起動後。
  • 前述の本の202ページでは、「この機能を使用して強制終了を送信する必要がある特定の状況があります。1つの例は、RS-232ターミナルサーバーです。RS-232ターミナルサーバーはCLOSE_WAIT、スタックしたターミナルにデータを配信しようとすると永久にハングする場合があります。ポートですがRST、保留中のデータを破棄することができた場合、スタックポートを適切にリセットします。」

私はあなたの質問に非常に良い答えを与えると私が信じているこの長い記事をお勧めします。


6
TIME_WAIT:それが原因の問題に起動しない唯一の友人であるstackoverflow.com/questions/1803566/...
Pacerier

2
では、Webサーバーを作成している場合はどうでしょうか。どのように「クライアントにクローズを開始するように伝える」のですか?
Shaun Neal 2016年

2
@ShaunNeal明らかにしません。しかし、適切に記述されたクライアント/ブラウザがクローズを開始します。クライアントが正常に動作していない場合は、幸運なことにTIME_WAITで暗殺して、ソケット記述子や一時的なポートが不足しないようにします。
mgd 2016年

16

lingerがオンでタイムアウトがゼロの場合、TCPスタックは接続を閉じる前に保留中のデータが送信されるのを待ちません。これが原因でデータが失われる可能性がありますが、この方法で長引くように設定すると、これを受け入れ、接続を正常に閉じるのではなく、すぐにリセットするように要求します。これにより、通常のFINではなくRSTが送信されます。

EJPのコメントに感謝します。詳細については、こちらをご覧ください。


1
これは分かります。私が求めているのは、ハードリセットを使用したい場合の「現実的な」例です。
dimba

5
接続を中止したいときはいつでも; ので、あなたのプロトコルが検証に失敗し、あなたは突然、あなたがなどRST、との接続を中止したいのあなたのクライアント話してゴミを持っている場合
レンホルゲート

5
リンガーがオフの状態で、リンガータイムアウトがゼロになると混乱します。Lingerオフは、close()がブロックしないことを意味します。正のタイムアウトで長引くと、タイムアウトまでclose()がブロックすることを意味します。タイムアウトがゼロの状態が続くと、RSTが発生します。これが問題です。
ローン侯爵2014

2
はい、あなたは正しいです。用語を修正するために答えを調整します。
Len Holgate、2014

6

コード内の残留を安全に削除できるかどうかは、アプリケーションのタイプによって異なります。それは「クライアント」(TCP接続を開いてアクティブに最初に閉じる)か、「サーバー」(TCPのオープンと反対側がクローズを開始した後にそれを閉じる)?

アプリケーションに「クライアント」(最初に閉じる)のフレーバーがあり、さまざまなサーバーへの膨大な数の接続を開始およびクローズする場合(たとえば、アプリが膨大な数のさまざまなサーバーの到達可能性を監視する監視アプリである場合)すべてのクライアント接続がTIME_WAIT状態でスタックするという問題があります。次に、タイムアウトをデフォルトよりも小さい値に短くして、正常にシャットダウンし、クライアント接続リソースをより早く解放することをお勧めします。0はFINでは正常にシャットダウンせず、RSTでは異常終了するため、タイムアウトを0に設定しません。

アプリケーションに「クライアント」のフレーバーがあり、同じサーバーから大量の小さなファイルをフェッチする必要がある場合は、ファイルごとに新しいTCP接続を開始して、TIME_WAITで大量のクライアント接続になることはありませんが、接続を開いたままにし、同じ接続を介してすべてのデータをフェッチします。Lingerオプションは削除でき、削除する必要があります。

アプリケーションが「サーバー」(ピアのクローズへの反応として2番目に閉じる)の場合、close()で接続が正常にシャットダウンされ、TIME_WAIT状態に入らないためリソースが解放されます。リンガーは使用しないでください。しかし、サーバーアプリに監視プロセスがあり、アクティブでない開いている接続が長時間アイドル状態であることが検出されている場合(「長い」と定義されます)、この非アクティブな接続を側からシャットダウンできます。これは、残存タイムアウトを0に設定することによって行われます。close()は、RSTをクライアントに送信し、怒っていることを伝えます:-)


1

サーバーでは、誤動作しているクライアントを切断するときではRSTなく、送信したい場合がありFINます。FIN-WAITこれによりTIME-WAIT、サーバーのソケット状態がスキップされます。これにより、サーバーリソースが使い果たされないようになり、このようなサービス拒否攻撃から保護されます。


0

DOS攻撃がサーバーリソースを使い果たす可能性があるというマキシムの観察が好きです。それはまた、実際に悪意のある敵なしで起こります。

一部のサーバーは、クライアントアプリに接続リークのバグがある場合に発生する「意図しないDOS攻撃」に対処する必要があり、サーバーに送信する新しいコマンドごとに新しい接続を作成し続けます。そして、GCのプレッシャーに達した場合、最終的には接続を閉じるか、接続がタイムアウトする可能性があります。

別のシナリオは、「すべてのクライアントが同じTCPアドレスを持っている」シナリオです。その場合、クライアント接続は、ポート番号によってのみ区別できます(単一のサーバーに接続する場合)。そして、クライアントが何らかの理由で接続の開閉を急速に循環し始めた場合、(クライアントアドレス+ポート、サーバーIP +ポート)タプルスペースを使い果たす可能性があります。

したがって、サーバーは、TIME_WAIT状態のソケットが多数ある場合、Linger-Zero戦略に切り替えることをお勧めします。クライアントの動作は修正されませんが、影響が軽減される可能性があります。


0

サーバー上の待機ソケットは、時間0のlingerを使用して、ソケットへのバインディングに即座にアクセスし、接続がまだ完了していないクライアントをリセットできます。TIME_WAITは、マルチパスネットワークがあり、パケットの順序が正しくない場合や、奇数のネットワークパケットの順序/到着タイミングを処理している場合にのみ興味深いものです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.