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

推薦する

Dockerバッチコンテナオーケストレーションの実装

導入Dockerfile ビルドの実行は、単一のコンテナの手動操作です。マイクロサービス アーキテク...

Ubuntu 20.04 ダブルピンイン入力方式のインストール手順

1. 中国語入力方法を設定する 2. ダブルスペルモードを設定する 3. 注意事項20.04 で S...

nginx を使用して 1 つのドメイン名で複数の Laravel プロジェクトを構成する方法の例

背景会社のサブプロジェクトが増えるにつれて、さまざまなサイズのプロジェクトが10個以上になります(バ...

シンプルなナビゲーションバー機能を実現するHTML+CSS

さっそく、コードを見てみましょう(初心者:特に言うことはありません) <!DOCTYPE ht...

星のきらめき効果を実現するネイティブ js

この記事の例では、星のきらめき効果を実現するためのjsの具体的なコードを共有しています。具体的な内容...

JavaScript 配列と非配列オブジェクトのディープ クローンとシャロー クローンの原則の詳細な説明

目次シャロークローニングとディープクローニングとは何ですか? 1. アレイのクローンを作成する1.1...

順序再構築に関する簡単な説明: MySQL シャーディング

目次1. 目的2. 環境整備1. 基本情報2. データベース環境の準備3. データベースを構築し、サ...

MySQL はカスタムシーケンスを使用して row_number 関数を実装します (詳細な手順)

いくつかの記事を読んだ後、ようやく MySQL で row_number() ソートを実装する方法が...

Windows に WSL をインストールして構成する方法

WSLとはBaidu 百科事典からの一節を引用します。 Windows Subsystem for ...

JS Canvas インターフェースとアニメーション効果

目次概要Canvas API: グラフィックスの描画パス線種矩形アーク文章グラデーションと画像の塗り...

td セルを結合した場合の td 幅の問題

以下の例では、名前が入っている td の幅が 60px のとき、2 行目の文字数が少ない場合は正常に...

クラウドネイティブテクノロジーKubernetesスケジューリングユニットポッドの使用の詳細な説明

k8s の最小のスケジューリング単位 --- pod前回の記事では、k8s が解決できる問題を簡単に...

JavaScriptカルーセルの実装について

今日もとても実践的な事例です。名前を聞くだけで高度で難しそうですよね?今日はカルーセル画像の真髄を簡...

CentOS7 で yum を使用して PostgreSQL と PostGIS をインストールする方法

1. yumソースを更新するCentOS7 のデフォルトの yum リポジトリの PostgreSQ...

v-html レンダリング コンポーネントの問題

以前 HTML を解析したことがあるので、今日は Vue ドラッグ アンド ドロップを使用して、Ku...