TCPプロトコルハンドシェイクと手を振る

 

TCPプロトコルを知る

TCPは「Transmission Control Protocol」と呼ばれ、データの送信を詳細に制御するトランスポート層のプロトコルです。 
特徴:

  • バイト指向
  • 安全で信頼できる
  • 接続指向

TCPプロトコルセグメント形式

  • 送信元ポート番号と宛先ポート番号:これはUDPと同じです。各データは、どのプロセスからどのプロセスに到達するかを知っている必要があります。
  • 32ビットのシリアル番号と32ビットの確認シリアル番号:ここでのシリアル番号と確認信号は、2つの通信プロセスがデータを送受信するときに相互に応答する情報として理解できます。たとえば、プロセスAがシーケンス番号1000からプロセスBへのデータの送信を開始し、5つのデータを送信します。次に、Bがデータ応答を受信するとき、ここでのAの確認シーケンス番号は1006からである必要があります。1006でない場合、たとえば1003は、1004、1005データパケットBが受信されなかったことを意味するため、Aは再送信メカニズムを開始します。 。これはデータの信頼性を保証し、TCPの特性の1つでもあります。シリアル番号はプロセスがメッセージを送信する番号であり、確認は宛先プロセスが返すと予想される番号です。比較を実行して、データパケットが到着するかどうかを確認します。
  • 4ビットTCPヘッダー長:ここでの4ビットTCPヘッダー長は、長さを表す4ビットとして理解でき、4ビットに4を掛けた値は、TCPヘッダーの長さです。図から、ヘッダーの最小長は20バイトであることがわかります。つまり、ここでの4ビットTCPヘッダーの長さは、デフォルトで0101です。また、TCPヘッダーの長さは15 * 4 = 60バイトを超えることはできません。
  • 6ビットフラグ:①URG:緊急ポインタが有効かどうか②ACK:確認番号が有効かどうか③PSH:受信アプリケーションにTCPバッファからデータをすぐに読み取るように指示する④RST:相手が接続の再確立を要求し、RSTロゴを呼び出すセグメントのリセット⑤SYN:接続を確立する要求。同期セグメントを運ぶSYN識別子を呼び出します。⑥FIN:ローカルエンドが閉じていることを相手に通知します。FINを運ぶエンドセグメントを呼び出します。
  • 16ビットのウィンドウサイズ:ここでのウィンドウサイズは、TCPバッファーの残りのスペースのサイズを示す記号として見ることができます。フロー制御の役割を果たす。16がウィンドウがいっぱいの場合、今回はデータを受信できません。後で到着するデータは失われます。
  • 16ビットチェックサム:ここでのチェックサムは、送信者のCRCチェックによって満たされます。受信側がデータを検証するときに検証が失敗した場合、データに問題があると見なされます。ここでのチェックサムは、TCPヘッダーだけでなくデータ部分も検証します。
  • 16ビット緊急ポインター:データのどの部分が緊急データであるかを識別します。

    接続プロセス

    TCPプロトコルは接続指向であることを知っています。つまり、TCPプロトコルは、クライアントとサーバーが正常に接続した後でのみ使用できます。それでは、クライアントとサーバー間の接続プロセスは何ですか?簡単に言えば、3つのハンドシェイクと4つのウェーブハンドですが、主な考え方は、クライアントがサーバーに接続するには3つのハンドシェイクが必要であり、通信が完了したら切断するには4つのウェーブハンドが必要だということです。

  • 3つの握手

 

クライアントとサーバーの両方が握手をする前にいくつかの準備をしました。サーバーは最初に記述子を割り当て、次にsockaddr_in構造体に入力し、作成されたファイル記述子とサーバーポートをバインドし、次にファイル記述子がリスナー記述子になるようにリッスンし、最後にクライアントに受け入れるまでブロックします。接続します。クライアントは比較的単純で、ファイル記述子を割り当て、sockaddr_in構造体に入力し、最後に接続してサーバーが応答するまでサーバー接続を要求します。

クライアントが接続を介してサーバーの応答を要求すると、クライアントは同期メッセージセグメント、つまりSYN要求をサーバーに送信し、送信後にサーバーの応答を待ちます。サーバーはSYN同期セグメントを受信すると、クライアントにACK応答を送信します。つまり、クライアントが送信した同期セグメントを受信します。同時に、サーバーはSYN同期セグメントも送信してクライアントに要求します。応答します。SYN同期セグメントを受信した後、クライアントはACK応答も送信してサーバーに応答します。このプロセスは、3つのハンドシェイクのプロセスです。

このように、クライアントとサーバー間の接続は両方とも、両方とも要求を送信する必要があり、両方が応答する必要があります。この図から、SYN_SENTは要求接続ステータスであり、SYN_RCVDは待機接続ステータスです。スリーウェイハンドシェイクが成功すると、サーバーとクライアントの両方がESTABLISHED状態、つまりTCP接続が成功し、この時点でデータを送信できます。

このプロセス中に、クライアントのSYN要求が失われた場合、サーバーは応答せず、クライアントには待機時間があります。待機時間が到着し、ACK応答が受信されない場合、クライアントは別の要求を開始します。複数の要求が失敗した場合、クライアントはネットワークが異常であると判断し、再度要求しない場合があります。同様に、サーバーはクライアントのSYN要求を受信した後、ACK応答を送信し、SYN要求を送信します。クライアントがサーバーACKに応答しない場合、サーバーはネットワークが異常であると判断されるまで再送信します。したがって、3つのハンドシェイクのいずれかが欠落している場合、接続は成功せず、通信はできません。したがって、3ウェイハンドシェイクは、TCPの信頼性を確保する方法でもあります。

3ウェイハンドシェイクの目的は何ですか?
回答:個人的な理解は、2つのパーティのシリアル番号と確認番号を同期させ、tcpウィンドウのサイズ情報を交換することで理解しやすくなります。
ソリューションを完了するために2回のハンドシェイクが3回必要なのはなぜですか?
回答:エラーの原因となる無効な接続要求セグメントが突然サーバーに送信されるのを防ぐために、3ウェイハンドシェイクが必要です。

4回振ります

クライアントとサーバー間のデータ転送が完了した後、クライアントには要求がないため、この時点でcloseが呼び出されてファイル記述子が閉じられ、FIN_WAIT_1状態になり、FIN終了メッセージセグメントがサーバーに送信されます。サーバーからの応答を待ちます。ここでサーバーがFIN終了セグメントを受信すると、今度はサーバーがCLOSE_WAIT状態になります。そして、クライアントに応答してACKを送信します。クライアントがサーバーからACK応答を受信すると、FIN_WAIT_2状態になります。サーバーはcloseを呼び出すと、FIN終了セグメントをクライアントに送信します。この時点でLAST_ACK状態に入ります。このとき、クライアントはサーバーから送信されたFINを受信すると、サーバーにACKで応答し、クライアントはTIME_WAIT状態になり、TIME_WAITが終了すると、CLOSEDになり、正常に切断されます。サーバーは、クライアントから最後のACKを受信すると、CLOSED状態になります。正常に切断されました。

CLOSE_WAITおよびLAST_ACKステータス

3ウェイハンドシェイク中に、サーバーはSYNとACKを同時に送信できますが、サーバーから送信されたFINとACKが別々に送信されるのはなぜですか?これは実際にそうです。 

まず、クローズを呼び出すため、FINシグナルが送信されます。クライアントがcloseを呼び出すと、FIN終了セグメントを送信し、FIN_WAIT_1状態に入ります。ただし、サーバーのユーザーセグメントは実際にはこのセグメントを認識できません。カーネルはこのセグメントを単独で処理するため、カーネルはACKで応答します。このプロセスはユーザーコードによって決定されるのではなく、サーバーのFINはユーザーコードによってcloseを呼び出すことによって送信されるため、カーネルとサーバーがこの情報を同時に処理するとは限りません。したがって、FINとACKが同時に送信されるとは限りません。注:これは必ずしもここにあるとは限りません!ただし、SYNは3ウェイハンドシェイク中にカーネルによって直接送信されるため、同期送信を実現できます。

サーバーコードがcloseを呼び出さない場合、FIN終了セグメントが送信されていないことを意味します。つまり、接続されたサーバーは長い間CLOSE_WAIT状態のままですが、これによりどのような影響がありますか? 
サーバーは長い間CLOSE_WAIT状態のままです。つまり、割り当てられたファイル記述子が閉じられておらず、返されていません。次に、多数のCLOSE_WAITが存在すると、リソースリークが発生します。最後に割り当て可能なファイル記述子がない可能性があり、一部のクライアントが接続できなくなり、計り知れない影響が生じます。

TIME_WAIT

クライアントが最後にACK応答を送信した後、それはTIME_WAIT状態に入り、この状態でクライアントは何をしていますか? 
答えは待つことです!クライアントは、最終的にACK応答を送信した後、最後のACK応答が失われるのを防ぐために、TIME_WAIT状態に入ります。ここで、TIME_WAIT状態は2MSLを待機します。

ここでのMSLの単位は、メッセージの最大生存時間を意味する最大セグメント寿命です。ここでの生存時間とは、メッセージの発生から受信までのプロセス全体を指します。このプロセスの時間はMSLです。 
Linuxでは、cat / proc / sys / net / ipv4 / tcp_fin_timeoutを使用してMSLの値を表示できます。 

クライアントが最後にACK応答を送信した後、なぜ2MSLを待つのですか?

これは、最後のACKメッセージが確実に到着するようにするためです。クライアントは最後のACK応答の送信後にTIME_WAIT状態になるため、ACKメッセージが失われた場合、サーバーはMSLを待機した後にACK応答を受信して​​いないことを検出し、FINメッセージを再送信します。そのようなACK応答の時間と再送信されたFINの時間は正確に2MSLです。2MSLの待機後にクライアントがFINメッセージを受信しない場合は、サーバーがクライアントから送信されたACKメッセージを受信したため、接続が切断されます。 

ここで、クライアントが終了すると、TIME_WAIT状態になることがわかります。

つまり、TIME_WAIT時点で、クライアントとサーバー間のTCP接続はまだ存在しています。 
場合によっては、サーバーも切断を要求することがあり、サーバーは最初にFIN_WAIT_1に入ります。この場合、サーバーは最終的にTIME_WAIT状態になります。では、この状態の何が問題なのでしょうか。

ここでは、サーバーを終了し、サーバーがTIME_WAIT状態に入ったことを確認しました。この時点で、サーバーは再起動され、起動できないことがわかりました。このとき、起動に失敗したため、ポート番号のバインドに失敗しました。これはなぜですか?

実際、これはTIME_WAIT状態でもTCP接続が存在するため、現在のポート番号がまだバインドされているためです。サーバーを再起動したとき、このときのポート番号は解放されませんでした。そのため、バインドが失敗したことのみを通知します。

サーバーが多数のクライアント接続を処理する必要がある場合、各接続の存続時間は非常に短くなりますが、1秒あたりのクライアント要求の数は多くなります。このとき、サーバーがアクティブに接続を閉じると、多数のTIME_WAIT接続が生成されます。需要が大きいため、TIME_WAIT接続が大量に発生し、新しい接続を処理するためのサーバーポートが不十分になります。今回の解決方法は?

現時点では、関数setsockopt(listenfd、SOL_SOCKET、SO_REUSEADDR、&opt、sizeof(opt)を使用して、この問題を解決できます。この関数の機能は、同じポート番号でIPアドレスが異なる複数のソケット記述子を作成できるようにすることです。ソケット()およびbind()の間に呼び出すだけです。
 

42件のオリジナル記事を公開 いいね10 10,000人以上の訪問者

おすすめ

転載: blog.csdn.net/qq_37659294/article/details/104561843