TIME_WAITでソケットを強制的に閉じる方法は?


113

Linuxで特定のプログラムを実行すると、時々クラッシュします。その後すぐに開くと、最初に行ったように49200ではなくソケット49201でリッスンします。netstatは、49200がTIME_WAIT状態にあることを示しています。

ソケットをTIME_WAIT状態からすぐに強制的に移行するために実行できるプログラムはありますか?


1
あなたが原因にここにいる場合は、「あまりにも多くのTIME_WAITサーバー上の」だけをスキップ代わりにそれに答えるの問題を避けるため最初の3つの答え。
Pacerier

回答:


148
/etc/init.d/networking restart

詳しく説明させてください。Transmission Control Protocol(TCP)は、2つのエンドポイント(プログラム)間の双方向の、順序付けられた、信頼性の高いデータ伝送プロトコルとして設計されています。このコンテキストでは、信頼できるという用語は、途中でパケットが失われた場合にパケットを再送信することを意味します。TCPは、ピアから受信した単一または一定範囲のパケットに対して確認応答(ACK)パケットを送り返すことにより、信頼性を保証します。

これは、終了要求/応答などの制御信号でも同じです。RFC 793では、TIME-WAIT状態が次のように定義されています。

TIME-WAIT-リモートTCPが接続終了要求の確認応答を受信したことを確認するのに十分な時間待機することを表します。

次のTCP状態図を参照してください。 代替テキスト

TCPは双方向通信プロトコルであるため、接続が確立されると、クライアントとサーバーの間に違いはありません。また、どちらかが終了を呼び出すことができ、確立されたTCP接続を完全に閉じるには、両方のピアが閉じることに同意する必要があります。

終了をアクティブなクローザーと呼び、他のピアをパッシブなクローザーと呼ぶ最初のものを呼び出しましょう。アクティブクローザーがFINを送信すると、状態はFIN-WAIT-1になります。次に、送信されたFINのACKを受信し、状態はFIN-WAIT-2になります。パッシブクローザーからもFINを受信すると、アクティブクローザーはACKをFINに送信し、状態はTIME-WAITになります。パッシブクローザーが2番目のFINへのACKを受信しなかった場合、FINパケットを再送信します。

RFC 793は、タイムアウトを最大セグメント寿命の2倍、つまり2MSLに設定します。パケットがインターネットをさまようことができる最大時間であるMSLは2分に設定されているため、2MSLは4分です。ACKに対するACKがないため、アクティブなクローザーは、TCP / IPプロトコルに正しく準拠している場合、パッシブ送信者がそのFINへのACKを受信して​​いない場合(理論的に)、4分間待つ以外に何もできません。

実際には、パケットの欠落はおそらくまれであり、すべてがLAN内または単一のマシン内で発生している場合は非常にまれです。

「TIME_WAITでソケットを強制的に閉じる方法」という逐語的な質問に答えるために、元の答えに固執します。

/etc/init.d/networking restart

実際には、WMRが述べたようにSO_REUSEADDRオプションを使用してTIME-WAIT状態を無視するようにプログラムします。 SO_REUSEADDRは正確に何をしますか?

このソケットオプションは、このポートがビジー(
TIME_WAIT状態)であっても、先に進み、とにかく再利用するようカーネルに指示します。それがビジーであるが、別の状態では、まだ使用中のエラーのアドレスが表示されます。サーバーがシャットダウンされ、ポートでソケットがまだアクティブな間にすぐに再起動された場合に便利です。予期しないデータが入った場合、サーバーが混乱する可能性があることに注意する必要がありますが、これは可能ですが、可能性は低いです。


8
素晴らしい答えですが、彼の質問に対する正しい答えではありません。ネットワークの再起動は機能しますが、その後は再起動するため、これは正しくありません。
クリス黄リーバー

3
@Chris Huang-Leaverの質問は、「ソケットをTIME_WAIT状態からすぐに強制的に移行するために実行できるプログラムはありますか?」です。再起動がプログラムの実行と見なされる場合、それも正解です。なぜこれが正しくないと思いますか?
ユージン横田

8
WMRには、最も役立つ答えがあります(この種の問題に遭遇したとき、私はそれを行います)。ネットワークを再起動するのはあまりにも劇的であり、単にタイムアウトを待つよりも時間がかかる可能性があります。彼の質問に対する正しい答えは「いいえ」ですが、SOでは2文字の答えを入力できません:-)
Chris Huang-リバー

6
わかりました。次回SIGTERMでプロセスがハングしたとき、コンピューターを修正するのではなく、破壊するだけです。
ロングポーク

これの一般化は「ネットワークサービスの再起動」です。特定の場所/etc/init.d/networkingはプラットフォーム固有(Debian?)であるため、他のシステムでは正確なコマンドラインが(場合によってはかなり根本的に)異なります。私は他のコメント者に同意しますが、これは深刻な行き過ぎであり、関連のないネットワークサービスにとって明らかに破壊的であるように見えます。

51

あなたが実行している特定のプログラムのソースコードを持っているかどうかはわかりませんが、そうであれば、SO_REUSEADDRを設定するだけでsetsockopt(2)、ソケットがTIME_WAIT状態であっても同じローカルアドレスにバインドできますソケットはアクティブにリッスンしていますsocket(7)

TIME_WAIT状態の詳細については、UnixソケットFAQを参照してください


しかし、すでにバインドされたエラーは発生しませんでした。プログラムを再度実行すると、post(123456)でリッスンします。また、システムがそのポートのTIME_WAITを表示していることを確認できますが、それでも接続できます。どうして?
ジャヤパルチャンドラン

2
SO_REUSEADDRを使用しても、「アドレスは既に使用中です」というエラーが発生する可能性があります。詳細については、hea-www.harvard.edu /〜fine / Tech / addrinuse.htmlを参照してください
静国八尾

@WMR SO_REUSEADDRはソケットを「閉じません」。すでに開いているものを再利用できるようにするだけです。したがって、質問は「ソケットを強制的に閉じる方法TIME_WAIT」です。
Pacerier

これは正しい答えですが、質問は完全に正しいものではありませんでした。少なくとも私の問題はうまく解決しました(ネットワーク全体を再起動して他のすべての接続も切断するのとは異なります)。
Vマーク

SO_REUSEADDRできるようになるbind()進みます。ただし、そのソケットをリッスンする場合は、すべて同じものlisten()を返しEADDRINUSEます。言い換えれば、この回答は、一時ポートを使用するクライアントソフトウェアに役立つ可能性がありますが、サーバーソフトウェアの問題を解決するものではありません。
ウィル

33

私の知る限り、プログラムに適切なシグナルハンドラを記述する以外に、ソケットを強制的に閉じる方法はありませんが、タイムアウトにかかる時間を制御する/ procファイルがあります。ファイルは

/proc/sys/net/ipv4/tcp_tw_recycle

これを行うことにより、タイムアウトを1秒に設定できます。

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

ただし、このページには、この変数を設定する際の信頼性の問題に関する警告が含まれています。

関連ファイルもあります

/proc/sys/net/ipv4/tcp_tw_reuse

TIME_WAITソケットを(おそらくタイムアウトなしで)再利用できるかどうかを制御します。

ちなみに、カーネルのドキュメントでは、「技術専門家のアドバイス/リクエスト」なしにこれらの値を変更しないように警告しています。私はそうではありません。

プログラムは、ポート49200へのバインドを試行し、ポートがすでに使用されている場合は1ずつ増分するように作成されている必要があります。したがって、ソースコードを制御できる場合は、この動作を変更して、数秒待ってから、増分する代わりに同じポートで再試行することができます。


2番目の2つの例はs / rw / tw /であるべきだと思うが、十分な担当者がいない。

1
カーネルのドキュメントから:注意。tcp_tw_recycleとtcp_tw_reuseの両方が問題を引き起こす可能性があります。パラメータが有効になっているノードを使用または使用しているノード間のネットワークトポロジを理解せずに有効にしないでください。ファイアウォール、NAT、ロードバランサーなど、TCP接続状態を認識しているノードを経由する接続は、設定が原因でフレームのドロップを開始する場合があります。この問題は、十分な数の接続がある場合に明らかになります。

1将来の接続で機能するように設定しますが、すでに開かれている現在の接続はどうですか?
Pacerier

18

実際には、接続を強制終了する方法があります-killcx。彼らは、接続のどの状態でも動作すると主張しています(私は確認していません)。ただし、通信が発生するインターフェイスを知る必要があります。デフォルトではeth0を想定しているようです。

更新:別の解決策は、一部のLinuxディストリビューションのリポジトリにあるカッターです。


3

別のオプションは、タイムアウトを0にしてSO_LINGERオプションを使用することです。この方法では、ソケットを閉じると強制的に閉じられ、FIN / ACKの閉じ動作に入る代わりにRSTが送信されます。これにより、TIME_WAIT状態が回避され、一部の用途により適している場合があります。


2
また、まだ送信中の送信データも失われ、反対側でエラーが発生する可能性があります。推奨されません。
user207421

@EJP早期に失敗することは、ほとんど常に正しい呼び出しです。ネットワーキングは信頼できません、そしてそれは物事を遅くします。クラッシュしたアプリは、データが安全に作成されたと想定することはできません。
東武14

1
実際、他のエンドポイントがTCPを介した独自のアプリケーション層の信頼できるトランスポートを実装するバグのある組み込み産業バスゲートウェイである場合、これをお勧めします。そのゲートウェイの接続制限。そこ。悲しいことに、このようなハッキングに頼る必要がある非常に具体的で非常に現実的な例を挙げました。
-andyn

@Tobu Networkingは信頼できませんが、TCPはそうしようとします。それを悪化させることは何も改善することにはなりません。
user207421

2

別の解決策は、ポート49200でリッスンする信頼性の高いプロキシまたはポート転送ソフトウェアを用意し、異なるポートを使用して信頼性の低いプログラムのいくつかのインスタンスの1つに接続を転送することです。

ちなみに、接続先のポートは非​​常に高いです。0〜1024の範囲を超える未使用のものを使用してみてください。システムは、より短いポート番号を一時ポートとして使用する可能性が低くなります。


0

TIME_WAITは、ソケットプログラミングクライアントサーバーアーキテクチャで最も一般的な問題です。定期的に試行する数秒間待つのが最善の解決策です。サーバーを必要とするリアルタイムアプリケーションの場合、すぐに起動する必要がありますSO_REUSEADDRオプションがあります。

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