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

推薦する

CSSを使用して複数の画像を中央に水平に表示する方法

まず実装手順について説明します。最終結果 2. コードの実装HTML部分 <div class...

Linux デュアル ネットワーク カード バインディング スクリプト メソッドの例

Linux の操作と構成作業では、デュアル ネットワーク カードのバインディングがよく使用されます。...

Dockerを使用してphabricatorをインストールする方法

ここでは Ubuntu 16.04 システムを使用しています。 dockerを使用したインストールh...

WeChat 8.0の爆発的な特殊効果を実現するために300行以上のCSSコードが必要

WeChat 8.0 アップデートの主な特徴は、アニメーション絵文字のサポートです。送信するメッセー...

Linux 構成 SSH パスワードフリーログイン「ssh-keygen」の基本的な使い方

目次1 SSHとは何か2 SSHパスワードフリーログインを設定する2.1 必要なソフトウェアのインス...

光るテキストとちょっとしたJS特殊効果を実現するCSS

実装のアイデア: CSSでtext-shadowを使用してテキストの光る効果を実現します効果画像: ...

Vueはソースコード付きのリファレンスライブラリのメソッドを使用します

monaco-editor-vueの公式ソースコードは次のとおりです。インデックス 'mon...

JDBC が MySQL に接続して中国語を処理するときに文字化けする問題の解決方法の詳細説明

JDBC が MySQL に接続して中国語を処理するときに文字化けする問題の解決方法の詳細説明最近、...

MySQLを定期的にバックアップしてQiniuにアップロードする方法

ほとんどのアプリケーション シナリオでは、緊急時に備えて重要なデータをバックアップし、安全な場所に保...

開発効率の向上に役立つ 56 個の実用的な JavaScript ツール関数

目次1. デジタルオペレーション(1)指定された範囲内で乱数を生成する2. 配列操作(1)配列の順序...

派手なカルーセル効果を実現するJavaScript

この記事では、JavaScriptで派手なカルーセル効果を実装する2つの方法を紹介します。具体的な内...

Vue で Baidu Map を呼び出して経度と緯度を取得する

プロジェクトでは、現在地の緯度経度を取得したり、場所を検索して緯度経度情報を取得したりする必要があり...

Mysql SQL ステートメント演習 (50 問)

テーブル名とフィールド–1. 学生リスト学生 (s_id、s_name、s_birth、s_sex)...

HTML ユーザー登録ページ設定ソースコード

上記の Web ページをデザインします。 <!DOCTYPE html> <htm...

Nginx タイムアウト設定の詳細な説明

最近、プロジェクトで nginx を使用し、バックエンドで Java を使用しました。バックエンドで...