write() 関数を呼び出しただけでは、イーサネット ケーブルの抜き差しを検出できません。これは、tcp スタックによる tcp 再送信が無意識のうちに行われているためです。ここに解決策があります。
キープアライブ オプションをアプリケーション ソケットに既に設定していても、アプリケーションがソケットに書き込みを続ける場合、ソケットのデッド接続状態を時間内に検出することはできません。これは、カーネル tcp スタックによる tcp 再送信が原因です。tcp_retries1 と tcp_retries2 は、tcp 再送信タイムアウトを設定するためのカーネル パラメータです。RTT メカニズムによって計算されるため、再送信タイムアウトの正確な時間を予測することは困難です。この計算は rfc793 で確認できます。(3.7. データ通信)
https://www.rfc-editor.org/rfc/rfc793.txt
各プラットフォームには、tcp 再送信用のカーネル構成があります。
Linux : tcp_retries1, tcp_retries2 : (exist in /proc/sys/net/ipv4)
http://linux.die.net/man/7/tcp
HPUX : tcp_ip_notify_interval, tcp_ip_abort_interval
http://www.hpuxtips.es/?q=node/53
AIX : rto_low, rto_high, rto_length, rto_limit
http://www-903.ibm.com/kr/event/download/200804_324_swma/socket.pdf
切断された接続を早期に検出したい場合は、tcp_retries2 (デフォルトは 15) に低い値を設定する必要がありますが、既に述べたように正確な時間ではありません。さらに、現在、これらの値を単一のソケットに対してのみ設定することはできません。これらはグローバル カーネル パラメータです。単一のソケットに tcp 再送信ソケット オプションを適用する試みがいくつかありましたが ( http://patchwork.ozlabs.org/patch/55236/ )、カーネルのメインラインには適用されなかったと思います。これらのオプション定義がシステム ヘッダー ファイルに見つかりません。
参考までに、以下のように「netstat --timers」を使用してキープアライブ ソケット オプションを監視できます。
https://stackoverflow.com/questions/34914278
netstat -c --timer | grep "192.0.0.1:43245 192.0.68.1:49742"
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (1.92/0/0)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (0.71/0/0)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (9.46/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (8.30/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (7.14/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (5.98/0/1)
tcp 0 0 192.0.0.1:43245 192.0.68.1:49742 ESTABLISHED keepalive (4.82/0/1)
また、キープアライブ タイムアウトが発生した場合、使用するプラットフォームによってさまざまなリターン イベントに遭遇する可能性があるため、リターン イベントだけで接続不能状態を判断してはなりません。たとえば、キープアライブ タイムアウトが発生した場合、HP は POLLERR イベントを返し、AIX は POLLIN イベントだけを返します。その時点で、recv() 呼び出しで ETIMEDOUT エラーが発生します。
最近のカーネル バージョン (2.6.37 以降) では、TCP_USER_TIMEOUT オプションを使用するとうまく機能します。このオプションは、シングル ソケットに使用できます。
最後に、MSG_PEEK フラグを指定して read 関数を使用すると、ソケットが正常であることを確認できます。(MSG_PEEK は、データがカーネル スタック バッファーに到着したかどうかを確認するだけで、データをユーザー バッファーにコピーすることはありません。) したがって、このフラグを使用して、ソケットが問題なく動作していることを確認できます。副作用はありません。