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

推薦する

vuex データの永続化のための 2 つの実装ソリューション

目次ビジネス要件:解決策 1: vuex-persistedstate解決策2: vuex-pers...

Excelアップロード機能を実現するVue + iViewの完全コード

1. HTML部分 <Col span="2">ファイルをアップロー...

スライダー検証コードを実装するJavaScript

この記事では、スライダー検証コードを実装するためのJavaScriptの具体的なコードを参考までに共...

Dockerがログファイルを保存する場所の詳細な説明

目次ログはどこに保存されますか?コンテナ内のアプリケーションからのログを表示するDockerデーモン...

MySQLの整数データ型tinyintの詳細な説明

目次1.1Tinyint型の説明1.2 練習環境の説明1.3 未署名属性の追加1.3.1 SQLモー...

MySQL ストレステストツールの使い方

1. MySQL 独自のストレステストツール - Mysqlslap mysqlslap は、mys...

Docker-compose ワンクリックデプロイ gitlab 中国語版の方法手順

1. GitLabの紹介Gitlab 公式アドレス: https://about.gitlab.co...

Jenkins + Docker + ASP.NET Core の自動デプロイメントの問題について (落とし穴を避ける)

このブログを書くつもりはなかったのですが、実際の操作中に、ネットワークの問題に圧倒されたこと (ネッ...

MyISAMとInnoDBの違いについてお話しましょう

主な違いは次のとおりです。 1. MySQL はデフォルトで MyISAM を使用します。 2. M...

MySQL エラー: 接続数が多すぎる場合の解決策

MySQLデータベースの接続が多すぎますこのエラーは明らかに、mysql_connect の後に m...

MySQL 完全崩壊: クエリフィルタ条件の詳細な説明

概要実際のビジネス シナリオ アプリケーションでは、ビジネス条件に基づいて対象データを取得およびフィ...

JavaScript の find() メソッドと filter() メソッドの違いのまとめ

目次序文JavaScript find() メソッドJavaScript filter() メソッド...

中国語ウェブコンテンツを紹介する10の経験

<br /> テキスト、シンボル、リンクの 3 つの側面に焦点を当て、主に中国語で、私の...

Vue は無限ロードウォーターフォールフローを実装します

この記事では、参考までに、無限ロードウォーターフォールフローを実現するためのVueの具体的なコードを...

Docker イメージのローカル Elasticsearch ポート操作へのアクセス

dockerスタックによってデプロイされたイメージサービスを使用すると、イメージを入力した後、理論的...