TCP 3 回目のハンドシェイク データ転送プロセス図

TCP 3 回目のハンドシェイク データ転送プロセス図

RFC793 ドキュメントの SYN フラグを持つプロセス パケットはデータを伝送できません。つまり、3 ウェイ ハンドシェイクの最初の 2 つはデータを伝送できません (論理的に言えば、接続はまだ確立されていないため、データを伝送するのは少し無理が​​あるようです)。重要な点は、3 回目のハンドシェイクでデータを伝送できるかどうかです。

まず結論を述べます。TCP プロトコルの 3 ウェイ ハンドシェイク プロセスにおける接続を確立するための 3 番目のハンドシェイクにより、データの伝送が可能になります。

上記の TCP 状態変化図の接続確立部分を参照して、RFC793 ドキュメントの内容を見てみましょう。 RFC793 ドキュメントには、次の内容が記載されています (重要でない部分は省略)。

重要な点は、「送信のためにキューに入れられたデータまたは制御が含まれる場合があります」という文です。これは、3 番目のハンドシェイクの ACK パケットがデータを伝送できることを標準が示していることを意味します。

まず、3 回目のハンドシェイクのパケットは、接続イニシエーター (以下、クライアント) からポート リスナー (以下、サーバー) に送信されるので、接続が SYN-RECV (図では SYN_RECEIVED) 状態のときにパケットを受信した後、カーネル プロトコル スタックの処理プロセスを見つけるだけで済みます。少し調べたところ、net\ipv4 ディレクトリの tcp_input.c ファイルの tcp_rcv_state_process 関数がこの処理を処理することがわかりました。図に示すように:

この関数は実際には TCP ステート マシンであり、TCP 接続がさまざまな状態にあるときに受信したデータ パケットの処理を処理するために使用されます。ここには並列の switch 文がいくつかあります。関数が非常に長いため、階層関係を誤読しやすくなります。次の図は、考慮する必要のないコードを簡略化した後の SYN-RECV 状態の処理を示しています。

2 つの switch ステートメントが並列であることに注意することが重要です。したがって、TCP_SYN_RECV 状態が合法かつ標準化された双方向ハンドシェイク パケットを受信すると、ソケット状態は直ちに TCP_ESTABLISHED 状態に設定されます。実行が次の TCP_ESTABLISHED 状態ケースに達すると、そこに含まれるデータ (存在する場合) の処理が続行されます。

上記は、クライアントから送信された 3 番目のハンドシェイクの ACK にデータが含まれている場合、サーバーはそれを正常に処理できることを示しています。では、クライアント側はどうでしょうか? SYN-SEND 状態のときにクライアントが 3 番目の ACK パケットを送信する方法を見てみましょう。図に示すように:

tcp_rcv_synsent_state_process 関数の実装は比較的長いので、最終的な重要なポイントは次のとおりです。

一目見て分かりますよね?条件が満たされない場合は、別の ACK パケットで直接応答します。条件が満たされた場合は、inet_csk_reset_xmit_timer 関数を使用して、短時間待機するタイマーを設定します。この期間中にデータがある場合は、データとともに ACK が送信されますが、ACK で返信するデータはありません。

これまでの疑問は解決されました。

条件 1: sk->sk_write_pending != 0

この値はデフォルトで 0 になりますが、0 以外になる原因は何でしょうか?答えは、プロトコル スタックのデータ送信機能が ESTABLISHED ではないソケット ステータスに遭遇すると、この変数に対して ++ 操作を実行し、しばらく待ってからデータの送信を試行するということです。写真をご覧ください:

net/core/stream.c の sk_stream_wait_connect 関数は次の処理を実行します。

sk->sk_write_pending が増加し、ソケット接続が ESTABLISHED 状態に達した後にデータが送信されます。それは明らかに説明しています。

Linux ソケットのデフォルトの動作モードはブロッキングです。つまり、クライアントの接続呼び出しはデフォルトでブロックされ、3 ウェイ ハンドシェイク プロセスが完了するかエラーが発生するまで戻りません。次に、ブロッキング ソケットで完全に実装され、デフォルトのソケット パラメータを変更しないコマンド ライン アプレットである nc は、データを送信する前に connect が成功または失敗を返すのを待ちます。これが、3 番目のハンドシェイクのパケットでデータをキャプチャできない理由です。

次に、非ブロッキング ソケットを設定し、接続後すぐにデータを送信します。接続プロセスがすぐに成功しない場合は、データを含む 3 番目のハンドシェイク パケットが表示される可能性があります。ただし、オープンソース ネットワーク ライブラリが非ブロッキング ソケットであっても、ソケットの書き込み可能なイベントを監視し、接続が成功したことを確認した後にのみデータを書き込みます。このわずかなパフォーマンス向上を節約することは、安全で信頼性の高いコードを持つことほど価値がありません。

条件2: icsk->icsk_accept_queue.rskq_defer_accept != 0

この状態は非常に奇妙です。defer_accept は、受け入れを延期するために使用されるソケット オプションです。実際には、最初のデータが受信された後にのみ接続が作成されます。 tcp_defer_accept オプションは通常サーバー側で使用され、ソケットの SYN キューと ACCEPT キューに影響します。デフォルトで設定されていない場合は、3 ウェイ ハンドシェイクが完了した後、ソケットは受け入れキューに入り、アプリケーション層が関連する接続を感知して ACCEPT します。 tcp_defer_accept が設定されている場合、3 ウェイ ハンドシェイクが完了し、ソケットは ACCEPT キューに入らず、データが実際に送信されて ACCEPT キューに入れられるまで、SYN キューに直接留まります (長さの制限があり、長さが制限を超えるとカーネルは新しい接続を拒否します)。このパラメータを設定するサーバーは、受け入れ後すぐに読み取ることができるため、データが存在する必要があり、システムコールも節約されます。

SYN キューは、SYN_RECV 状態のソケットを格納します。長さは、net.ipv4.tcp_max_syn_backlog パラメータによって制御されます。受け入れキューは、listen が呼び出されたときに backlog パラメータによって設定されます。カーネルのハード制限は net.core.somaxconn によって制限されます。つまり、実際の値は min(backlog,somaxconn) によって決定されます。

興味深いのは、クライアントが最初にポートと IP にバインドし、setsockopt(TCP_DEFER_ACCEPT) を設定してからサーバーに接続すると、rskq_defer_accept=1 が表示されることです。この時点で、カーネルは ACK パケットに応答する前にデータを待機するためのタイマーを設定します。個人的にはこれをやったことはありません。パフォーマンスを向上させるために空の ACK パケットの送信を減らすだけでしょうか?クラスメイトで知っている人がいたら教えてください。よろしくお願いします。

条件3: icsk->icsk_ack.pingpong != 0

pingpong 属性は、実際には現在のリンクがインタラクティブ データ ストリームであるかどうかを示すソケット オプションです。値が 1 の場合、インタラクティブ データ ストリームであり、遅延確認メカニズムが使用されることを示します。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • tcpdf プラグインを thinkPHP フレームワークに統合する例
  • PHP で実装された TCP サーバーとクライアント関数の例
  • PHP ソケット通信 (tcp/udp) の例の分析
  • PHP で TCP ポート検出を実装する方法
  • PHP で TCPDF を使用して PDF ドキュメントを生成する例
  • TCPの3ウェイハンドシェイクと4ウェイウェーブの詳細な紹介
  • PHP での Swoole のホット アップデート実装コードの例
  • PHP swooleのプロセスモジュールはサブプロセス操作の例を作成して使用します
  • PHP SwooleとTCP 3ウェイハンドシェイクの詳細な説明

<<:  Vue3 (V) HTTPライブラリaxiosの統合の詳細

>>:  HTML テーブル マークアップ チュートリアル (28): セルの境界線の色属性 BORDERCOLOR

推薦する

JavaScript の基礎: スコープ

目次範囲グローバルスコープ関数のスコープもし、スイッチ、のために、その間ブロックスコープスコープチェ...

JavaScript クロージャの説明

目次1. クロージャとは何ですか? 2. クロージャの役割2.1) メモリ2.2) プライベート変数...

CSS3で実装された6つの境界遷移効果

6つの効果実装コードhtml <h1>CSS 境界遷移</h1> <セ...

Linux でショートカットアイコンを設定する方法

序文Linux でショートカットを作成すると、アプリケーションをより速く開くことができます。ここで、...

js 加算、減算、乗算、除算の正確な計算方法のサンプルコード

序文コンピュータの数値は浮動小数点であるため、計算プロセス中に取得されるデータは通常正確ではなく、そ...

Docker デプロイメント RabbitMQ コンテナ実装プロセス分析

1. イメージをプルするまず、次のコマンドを実行して、イメージをローカル コンピューターにダウンロー...

12個のJavascriptテーブルコントロール(DataGrid)が整理されています

DataGrid コントロールの DataSource プロパティがデザイン時に設定されている場合、...

MySQLデータベースはsysbenchに基づくOLTPベンチマークテストを実装します

Sysbench は、MySQL データベース ストレージ エンジン InnoDB のディスク I/...

macOS SierraにApache2.4+PHP7.0+MySQL5.7.16をインストールする

Mac システムには PHP と Apache が付属していますが、必要なバージョンではない場合があ...

Linux statコマンドの使用

1. コマンドの紹介stat コマンドは、ファイルまたはファイル システムに関する詳細情報を表示する...

Linux の圧縮および解凍コマンドの紹介

目次一般的な圧縮形式: gz .bz2 .xz .zip一般的に使用されるアーカイブは圧縮を必要とす...

XHTML 入門チュートリアル: XHTML とは何ですか?

HTMLとは何ですか?簡単に言えば、HTML は Web ページを作成するために使用されます。とて...

MySQLの3つの用途と違いは同等ではない

MySQLでは判定記号がよく使われますが、等しくない記号はもっと一般的に使われます。次の3つの等しく...

Linux カーネル デバイス ドライバーのメモリ管理に関する注意事項

/************************ * Linux メモリ管理 *********...