UNIXネットワークプログラミング-socketのkeep-alive

4363 ワード

このブログを読むときは、後のブログ<>と<>を参照してください.
第1部
【需要】サーバ処理に影響を及ぼさない前提で、クライアントプログラムが強制終了されたかどうかを検出する.
【現状】サーバ側とクライアント側のSocketにはkeepalive属性が設定されている.サーバ側はプローブ回数などのパラメータを設定し,クライアント,サーバはkeepalive機能を開いただけでサーバ側は監視スレッドを起こし,selectを利用してsocketが閉じられたか否かを検出する.
以下は私の少しの浅い理解です.
1.keep aliveについて
Windowsでもlinuxでもkeepaliveには3つのパラメータがあります.
sk->keepalive_probes: 
sk->keepalive_time    
sk->keepalive_intvl  

確立されたtcp接続の場合.keepalive_でtime時間内に双方にパケット転送がない場合、keepalive機能をオンにした一端はkeepaliveパケットを送信し、応答が受信されなければkeepalive_intvl時間再送信パケット、keepalive_送信probes次.応答が受信されないままrstパケットを送信して接続を閉じます.応答を受信すると、タイマーをクリアします.例★:
sk->keepalive_probes = 3;
sk->keepalive_time   = 30;
sk->keepalive_intvl = 1;

tcp接続については、socket上でデータがやり取りされていればkeepaliveはトリガーされませんが、30秒もデータがやり取りされていない場合はkeep aliveが動作し始めます.プローブパケットを送信し、応答を受けるとネットワークが良いと判断し、プローブを終了します.対応がなければ1秒おきにプローブパケットを3回送信し、3回後も対応していない場合は、RSTパケットを送信して接続をオフにします.つまり、ネットワークからsocketがネットワークの異常を意識することができ、最大33秒かかります.しかし、keep aliveが設定されていない場合は、recvがsocketが切断されていることを知らないため、反対側がアクティブに接続を閉じない限り、recvは戻ってこない可能性があります.送信:データ量の大きさによって、下位プロトコルスタックのbufferが送信データを置くことができる限り、アプリケーションレベルのsendは正常に返されます.bufferが満タンになるまで、bufferが満タンになるまで、bufferの空きを待つ時間をブロックします.だからsendの戻り値の検査に失敗は検出されません.keep alive機能がオンになると、受信した関数の戻り値を直接送信することで、ネットワークが異常であるかどうかを知ることができます.設定方法(適用レベル):
int keepalive = 1; //  keepalive 
int keepidle = 60; //  60 , 
int keepinterval = 5; //  5  
int keepcount = 3; //  。 1 , 2 。
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive ));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle ));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval ));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));

2.selectとkeep aliveの関係
selectは、単一プロセスで複数のsocketを使用するために設計されており、接続の検出に関係なく、1つのsocketのみを検出する場合はselectを使用する必要はありません.keepalive機能をオンにすると、recvまたはsendを呼び出すたびに戻り値をチェックし、エラーまたは0であるかどうかを判断します.エラーが発生した場合は、errnoをチェックして資料を調べ、リンクが切れたか存在しないかを示すエラー番号をいくつか見てください.
また、接続状況を定期的にチェックしたい人はkeep aliveを有効にします.もう一方の端は起きなくてもよいが,keep aliveとは無関係にtcpプロトコルの基本的な要求であるプローブパケットに受動的に応答するだけである.クライアント側とサーバ側がkeep aliveをオンにする必要はありません.
3.テスト結果
例★の値に従って、一端のsocketでkeep aliveをオンにし、recvまたはsendにブロックします.このとき、ネットワークケーブルを抜いて、ネットワークケーブルを抜いてrecv/sendに戻って失敗した時間をテストします.
linux kernelのテストでは、ブロック型のsocketに対して、recvの場合、keep aliveが設定されていなければ、ネットワークケーブルが抜かれたりifdownになったりしても、recvは長い間戻ってこないことがわかりました.最大17分ですが、この時間はlinuxのデフォルトタイムアウト時間()よりずっと短いです.ただしkeep aliveが設定されている場合は、基本的にkeepalive_time +keepalive_probes*keepalive_intvl=33秒以内にエラーが返されます.
しかし、sendをループするsocketについては、ネットワーク線を抜くとsendが成功(0~10秒程度、送信データの量に依存)に戻るまでしばらく続き、sendがブロックされます.プロトコル層のbufferがいっぱいになっているため、bufferが空いているのを待つと、90秒程度でエラーが返されます.このことからsendの場合keep aliveが機能していないようで,その原因は今でも不明である.後でsendの前にtimerを設定することで解決します.
 

第2部


TCP接続がオフになった場合、接続する両端のどちらか一方がオフ動作を開始する必要があることを知っていますが、一方が突然オフになった場合、もう一方の端は知ることができません.tcpのkeep_aliveは異常を検出するためのメカニズムである.
3つのパラメータがあります.
  • 心拍メッセージ送信間隔
  • 返信がない場合、再試行の間隔
  • 再試行回数
  • Linuxオペレーティングシステムの場合、この3つの値はそれぞれ
    huangcheng@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_keepalive_time
    7200
    huangcheng@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_keepalive_intvl
    75
    huangcheng@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_keepalive_probes
    9

    すなわち7200 s(2時間)おきにkeepaliveのメッセージが開始され、応答がなければ75秒後に再試行され、最大9回再試行されると接続が閉じたとみなされることを意味する.
    この3つのオプションは、それぞれTCP_に対応しています.KEEPIDLE、TCP_KEEPINTLとTCP_KEEPCNTのオプション値は、setsockoptで設定します.
    しかし、tcp自身のkeepaliveにはこのようなバグがあります.
    通常、接続の反対側はcolseをアクティブに呼び出して接続を閉じ、tcpは接続が閉じたことを通知します.しかし、tcp接続の反対側が突然オフラインになったり、電源が切れたりすると、ネットワークが閉鎖されたことを知りません.このとき,送信データが失敗すると,tcpは自動的に再送する.再転送パケットの優先度はkeepaliveよりも高く、それは私たちのkeepaliveがいつも送信できないことを意味します.このとき、接続がエラーで中断されたことも知られていません.長い間再パスに失敗した後、私たちは知っています.
    このような状況を避けるために、私たちはtcpの上層部で、自分で制御しなければなりません.このメッセージに対して、送信時間と応答を受信した時間を記録します.長い間応答がなければ、ネットワークが中断する可能性があります.長い間送信されていない場合、すなわち、長い間通信されていない場合、keepaliveのためにパケットを自分で送信して、接続の存在を維持することができる.