web-dev-qa-db-ja.com

SO_KEEPALIVEオプションを正しく使用して、反対側のクライアントがダウンしていることを検出する方法は?

linux環境でのC言語でのソケットプログラミングにおけるオプションSO_KEEPALIVEの使用法を学ぼうとしていました。

サーバーソケットを作成し、ブラウザを使用して接続しました。それは成功し、GETリクエストを読み取ることができましたが、SO_KEEPALIVEの使用法に行き詰まりました。

私はこのリンク [email protected] を確認しましたが、使用方法を示す例は見つかりませんでした。

accept()関数でクライアントのリクエストを検出したらすぐに、SO_KEEPALIVEオプション値1クライアントソケット。ここで、クライアントがダウンしているかどうかを確認する方法はわかりませんか?、送信されるプローブ間の時間間隔を変更する方法など。

クライアントがダウンしているというシグナルをどのように取得するのか(クライアントで読み取りまたは書き込みをせずに...クライアントからプローブが返信されないときにいくつかのシグナルを受け取ると思っていました)、オプションを設定した後にそれをプログラムするにはどうすればよいですかSO_KEEPALIVEがオン。

また、プローブが3秒ごとに送信され、クライアントがその間にダウンした場合、クライアントがダウンしていることを知ることができず、SIGPIPEが発生する可能性があります。

とにかく重要なことに、コードでSO_KEEPALIVEを使用する方法を知りたいです。

事前にトンに感謝します!!!

18
Durin

プローブ数またはプローブ間隔を変更するには、次のように/ procファイルシステムに値を書き込みます

 echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time
 echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl
 echo 20 > /proc/sys/net/ipv4/tcp_keepalive_probes

これらの値は、システム上のすべてのキープアライブ対応ソケットに対してグローバルであることに注意してください。setsockoptを設定するときに、ソケットごとにこれらの設定をオーバーライドすることもできます。リンクしたドキュメントのセクション4.2を参照してください。

キープアライブを使用して、ユーザー空間からソケットのステータスを「チェック」することはできません。代わりに、カーネルはリモートエンドにパケットの確認応答を強制し、ソケットが不良かどうかを判断することについて、より積極的です。ソケットに書き込もうとすると、リモートエンドがダウンしているとキープアライブが判断した場合、SIGPIPEを取得します。

20
bdk

SO_KEEPALIVEを有効にしない場合と同じように、SO_KEEPALIVEを有効にしない場合と同じ結果が得られます。通常、ソケットの準備ができており、そこから読み取るとエラーが発生します。

Linuxでは、ソケットごとにキープアライブタイムアウトを設定できます(これはLinux固有の機能である場合があります)。システム全体の設定を変更するのではなく、これをお勧めします。詳細については、tcpのmanページを参照してください。

最後に、クライアントがWebブラウザの場合、とにかくかなり早くソケットを閉じる可能性があります。それらのほとんどは、キープアライブ(HTTP 1.1)接続を比較的短時間(30秒、1分など)開いたままにするだけです。もちろん、クライアントマシンが表示されなくなったり、ネットワークがダウンしたりした場合(SO_KEEPALIVEは検出に非常に役立ちます)、アクティブにソケットを閉じることができません。

10
MarkR

すでに説明したように、SO_KEEPALIVEは、何もしていなくても継続的に接続を確認することについてカーネルをより積極的にしますが、notは情報の配信方法を変更または強化します。実際に何か(たとえば、「書き込み」)を実行しようとすると、すぐにわかります。カーネルは、数秒待つ必要がなく、以前に設定されたフラグのステータスを報告するだけなので、すぐにわかります。ネットワークアクティビティが失敗するまでの秒数(場合によってはそれ以上)。 「反対側が予期せず消えた」状態を処理するために使用したものとまったく同じコードロジックが引き続き使用されます。変更されるのはタイミングです(メソッドではありません)。

事実上すべての「実用的な」ソケットプログラムは、データフェーズ中にソケットへのアクセスをnon-blockingアクセスを提供します(select()/ poll()または多分fcntl()/ O_NONBLOCK/EINPROGRESS&EWOULDBLOCK)。 、またはカーネルがMSG_DONTWAITでサポートしている場合)。これが他の理由で既に行われていると仮定すると、接続の切断についてすぐに確認することは簡単です(コードをまったく必要としないこともあります)。ただし、データフェーズでnotがすでに何らかの方法でソケットへの非ブロックアクセスを提供している場合、次に何かを実行するまで接続の切断についてはわかりません。

(A TCPデータフェーズ中になんらかの非ブロッキング動作を行わないソケット接続は、悪意のあるパケットがネットワークの問題に遭遇した場合、プログラムが「ハング」するのが非常に簡単であるため、悪名高いほど脆弱です無期限に、そしてあなたがそれについてできることはたくさんありません。)

3
Chuck Kollars

短い答え、追加

_int flags =1;
if (setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags))) { perror("ERROR: setsocketopt(), SO_KEEPALIVE"); exit(0); };
_

サーバー側では、クライアントがダウンしているときにread()のブロックが解除されます。

完全な説明は here にあります。

3
Alt Eisen