1. ホーム
  2. Web プログラミング
  3. プログラミング10000問

コンピュータネットワークの伝送プロトコルTCPの3つのハンドシェイクと4つの波の原理

2022-01-16 13:48:14

TCP 3つのハンドシェイクと4つの波

以前は トランスポート層プロトコルのTCPとUDP TCPプロトコルはコネクション指向であり、コネクション指向にはコネクションの生成、維持、切断が必要であることが分かっています。

サーバーの状態遷移

[CLOSED->LISTEN] サーバはlistenを呼び出した後、LISTEN状態になり、クライアントが自身への接続を開始するのを待つ

[LISTEN->SYN_RCVD] 接続要求(同期メッセージセグメント)を待ち受けると、カーネルで管理されている接続待ちキューに入れ、クライアントからの接続要求を受け取ったことを確認するために、SYN+ACKメッセージをクライアントに送信します。

[SYN_RCVD->ESTABLISHED] サーバはクライアントからの確認メッセージを受信すると、ESTABLISHED 状態になり、データの読み書きができるようになる。

[ESTABLISHED->CLOSE_WAIT] クライアントが率先して接続を閉じ(closeを呼び出す)、サーバがクライアントから終了メッセージセグメントFINを受け取り、サーバがクライアントに対して閉じた接続を受け取ったという確認メッセージを返すと、CLOSE_WAITに入る。

[CLOSE_WAIT->LAST_ACK] サーバーがCLOSE_WAIT状態になると、サーバーは接続を閉じる準備ができていることを意味します(ただし、先に前のデータの処理を終える必要があります)。サーバーが本当に接続を閉じるためにクローズコールを行うと、クライアントにFINメッセージを送信して自らLAST_ACK状態になり、最後のACK(ここでACKはサーバーから送られたFINメッセージに対するクライアントの応答)を待機します。

[LAST_ACK->CLOSED] FINメッセージに対するクライアントからのACKを受信したので、サーバは接続を正常に終了した。

クライアントの状態遷移

[CLOSED->SYN_SEND] クライアントはconnectを呼び出して、サーバとの接続を確立したいことを示す同期メッセージセグメントを送信し、自らSYN_SEND状態になってサーバの応答を待ちます。

[SYN_SEND->ESTABLISHED] 接続呼び出しが成功すると、クライアントはサーバーからの応答メッセージACKを受け取り、ESTABLISHED状態になり、データの読み取りと書き込みができるようになります。

[ESTABLISHED->FIN_WIAT_1] クライアントが自発的にcloseを呼び、サーバに終了メッセージセグメントを送信し、FIN_WAIT_1状態になり、サーバからの応答を待つ。

[FIN_WAIT_1->FIN_WAIT_2] クライアントは、サーバーからの終了メッセージセグメントに対する承認 を受け取り、FIN_WAIT_2 状態になってサーバーからの終了メッセージセグメント を待ち始めます。

[FIN_WAIT_2 -> TIME_WAIT] サーバから終了メッセージセグメントを受け取ったクライアントは、TIME_WAIT に入り、サーバから送られてきた終了メッセージセグメントに対して LAST_ACK の応答を送信する。

[TIME_WAIT -> CLOSED] 2MSL (Max Segment Life)待機後、CLOSED状態になる (サーバがLASK_ACKを受信せず、再送が必要な場合)。

TCP状態遷移図

画像

TCPでよくある面接の質問

なぜ1回や2回でなく、3回の握手なのか?

A: 3ハンドシェイクは、双方がデータを送信する準備をすること(双方は互いに準備が整っていることを知る)と、双方が初期シーケンス番号を取り決めること(ハンドシェイク中に送信し確認する)の2つの重要な機能を実現するものです。

  • ここで、3回のハンドシェイクから2回のハンドシェイクのみに変更すると、デッドロックが発生する可能性があります。例として、コンピュータSとCの間の通信を考えてみましょう。CがSに接続要求パケットを送信し、Sがこのパケットを受信して確認応答パケットを送信したと仮定します。
    2つのハンドシェイクの合意により、Sは接続が正常に確立されたとみなし、データパケットの送信を開始することができます。
    しかし、CはSのアンサーパケットが送信中に失われた場合、Sの準備ができているかどうかわからないし、Sがどのようなシーケンス番号を確立しているかもわからないし、CはSが自分の接続要求パケットを受け取ったかどうかさえも疑わしい。
    この場合、Cは接続が正常に確立されていないと判断し、Sから送信されたデータパケットを無視し、接続確認応答を待つだけでパケットに応答することになります。一方Sは、送信したパケットがタイムアウトした後、同じパケットを繰り返し送信しています。この結果、デッドロックが発生する。
  • {を使用します。 2つのハンドシェイクはまた、サーバーリソースの無駄を引き起こす可能性があり、どのように言って、栗を与える、今日CはSに接続要求を送信し、Sの応答を待って、SはCにACKを送信した後、それは接続が確立されていると考えられ、我々はそれが確立した後、接続を維持する必要があることを知って、この時点でSのオペレーティングシステムがリソースとスペースを確保する必要がありますしますC応答へのSの応答が失われたものとします。Cは、接続が正常に確立されていない応答を受信しなかった、正常に通信することはできません、この時点で接続のSのメンテナンスは、失敗した接続であり、正常に通信することはできません、それは、今日百万のクライアントがサーバーに接続要求を送信すると仮定すると、結果は応答で受信されていない、この時点でSは、サーバーのリソースを無駄に百無駄接続を維持されます。 {を使用します。

なぜ3回の握手と4回のウェーブなのか

A:サーバー側は、クライアント側からSYN接続要求メッセージを受信すると、そのままSYN+ACKメッセージを送信することができるからです。ACKメッセージは応答、SYNメッセージは同期のために使われます。

しかし、接続を閉じるとき、Server側はFINメッセージを受信しても、おそらくすぐにSOCKETを閉じないので、まずACKメッセージを返信して、Client側に "あなたが送ったFINメッセージを受信しました "と伝えるしかない。

すべてのメッセージを送信し、Server側ですべてのデータを処理した後に初めて、Server側は切断できることを示すFINメッセージを送信できるので、一緒に送信することはできません。そのため、4段階のハンドシェイクが必要です。

接続が確立されたが、クライアントが突然失敗した場合はどうすればよいですか?

A:TCPコネクションには、キープアライブタイマーがあります。明らかに、クライアントに障害が発生した場合、サーバーは待ち続けることができず、無駄にリソースを消費することになります。

サーバーは、クライアントからリクエストを受けるたびに、このタイマーをリセットします。時間は通常2時間に設定され、2時間経ってもクライアントからデータを受信しなかった場合、サーバーはプローブメッセージセグメントを送信し、その後75秒ごとに送信します。

10回連続でプローブを行っても応答がない場合、サーバーはクライアントに障害が発生したと判断し、接続を終了する。

なぜTIME_WAITステータスがあるのですか?

では、テストをしてみましょう。まずサーバーを起動し、次にクライアントを起動し、Ctrl-Cでサーバーを終了させ、すぐにサーバーを再実行すると、結果は次のようになります。

画像

これは、サーバーアプリケーションは終了しているものの、TCPプロトコル層は完全に切断されていないため、同じサーバーポートを再びリッスンすることができないからです。これをnetstatコマンドで確認してみましょう。

  • TCPプロトコルは、積極的に接続を閉じる側がTIME_ WAIT状態であり、CLOSED状態に戻る前に2MSL(最大セグメント寿命)待つことを要求している。
  • Ctrl-Cでサーバーを終了させたので、サーバーは積極的に接続を閉じたものであり、TIME_WAIT期間中は同じサーバーポートを再びリッスンすることができません。
  • MSLはRFC1122では2分とされていますが、OSによって実装が異なり、Centos7では60sがデフォルトの設定となっています。
  • Centos7 ではデフォルトで 60s になっています; /{li cat /proc/sys/net/ipv4/tcp_fin_timeout をクリックすると、mslの値が表示されます。

TIME_WAITが2MSLである理由を考えてみよう

MSL は TCP メッセージが生存できる最大時間なので、TIME_WAIT を 2MSL にすることで、まだ受信していないセグメントや送信の両方向で遅れているセグメントが確実に消えます (そうしないとサーバーはすぐに再起動し、前のプロセスから遅れたデータを受信するかもしれませんが、このデータは不正確である可能性があります) 。

また、最後のメッセージが確実に届くことが理論的に保証されている(最後のACKが失われたと仮定すると、サーバーはFINを再送する。この時点でクライアントプロセスは消えていますが、TCP 接続はまだ生きており、 LAST_ACK はまだ再送することができます)。

TIME_WAIT状態によるバインド失敗の解決策

  • サーバーとのTCP接続が完全に切れるまで再試聴を許可しない(場合によっては不合理な場合がある
  • サーバーが非常に多くのクライアント接続を処理する必要がある(各接続の寿命は非常に短いかもしれないが、非常に多くのクライアントが毎秒要求している)。
  • サーバーが積極的に接続を閉じる場合 (たとえば、一部のクライアントが非アクティブの場合、サーバーによるクリーンアップが必要)、大量の TIME_WAIT 接続が生成されます。
  • 大量のリクエストがあるので、大量のTIME_WAITコネクションが発生する可能性があり、それぞれが通信五重奏(送信元IP、送信元ポート、送信先IP、送信先ポート、プロトコル)を占有しているのです。ここで、サーバipとポート、プロトコルは固定である。新しいクライアント接続が、TIME_WAITが占有しているリンクとipとポート番号が重複している場合、問題が発生する。
  • {を使用します。 setsockopt() を用いて、ソケットディスクリプターのオプション SO_REUSEADDR を 1 に設定し、同じポート番号で異なる IP アドレスのソケットディスクリプターを複数作成できるようにする。

画像

  • setsockoptを追加することで、ctrl+cでサーバを終了させた後、すぐに起動させることができます

画像

{リンク {リンク 上記は、コンピュータネットワークの伝送プロトコルのTCP 3ハンドシェイクと詳細の4波の原則であり、TCP 3ハンドシェイクと4波についての詳細は、スクリプトホーム他の関連記事に注意を払うください