これはTCPの実装によるものです(そしてsshはTCPを使用します)。send()は、単なるファイル記述子であるソケットに書き込むだけであり、returnは、この操作が成功したことを意味します。データが送信されたという意味ではありません。結局のところ、ファイル記述子はカーネルの状態を示す単なるポインタです。これは、セッションに失敗する前にTCP状態を少し長く保つためにカーネルに実装されています。実際、カーネルは、close()を明示的に呼び出すか、プロセスを強制終了するまで、このセッションを無期限に保持できます。したがって、データは実際には、ネットワークカードが後で配信できるようにカーネルスペースにバッファリングされます。
これがあなたがすることができる簡単な実験です:接続を確立した後もメッセージを受信し続けるサーバーを書いてください
socket();
bind();
listen();
while (1) {
accept();
recv();
}
クライアントを作成して接続を確立し、cin入力を受け取り、returnキーを押すたびにサーバーにメッセージを送信します。
socket();
connect();
while (1) {
getline();
send();
}
どちらの側でもwhileループでclose()を呼び出さないように注意してください。ここで、接続を確立した後でケーブルを抜き、メッセージを送信し、再度接続して、別のメッセージを送信すると、サーバー側に両方のメッセージが表示されます。
決して観察しないのは、最初のメッセージの前に2番目のメッセージを受信することです。あなたはそれらをすべて失うか、順番に受け取るかのどちらかです。
それでは、なぜこのように動作するのかを説明しましょう。これは、TCPセッションの状態図です。
https://dl.dropbox.com/u/17011409/TCP_State.png
close()を明示的に呼び出すまで、接続は常に確立された状態であることがはっきりとわかります。これはTCPの予想される動作です。TCP接続の確立にはコストがかかり、セッションを存続させることはパフォーマンスに役立ちます。(これは、これらのTCP DOSの動作の一部です。攻撃者は、サーバーがTCP状態情報を保持するためのリソースを使い果たすまで、接続を確立し続けます。)
この状態では、send()は実際の送信のためにカーネルに委任されます。TCPは、順序どおりの信頼性の高い配信を保証しますが、ネットワークはいつでもパケットを失う可能性があります。したがって、TCPはパケットをバッファリングする必要があり、試行を続けます。この再試行を抑制するアルゴリズムはありますが、失敗を宣言する前に非常に長い間バッファリングされます。Linuxでは、パケット損失を想定するためのデフォルトのタイムアウトは3秒です。しかし、損失の後、TCPは再試行します。その後、特定の秒後に再試行してください。ケーブルを抜いたという事実は、宛先への途中でのパケット損失とまったく同じ状況です。ケーブルを再度接続すると、再試行が成功し、TCPは残りのメッセージを順番に送信し始めます。
私はそれを完全に説明できなかったに違いないことを知っています。この動作を推論するには、TCPの詳細を知る必要があります。TCPが提供するプロパティに必要です。また、内部実装をプログラマーに公開することは受け入れられません。(ミリ秒以内に返されることもあれば、10秒後に返されることもある送信呼び出しはどうですか?コードにこのパフォーマンス爆弾が必要になることはないでしょう。TCPライブラリを持つことのポイントは、ネットワークのこの醜い性質を隠すことです。)実際、TCPが損失の多いネットワーク上で順番に信頼できる配信を実現する方法に関する複数のRFCとアルゴリズムを理解する必要さえあります。輻輳制御は、バッファがそこにある時間の関係にもなります。ウィキペディアは良い出発点ですが、それは完全な学期です」