Linux ネットワーク - トランスポート層

目次

1. 港のコンセプトについてもう一度話しましょう

 2. UDPプロトコル

1.UDPプロトコル形式

2. UDPの特徴

3. データグラム指向

4.UDPバッファ 

5. UDP利用時の注意点 

 6. カーネルにおけるUDPプロトコルの表現形式

 7. UDPに基づくアプリケーション層プロトコル

 3.TCPプロトコル

1.TCPプロトコル形式

2.TCP確認応答メカニズム

3. タイムアウト再送メカニズム

4. TCP メッセージの 6 ビットのフラグ ビット

5. 引き違い窓

6. フロー制御

7.輻輳制御

8. 応答の遅れ

9. 応答に便乗する 

10. バイトストリーム指向 

11. 粘着袋問題 

12. 接続管理メカニズム 

13. listen の 2 番目のパラメータ

14.TCP異常事態 

15.TCPの概要

16. TCPアプリケーション層プロトコルに基づく 

4.TCP/UDPの比較


1. 港のコンセプトについてもう一度話しましょう

ポート番号 (ポート) は、ホスト上で通信するさまざまなアプリケーションを識別します。

TCP/IP プロトコルでは、「送信元 IP」、「送信元ポート番号」、「宛先 IP」、「宛先ポート番号」、「プロトコル番号」の 5 つのタプルを使用して通信を識別します (netstat で確認できます)。 -n);

Netstat の共​​通オプション:

  • n エイリアスの表示を拒否し、表示可能なすべての数値を数値に変換します。
  • l リスニング中のサービスステータスのみをリストします。
  • p は、関連するリンクを確立するプログラムの名前を表示します。
  • t (tcp) は tcp 関連のオプションのみを表示します
  • u (udp) は udp 関連のオプションのみを表示します
  • a (all) はすべてのオプションを表示します。LISTEN 関連はデフォルトでは表示されません

pidof: は、サーバーのプロセス ID を表示する場合に非常に便利です。

  • 構文: pidof [プロセス名]
  • 機能: プロセス名からプロセス ID を表示

 2. UDPプロトコル

1.UDPプロトコル形式

16 ビット UDP 長: データグラム全体 (UDP ヘッダー + UDP データ) の最大長を示します。
16 ビット チェックサム: チェックサムが正しくない場合、直接破棄されます; 

UDP プロトコルはヘッダーとペイロードをどのように分離しますか?

  1. UDP プロトコルには固定長のヘッダーがあります。
  2. 固定長のヘッダーを読み取り、16 ビットのメッセージの長さ、つまりヘッダーとペイロードの長さを取得するだけです。
  3. ペイロード長は、16 ビットのメッセージ長から 8 バイトの固定長ヘッダーを減算して得られます。

UDP プロトコルがどのように上向き配信を実現するか:

  • ヘッダーを読み取れれば宛先ポートが分かりますが、その宛先ポートが今回提供するアプリケーション層のプロセスとなります。

2. UDPの特徴

UDP 送信のプロセスは手紙の送信に似ています。

  1. 接続なし: 相手側の IP とポート番号がわかっている場合は、直接送信します。接続を確立する必要はありません;接続を確立する必要はありません。あ>
  2. 信頼性が低い: 確認メカニズムも再送信メカニズムもありません。ネットワーク障害によりセグメントを相手に送信できない場合、UDP プロトコル層はアプリケーション層にエラー メッセージを返しません。
  3. データグラム指向: データの読み取りおよび書き込みの数と量を柔軟に制御できません。

3. データグラム指向

アプリケーション層は任意の長さのメッセージを UDP に渡し、UDP はそれを分割も結合もせずにそのまま送信します。

UDP を使用して 100 バイトのデータを転送します。

  • 送信者が sendto を 1 回呼び出して 100 バイトを送信した場合、受信者もループ内で recvfrom を 10 回呼び出して毎回 10 バイトを受信するのではなく、対応する recvfrom を 1 回呼び出して 100 バイトを受信する必要があります。

4.UDPバッファ 

  • UDP には実際の送信バッファがないため、sendto の呼び出しはカーネルに直接渡され、その後の送信アクションのためにデータがネットワーク層プロトコルに渡されます。
  • UDP には受信バッファがあります。ただし、この受信バッファは、受信した UDP パケットの順序が送信された UDP パケットの順序と一致していることを保証できません。バッファがいっぱいの場合、到着した UDP データは破棄されます。これは、UDP が示すことでもあります。 . TCP の信頼性の低さに関連して。
  • UDP ソケットは読み取りと書き込みの両方が可能です。この概念は全二重と呼ばれます。

5. UDP利用時の注意点 

  1. UDP プロトコルのヘッダーの長さは最大 16 ビット、つまり、UDP で送信できるデータの最大長は 64K (UDP ヘッダーを含む) であることがわかりました。
  2. ただし、64K という数字は、今日のインターネット環境では非常に小さい数字です。
  3. 送信する必要があるデータが 64K を超える場合は、アプリケーション層でデータを手動でサブパッケージ化し、複数回送信し、受信側で手動で組み立てる必要があります。

 6. カーネルにおけるUDPプロトコルの表現形式

Linux カーネルは C 言語で書かれており、トランスポート層とネットワーク層はオペレーティング システムに属するため、トランスポート層のプロトコルも C 言語で書かれています。 C言語で書かれているので、UDPの形式で構造体を考えれば、構造体やビットフィールドを使って簡単に実装できます。ペイロードのサイズを決定できないのではないかという疑問がありますが、構造をどのように定義するのでしょうか? C99 構文は柔軟な配列をサポートします。

struct Udp
{
    uint16_t src_port;
    uint16_t dst_port;
    uint16_t udp_len;
    uint16_t check;
    char date[];//柔性数组。
};

 7. UDPに基づくアプリケーション層プロトコル

  • NFS: ネットワーク ファイル システム
  • TFTP: トリビアル ファイル転送プロトコル
  • DHCP: 動的ホスト構成プロトコル
  • BOOTP: ブート プロトコル (ディスクレス デバイスのブート用)
  • DNS: ドメイン名解決プロトコル
  • 独自の UDP プログラムを作成する場合、カスタム アプリケーション層プロトコルも含まれます。 

 3.TCPプロトコル

TCP の正式名称は「Transmission Control Protocol(伝送制御プロトコル)」で、その名のとおり、データの伝送を詳細に制御する必要があります。

上位のアプリケーション層サービスが send と write を使用して「ネットワーク」に送信する必要があるデータを送信するとき、実際、アプリケーション層にとっては、send と write を呼び出している限り、データは送信されたと見なされます。実際にはそうではなく、データは送信する前にトランスポート層プロトコルを通過する必要があります。アプリケーション層については、トランスポート層がデータをどのように送信するのか、いつ送信するのかは知らない、知らない、あるいは気にしていないと述べた。どのように送信するか、いつ送信するか、送信データの信頼性をどのように確保するか、これがトランスポート層の TCP が行うべきことです。

1.TCPプロトコル形式

1. 送信元/宛先ポート番号: データがどのプロセスから送信され、どのプロセスに送信されるかを示します。 

2. 32 ビットのシリアル番号/32 ビットの確認番号: 詳細は後ほど説明します。

3. 4 ビット TCP ヘッダーの長さ: TCP ヘッダーにある 32 ビット ビットが何個あるか (4 バイトが何個あるか) を示します。つまり、TCP ヘッダーの最大長は 15 * 4 = 60 です。

4. 6ビットフラグ:

  • URG: 緊急ポインタは有効ですか?
  • ACK: 番号が有効かどうかを確認します
  • PSH: 受信側アプリケーションに、TCP バッファからデータをすぐに読み取るよう要求します。
  • RST: 相手は接続を再確立する必要があります。RST 識別子を運ぶメッセージ セグメントをリセット セグメントと呼びます。
  • SYN: 接続を確立するリクエスト。SYN 識別子を同期セグメントと呼びます。
  • FIN: ローカルエンドが閉じようとしていることを相手に通知する FIN 識別子を持つセグメントをエンドセグメントと呼びます。

5. 16 ビット ウィンドウ サイズ: 詳細については後ほど説明します。

6. 16 ビット チェックサム: 送信側でのパディング、CRC チェック。受信側でのチェックが失敗した場合は、データに問題があると見なされます。ここでのチェックサムには、TCP ヘッダーだけでなく、
7. 16 ビット緊急ポインタ: データのどの部分が緊急データであるかを識別します。
8. 40 バイトのヘッダー オプション: 今のところ無視されます;

TCP プロトコルはヘッダーとペイロードをどのように分離しますか?

まず、4 桁のヘッダー長 cold len が読み取られます (ヘッダー長は len*4 バイトです)。ヘッダー データはメッセージから削除され、残りはペイロードになります。

注: オプションの長さを指定しない場合、4 桁のヘッダーの長さは 20 バイトになります。当然、4 桁のヘッダーの長さが入力されるバイナリ フィールドは 1001 になります。

TCP が上向き配信を実現する方法:

ヘッダーを読めば当然宛先ポートが分かりますが、これが今回納品したアプリケーション層の処理です。

2.TCP確認応答メカニズム

信頼性:

TCP について言及すると、まず信頼性が考えられます。では、何が信頼性であり、何が信頼性ではないのでしょうか?

信頼性の低さ: データの損失 (パケット損失)、送信が速すぎる、送信が遅すぎる、乱れ、重複などはすべて信頼性がありません。

その反対は当然信頼性です。

確認応答:パケットロス問題の解決

TCP の信頼性を確保するために、複数のメッセージを相手のホストに送信する場合、相手のホストがメッセージを受信したかどうかをどのように確認すればよいでしょうか?とても簡単で、相手が応答してくれれば、こちらが送ったメッセージデータを相手が受信したことが分かります。

ただし、これは毎回 1 つの応答を送信するシリアルな処理であり、必然的に効率が若干低くなります。そのため、実際にはメッセージの送信と応答は並列ではなく、同時実行されます。

 シリアル番号と確認用シリアル番号の役割:

それでは、応答を受信しないデータ メッセージがあり、受信側で受信したメッセージの順序が送信者が送信した順序と必ずしも一致しない場合、どのデータ メッセージが失われたかをどのように判断すればよいでしょうか?

データがピア ホストに送信されない場合、データは TCP バッファに保存されます。

実際、TCP の送信バッファは char 配列であり、配列には当然添字があるため、TCP はバッファ内の各バイトに番号を付けます。そして、この番号は実際には、TCP プロトコル ヘッダーのシーケンス番号です。毎回送信するデータにシーケンス番号が付いていれば、受信側はシーケンス番号に従ってデータを並べ替えたり重複を排除したりするだけで済み、まずデータの順序性が保証され、どのメッセージにどのようなメッセージが含まれているかを簡単に知ることができます。届いていないのですが。

受信側では、メッセージを受信して​​シーケンス番号を取得した後、確認信号を含む応答メッセージで送信側に応答できます。応答メッセージの確認シーケンス番号は、メッセージのシーケンス番号の次の位置です。前のメッセージ。次回はその場所から送信を続けることができます。

シーケンス番号を確認する仕組みとは、送信者に次の送信位置を知らせること、つまり、現在の確認番号 X までのメッセージをすべて受信したことを送信者に伝えることを意味します。

3. タイムアウト再送メカニズム

データメッセージを送信しても応答がなかった場合、すぐに新しいメッセージを相手に送信する必要がありますか?いいえ、まず、応答を受け取らない場合は、通常次の 2 つの状況が考えられることを知っておく必要があります。

  1. 相手はデータ メッセージを受信しましたが、送信者に送信された応答が失われました。
  2. 確かに相手はメッセージを受信して​​いないので、当然応答は返されません。

ケース 1 の場合:現在のメッセージ データに対する応答メッセージは受信できませんでしたが、しばらくしてから次のメッセージに対する応答メッセージを受信しました。応答メッセージの仕組みでは、次のメッセージの応答を受け取ります。これは、相手が現在のメッセージをすべて受信したことを意味します。

状況 1 の場合、ホスト B は大量の重複データを受信します。その場合、TCP プロトコルはそれらのパケットを重複パケットとして識別し、重複パケットを破棄できる必要があります。このとき、上記のシーケンス番号を使用して簡単に実行できます。減量効果が得られやすいです。 

ケース 2 の場合:送信されたメッセージが実際に失われたかどうかはわかりません。最初の状況が発生することをまだ予想していますが、しばらくすると、まだメッセージが失われています。 no 確認応答を受信した後、この時点でメッセージを再送信する必要があります。しかし最終的には、メッセージを直接再発行することはできません。 状況 2 に直面して、一定時間待機しても応答メッセージが存在しない場合は、メッセージを再送信します。これがタイムアウト再送信メカニズムです。

では、タイムアウトになった場合、どのようにタイムアウトを判断するのでしょうか? 

  • 最も理想的な状況では、「この時間内に確認応答が返されなければならない」ことを保証するための最小限の時間を見つけます。
  • ただし、この時間の長さはネットワーク環境によって異なります。
  • タイムアウトの設定が長すぎると、全体的な再送信効率に影響します。
  • タイムアウトの設定が短すぎると、重複したパケットが頻繁に送信される可能性があります。

あらゆる環境で高パフォーマンスの通信を保証するために、TCP はこの最大タイムアウトを動的に計算します。

  • Linux (BSD Unix および Windows にも同じことが当てはまります) では、タイムアウトは 500 ミリ秒単位で制御され、各タイムアウト再送信のタイムアウトは 500 ミリ秒の整数倍になります。
  • 一度再送信しても応答がない場合は、2*500ms 待ってから再送信してください。
  • それでも応答がない場合は、再送信まで 4*500 ミリ秒待機するなど、指数関数的に増加します。
  • TCPは再送回数が一定回数蓄積すると、ネットワークまたは相手ホストに異常があると判断し、強制的にコネクションを切断します。

 4. TCP メッセージの 6 ビットのフラグ ビット

1.ACK

説明: 有効かどうかを確認します。つまり、現在のメッセージが確認され有効である場合、現在のメッセージは応答メッセージである必要があります。

2.RST

説明: 相手は接続の再確立を必要としています。たとえば、ネットワークのジッターによりクライアントが切断された場合、サーバーはそれを認識しないため、2 つの当事者間の接続の再確立に不一致が生じます。クライアントは、SRT を使用してメッセージを送信して、接続の再確立を要求できます。

3.URG

注: 緊急ポインタが有効であるかどうかに関係なく、緊急ポインタは 16 ビット整数データです。緊急ポインタが有効な場合、このメッセージは受信バッファで待つ必要はなく、直接キューに入れて上位システムによって取得できます。ペイロードには 1 バイトの緊急データも含まれており、16 ビットの緊急ポインタがペイロード内の緊急データの開始位置を識別します。

4.シン

注: TCP は接続指向であるため、TCP 接続を要求するメッセージがある場合、接続を開始するメッセージには SYN フラグが付けられます。

5.終了

注: 双方間の通信が終了し、接続が切断される場合には、切断を要求するメッセージが必要です。 FIN フラグが付きます。

6.PSH

通信当事者の一方が、相手方の受信バッファに十分な空き領域が残っていないと感じた場合、相手方のアプリケーション層にバッファからデータを迅速に読み取るよう促すことができます。この種のメッセージには PSH フラグが付けられます。

5. 引き違い窓

先ほど確認応答戦略について説明しました。送信されるデータ セグメントごとに、ACK 確認応答を行う必要があります。ACK を受信した後、次のデータ セグメントが送信されます。これには大きな欠点があります。つまり、パフォーマンスが低下します。特に、これは次のような場合に当てはまります。データの往復時間が長くなります。

この 1 回送信 1 回受信方式はパフォーマンスが低いため、複数のデータを一度に送信する (実際には複数のセグメントの待ち時間をオーバーラップさせる) ことでパフォーマンスを大幅に向上させることができます。

引き違い窓はどこにありますか?それは何ですか?

今日では、データ パケットは TCP 送信バッファ内にあるため、TCP 送信制御は主に送信されるデータ パケットに対して行われることがわかりました。スライディング ウィンドウは TCP 内にあります。送信バッファ。スライディング ウィンドウは 2 つの指針にすぎません。

引き違い窓を大きくすることはできますか?小さくすることはできますか? 0でもいいですか?

相手の受け入れ能力に応じて 2 つのポインタの位置を変更するだけで、大きくしたり小さくしたりすることができます。

0 にすることもできます。つまり、送信者はデータを送信しません。

スライド窓はスライドし続けることができますか?どうやって滑るの?

スライディング ウィンドウはバッファ全体を自然に 3 つの部分に分割し、スライディング ウィンドウの前には応答を受信したデータ パケットがあり、スライディング ウィンドウの後にはまだ送信されていないパケットがあります。スライディング ウィンドウには、ACK を待たずに直接送信されたデータ パケット、または送信されたがまだ応答を受信して​​いないデータ パケットが含まれます。

ウィンドウ内の送信メッセージに対する応答が受け入れられた場合は、Win_begin を右に直接移動できます。

また、スライディングウィンドウの左端のメッセージは相手に受け入れられているため、送信バッファは空いており、送信バッファはリング状に設計でき、スライディングウィンドウが境界をまたぐことはない。

中国東部のウィンドウのサイズを更新するにはどうすればよいですか?根拠は何ですか?

スライディング ウィンドウのサイズによって、今回送信されるデータ パケットのサイズが決まります。TCP は、データが欠落しないことを保証するだけでなく、欠落した場合でも時間内に通知することもできます。また、毎回送信するデータ量が相手に受け入れられる量であることを確認する必要があり、相手の受信能力が弱ければ送信するデータ量は少なく、相手の受信能力が強ければ送信するデータ量は少なくなります。さらにデータを送信します。したがって、相手の受信バッファのサイズを知る必要があります。これには、TCP メッセージ形式で 16 ビットのウィンドウ サイズが必要です。

16 はウィンドウ サイズです。 は相手に受け入れ能力を通知するために使用されます。

したがって、スライディング ウィンドウのサイズは、相手のウィンドウのサイズと一致する必要があります。つまり、次のようになります。

  • Win_begin = シリアル化を確認します。
  • Win_end = Win_begin + Win_size。

データ パケットがスライディング ウィンドウ内で失われた場合はどうなりますか?

データ損失がある場合、失われたデータはウィンドウの最初の位置にある必要があります。受信した応答メッセージは画面上から削除されるため、メッセージが失われた場合は最初のメッセージを再送信してください。

例証します:

ウィンドウサイズとは、応答を待たずにデータを送信し続けることができる最大値を指します。上図のウィンドウサイズは 4000 バイト (4 セグメント) です。最初の 4 セグメントを送信する場合、応答を待つ必要はありません。 ; ACK が返され、直接送信されます。

最初の ACK を受信した後、スライディング ウィンドウは逆方向に移動し、後続のセグメントのデータの送信を続けます。
このスライディング ウィンドウを維持するために、オペレーティング システムはカーネルは、現在応答のないデータを記録するためにバッファを送信する必要があります。バッファから削除できるのは、確認応答されたデータのみです。ウィンドウが大きいほど、ネットワーク スループット レートが高くなります。

クイック再送信:

  • 特定のセグメントが失われると、送信者は常に 1001 のような ACK を受信します。これは、送信者に「私が欲しいのは 1001 です」と思い出させるようなものです。
  • 送信ホストが同じ「1001」応答を 3 回続けて受信すると、対応するデータ 1001 ~ 2000 を再送信します。
  • このとき、受信側が 1001 を受信した後、再度返される ACK は 7001 (2001 - 7000 であるため) ですが、受信側は実際に以前に受信しており、受信側オペレーティング システム カーネルの受信バッファに置かれていました。

このメカニズムは「高速再送制御」(「高速再送」とも呼ばれます)と呼ばれます。 

6. フロー制御

受信側のデータ処理速度には限界があります。送信側の送信速度が速すぎると、受信側のバッファがいっぱいになってしまいます。このとき、送信側が送信を続けるとパケットロスが発生します。
したがって、TCP は、受信側の処理能力に基づいて送信者の送信速度を決定することをサポートしています。このメカニズムは、フロー制御と呼ばれます。

  1. 受信側は受信できるバッファサイズをTCPヘッダーの「ウィンドウサイズ」フィールドに入れ、ACK側で送信側に通知します。
  2. ウィンドウ サイズフィールドが大きいほど、ネットワークのスループットが高くなります。
  3. 受信側は、バッファがほぼいっぱいであることを検出すると、ウィンドウ サイズをより小さい値に設定し、送信側に通知します。
  4. 送信者がこのウィンドウを受信すると、送信速度が低下します。つまり、スライディング ウィンドウが減少します。
  5. 受信側のバッファがいっぱいの場合、ウィンドウは 0 に設定され、送信側のスライディング ウィンドウ サイズは 0 になります。この時点で、送信側はデータを送信しませんが、ウィンドウ検出データを送信する必要があります。定期的にセグメント化して、受信側にウィンドウ サイズを知らせます。

受信側は送信側にウィンドウ サイズをどのように伝えるのでしょうか? TCP ヘッダーには、ウィンドウ サイズ情報を格納する 16 ビットのウィンドウ フィールドがあることを思い出してください。すると、最大 16 ビットであるという疑問が生じます。 -bit 数値は 65535 を表します。最大 TCP ウィンドウは 65535 バイトですか?
実際、TCP ヘッダーの 40 バイト オプションにはウィンドウ拡張係数 M も含まれています。実際のウィンドウ サイズはM ビット左にシフトされたウィンドウ フィールドの値; 

7.輻輳制御

TCP にはキラーとしてスライディング ウィンドウがあり、大量のデータを効率的かつ確実に送信できます。ただし、最初に大量のデータを送信すると、依然として問題が発生する可能性があります。
ネットワーク上には多数のコンピュータが存在するため、現在のネットワーク状態はすでに比較的混雑している可能性があります。現在のネットワーク状態を把握せずに、むやみに大量のデータを送信すると、状況がさらに悪化する可能性があります。

私を軽蔑しすぎていますか? 私が送信できる何千ものデータ パケットは、ネットワーク全体にとってはバケツの一滴にすぎません。ネットワークの混雑をさらに悪化させる可能性はありますか?

インターネット上には複数のホストが存在し、多数のホストがいつでも複数のパケットをネットワークに送信し、誰もが TCP プロトコルを使用するというコンセンサスを得る必要があります。したがって、ネットワークが混雑している場合、TCP がホストの送信速度の低下を防ぐことができれば、ネットワーク全体のホストが送信するデータも減ります。ネットワークへの負荷は徐々に回復します。

TCP ではスロー スタートメカニズムが導入されており、最初に少量のデータを送信し、パスを探索し、現在のネットワークの輻輳ステータスを確認します。 、次に、 データを転送する速度に従って決定します。

  1. ここでは輻輳ウィンドウと呼ばれる概念が導入されています
  2. 送信開始時に、輻輳ウィンドウ サイズを 1 として定義します。
  3. ACK 応答が受信されるたびに、輻輳ウィンドウは 1 ずつ増加します。
  4. データ パケットが送信されるたびに、輻輳ウィンドウが受信ホストからフィードバックされたウィンドウ サイズと比較され、小さい方の値が実際のスライディング ウィンドウとして使用されます。これは、この時点で送信に影響を与える要因は、輻輳ウィンドウだけではないためです。相手の受け入れ能力に加えて、現在のネットワークの混雑レベル。 2 つの値のうち小さい方がスライディング ウィンドウのサイズとして使用されます。ネットワークの輻輳が増大せず、相手がそれを受け入れることができることを確認してください。

上記のような輻輳ウィンドウの増加率は指数関数的であり、「スロースタート」とは、最初は遅いが、増加率が非常に速いことを意味します。 

  • 急速に増大しないようにするには、輻輳ウィンドウを単純に 2 倍にすることはできません。
  • ここではスロースタートと呼ばれるしきい値が導入されています
  • 輻輳ウィンドウがこのしきい値を超えると、指数関数的には増加せず、直線的に増加します。

  • TCP が開始されるとき、スロー スタートしきい値はウィンドウの最大値に等しくなります。
  • タイムアウトと再送信のたびに、スロー スタートしきい値は元の値の半分になり、輻輳ウィンドウは 1 にリセットされます。 

少量のパケット損失の場合、タイムアウト再送信のみがトリガーされます。大量のパケット損失の場合、ネットワークが混雑していると考えられます。
TCP 通信が開始されると、ネットワーク スループットが低下します。徐々に増加します。ネットワークの輻輳が発生すると、スループットはすぐに低下します。
輻輳制御は、最終的には、TCP プロトコルができるだけ早くデータを相手に送信することを目的としています。ただし、ネットワークに過度の圧力をかけないようにする必要があります。妥協策です。
TCP 輻輳制御のようなプロセスは、恋をしているような感覚 です。 >

8. 応答の遅れ

データを受信したホストがすぐに ACK 応答を返す場合、この時点での応答ウィンドウは小さくなる可能性があります。

  1. 受信側のバッファが 1M であるとすると、一度に 500K のデータを受信し、応答が即時であれば返されるウィンドウは 500K になります。
  2. しかし実際には、処理側の処理速度は非常に速く、10ms 以内に 500K のデータがバッファから消費されます。
  3. この場合、受信側の処理はまだ限界に達しておらず、ウィンドウを拡大しても処理できるため、受信側の処理が限界に達することはありません。
  4. 受信側が応答する前にしばらく待機する場合 (たとえば、応答する前に 200 ミリ秒待機する場合)、この時点で返されるウィンドウ サイズは 1M です。

必ず覚えておいてください。ウィンドウが大きいほど、ネットワーク スループットが向上し、伝送効率が高くなります。。私たちの目標は、ネットワークが混雑していないときに、伝送効率の向上を試みます。

  1. では、すべてのパケットに遅延を伴って応答できるのでしょうか? 決してそうではありません。
  2. 数量制限: N パケットごとに 1 回応答します。
  3. 時間制限: 最大遅延時間を超えた場合は 1 回応答します。

具体的な数とタイムアウト期間はオペレーティング システムによって異なります。通常、N は 2、タイムアウト期間は 200 ミリ秒です。

9. 応答に便乗する 

遅延応答に基づくと、多くの場合、クライアント サーバーはアプリケーション層でも「送受信」を行っていることがわかりました。これは、クライアントがサーバーに「調子はどうですか」と伝え、サーバーも同様に送信することを意味します。クライアントに「わかりました、ありがとう」と応答します。
このとき、ACK はそれを利用して、サーバーが応答した「わかりました、ありがとう」と一緒に返すことができます。 。 クライアント。

簡単に言うと:この応答は単なる応答ではなく、他の情報も含まれています。これはピギーバック応答と呼ばれます。

10. バイトストリーム指向 

TCP ソケットを作成し、カーネル バッファ内に送信バッファ受信バッファを作成します。 ;

  1. write を呼び出すと、データはまず送信バッファに書き込まれます。
  2. 送信バイト数が長すぎる場合は、複数の TCP パケットに分割されて送信されます。
  3. 送信バイト数が短すぎる場合は、バッファ長がほぼ同じになるまでバッファ内で待機するか、別の適切なタイミングで送信します。
  4. データを受信するとき、データはネットワーク カード ドライバーからカーネルの受信バッファーにも到達します。
  5. その後、アプリケーションは read を呼び出して受信バッファからデータを取得できます。
  6. 一方、TCP 接続には送信バッファと受信バッファの両方があるため、この接続ではデータの読み取りと書き込みの両方が可能です。この概念はと呼ばれます。全二重;

バッファの存在により、TCP プログラムの読み取りと書き込みは 1 対 1 で一致する必要はありません。次に例を示します。

  1. 100 バイトのデータを書き込む場合、write を 1 回呼び出して 100 バイトを書き込むことも、write 100 回を呼び出して毎回 1 バイトを書き込むこともできます。
  2. 100 バイトのデータを読み取る場合、書き込み方法を考慮する必要はなく、一度に 100 バイトずつ読み取ることも、一度に 1 バイトずつ読み取りを 100 回繰り返すこともできます。
  3. は水の流れのようなものです。 は一度に多くの時間を費やすことも、多くの場合には少なくなることができるため、バイト ストリーム指向と呼ばれます。

11. 粘着袋問題 

  • まず第一に、スティッキー問題の「パケット」がアプリケーション層のデータ パケットを指すことを明確にする必要があります。
  • TCPプロトコルのヘッダーには、UDPのような「メッセージ長」フィールドはありませんが、シーケンス番号などのフィールドがあります。
  • トランスポート層の観点から見ると、TCP メッセージは 1 つずつ到着し、シーケンス番号に従ってソートされ、バッファーに配置されます。
  • アプリケーション層の観点から見ると、私たちが目にしているのは、一連の連続したデータ バイトにすぎません。
  • すると、アプリケーションプログラムはそのような一連のバイトデータを見て、どの部分がどの部分から始まり、どの部分が完全なアプリケーション層データパケットであるのかを知りません。

では、パッケージの粘着性の問題を回避するにはどうすればよいでしょうか? それは、2 つのパッケージ間の境界を明確にするという 1 つの文に要約されます。 

  1. 固定長パケットの場合は、毎回固定サイズで読み取られるようにします。たとえば、Request 構造体が固定サイズの場合は、sizeof(Request) に従ってバッファの先頭から順に読み取ります。
  2. 可変長パケットの場合、パケット ヘッダーの位置でパケットの全長のフィールドを合意できるため、私たちが独自に作成したネットワーク バージョン計算ツールと同様に、パケットの終了位置を知ることができます。
  3. 可変長パッケージの場合、パッケージ間に明確な区切り文字を使用することもできます (区切り文字がテキストと競合しない限り、アプリケーション層プロトコルはプログラマ自身によって決定されます)。

UDP プロトコルの場合、「スティッキー パケットの問題」もありますか? 

  1. いいえ、UDP はデータグラム指向であり、データグラムの受信はアトミックです。完全なデータグラムが受信されるか、受信されません。UDP の場合、上位層がデータをまだ配信していない場合、UDP メッセージの長さは変わりません。同時に、UDP はデータを 1 つずつアプリケーション層に配信するため、データ境界は非常に明確です。
  2. アプリケーション層に立つ人の観点から見ると、UDP を使用する場合、完全な UDP メッセージを受け取るか受け取らないかのどちらかになり、「半分」の状況は存在しません。

12. 接続管理メカニズム 

通常の状況では、TCP は接続を確立するために 3 回のハンドシェイクを行う必要があります、そして 4 回波で切断します。接続

サーバー側のステータス遷移: 

  1. [CLOSED -> LISTEN] サーバーは listen を呼び出した後、LISTEN 状態に入り、クライアントの接続を待ちます。
  2. [LISTEN -> SYN_RCVD] 接続要求 (同期メッセージ セグメント) が監視されると、接続はカーネルの待機キューに入れられ、SYN 確認メッセージがクライアントに送信されます。
  3. [SYN_RCVD -> ESTABLISHED] サーバーがクライアントから確認メッセージを受信すると、ESTABLISHED 状態になり、データの読み取りと書き込みが可能になります。
  4. [ESTABLISHED -> CLOSE_WAIT] クライアントが積極的に接続を閉じる (close を呼び出す) と、サーバーは終了メッセージ セグメントを受信し、サーバーは確認メッセージ セグメントを返し、CLOSE_WAIT に入ります。
  5. [CLOSE_WAIT -> LAST_ACK] CLOSE_WAIT を入力すると、サーバーが接続を閉じる準備ができたことを意味します (以前のデータを処理する必要があります)。サーバーが実際に close を呼び出して接続を閉じると、クライアントに FIN が送信されます。このとき、サーバーは LAST_ACK 状態に入り、最後の ACK が到着するのを待ちます (この ACK は、クライアントによる FIN 受信の確認です)。
  6. [LAST_ACK -> CLOSED] サーバーは FIN の ACK を受信し、接続を完全に閉じました。

クライアントの状態遷移:

  1. [CLOSED -> SYN_SENT] クライアントは connect を呼び出し、同期メッセージ セグメントを送信します。
  2. [SYN_SENT -> ESTABLISHED] 接続呼び出しが成功すると、ESTABLISHED 状態になり、データの読み取りと書き込みが開始されます。
  3. [ESTABLISHED -> FIN_WAIT_1] クライアントがアクティブに close を呼び出すと、終了メッセージ セグメントがサーバーに送信され、FIN_WAIT_1 に入ります。
  4. [FIN_WAIT_1 -> FIN_WAIT_2] クライアントはサーバーからの終了セグメントの確認を受信すると、FIN_WAIT_2 に入り、サーバーの終了セグメントの待機を開始します。
  5. [FIN_WAIT_2 -> TIME_WAIT] クライアントは、サーバーから送信された終了セグメントを受信し、TIME_WAIT に入り、LAST_ACK を発行します。
  6. [TIME_WAIT -> CLOSED] クライアントは、CLOSED 状態に入る前に 2MSL (最大セグメント寿命、最大パケット生存時間) 待機する必要があります。

つながりとは何ですか?

OS 内には必然的に多数の接続が同時に存在し、オペレーティング システムはこれらの接続を管理する必要があるため、このようなリンク データ構造と管理構造が存在します。 struct link{......}; にはメモリと CPU リソースが必要です。

接続を確立するときに 3 ウェイ ハンドシェイクが 2 回、4 回、5 回行われるのはなぜですか? これは問題ありませんか?

2 回のハンドシェイクで接続が確立された場合、クライアントは SYN を開始するだけで済み、サーバーが接続を確立することは難しくないことに注意してください。これにより、サーバーは非常に低コストで接続を確立できます。サーバー接続。接続の作成には CPU とメモリ リソースも必要です。マシンがサーバーに大量の SYN を送信すると、サーバーはすぐに過負荷になります。これは SYN フラッド攻撃です。偶数番目の接続にはこのような問題がいくつかあり、奇数番目の接続では、接続を確立するための最小コストは 3 ウェイ ハンドシェイクです。

2 回目のハンドシェイクは便乗応答です。

 切断時のいくつかの状態:

1.TIME_WAIT

この図から、積極的に切断する側が TIME_WAIT 状態になることがわかります。

TCP プロトコルでは、接続をアクティブに閉じる側が TIME_WAIT 状態にあり、CLOSED 状態に戻る前に 2 MSL (最大セグメント有効期間) 時間待機する必要があると規定しています。

MSL は RFC1122 で 2 分と指定されていますが、各オペレーティング システムの実装は異なります。Centos7 のデフォルト構成値は 60 秒です。
cat /proc/sys を使用できます。 /net /ipv4/tcp_fin_timeout msl の値を表示します。

このカーネル ファイルは変更できます。root ユーザーである必要がありますが、変更は効果がありません。

テスト:

サービスを開始し、Ctrl C を押して切断し、再度開始します。

ポート 8081 の現在の接続を確認したところ、サービスがまだポート 8081 を使用していることがわかりました。 、これがバインディングが失敗した理由です。

TIME_WAIT の時間が 2MSL なのはなぜですか?

  1. MSL は TCP メッセージの最大生存時間であるため、TIME_WAIT が 2MSL 持続する場合
  2. これにより、両方の送信方向で未受信または遅れているすべてのメッセージ セグメントが確実に消失します (そうでないと、サーバーはすぐに再起動し、前のプロセスから遅れたデータを受信する可能性がありますが、このデータは間違っている可能性があります)。 ;
  3. 同時に、最後のメッセージが確実に到着することも理論的には保証されています (最後の ACK が失われたと仮定すると、サーバーは FIN を再送信します。この時点で、クライアントのプロセスはなくなっていますが、TCP 接続はまだ存在しています。 LAST_ACK は引き続き再送信できます)。

TIME_WAIT 状態によるバインドの失敗を解決する方法:

サーバーの TCP 接続が完全に切断されるまで、再リッスンは許可されません。これは場合によっては不合理な場合があります。

  1. サーバーは非常に多くのクライアント接続を処理する必要があります (各接続の存続期間は短いかもしれませんが、毎秒大量のクライアント要求が発生します)。
  2. このとき、サーバーが積極的に接続を閉じる場合 (たとえば、一部のクライアントが非アクティブであり、サーバーによって積極的にクリーンアップする必要がある場合)、大量の TIME_WAIT 接続が生成されます。
  3. リクエスト量が多いため、多数の TIME_WAIT 接続が存在する可能性があり、各接続は通信の 5 つ組 (送信元 IP、送信元ポート、宛先 IP、宛先ポート、プロトコル) を占有します。サーバーの IP とポートは次のとおりです。プロトコルは次のとおりです。新しいクライアント接続の IP およびポート番号が、TIME_WAIT によって占有されているリンクと同じである場合、問題が発生します。

setsockopt() を使用してソケット記述子の SO_REUSEADDR オプションを 1 に設定します。これは、同じポート番号を持つが異なる IP アドレスを持つ複数のソケット記述子を作成できることを意味します。

void Bind()
    {
        // 设置无需等待TIME_WAIT状态
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        struct sockaddr_in host;
        host.sin_family = AF_INET;
        host.sin_port = htons(_port);
        host.sin_addr.s_addr = INADDR_ANY; // #define INADDR_ANY 0x00000000
        socklen_t hostlen = sizeof(host);
        int n = bind(_listensock, (struct sockaddr *)&host, hostlen);
        if (n == -1)
        {
            Logmessage(Fatal, "bind err ,error code %d,%s", errno, strerror(errno));
            exit(BING_ERR);
        }
    }

2.CLOSE_WAIT状態

もう一方のホストが最初に自身の fd を閉じて FIN リクエストを開始すると、相手側も FIN を受信した後、CLOSE_WAIT 状態に入り、通信ファイル記述子を閉じてから ACK を送信して LAST_ACK 状態に入ります。ただし、接続されたファイル記述子のクローズはプログラマによって制御されます。ファイル記述子をクローズしない場合はどうなるでしょうか。

クライアントを切断します。

この時点でサーバーは CLOSE_WAIT 状態に入っており、4 波のウェーブ フローチャートと組み合わせると、4 波のウェーブが正しく終了していないと考えられます。

 概要: サーバー上の多数の CLOSE_WAIT 状態の原因は、サーバーがソケットを正しく閉じなかったため、4 つのウェーブが正しく完了しなかったことです。これはバグです。対応するクローズを追加するだけで問題を解決できます。

13. listen の 2 番目のパラメータ

const static int backlog = 1;
int n = listen(_listensock, backlog);

サーバーがリッスンのみを行い、受け入れはしないようにします。接続は受け入れますが、上位層へのリンクは取得しません。

Telnet を使用して接続します。

この時点では、2 つのクライアントが同時にサーバーへの接続を開始し、netstat を使用してサーバーのステータスがチェックされます。すべて正常です。
ただし、 3 番目のクライアントが開始されると、サーバーが 3 番目の接続を受け入れることができないことがわかり、ステータスに問題があります。

クライアントのステータスは正常ですが、サーバー側では ESTABLISHED ステータスではなく SYN_RECV ステータスになります
これは、Linux カーネル プロトコル スタックが TCP 接続管理に 2 つのキューを使用するためです。

  1. 1. セミリンクキュー (SYN_SENT および SYN_RECV ステータスのリクエストを保存するために使用)
  2. 2. フル接続キュー (accpetd ​​キュー) (確立された状態にあるリクエストを保存するために使用されますが、アプリケーション層は accept を呼び出しません)

完全な接続キューの長さは、listen の 2 番目のパラメータの影響を受けます。
完全な接続キューがいっぱいになると、現在の接続ステータスは引き続き確立された状態になることができません。 . .
このキューの長さは、上記の実験から知ることができ、これは listen + 1 の 2 番目のパラメータです。

注: listen +1 の 2 番目のパラメータは、サーバーによって処理されるリンクの最大数ではなく、一時的に保存され、アプリケーション層には送信されないリンクの最大数です。

14.TCP異常事態 

  1. プロセスの終了: プロセスの終了によりファイル記述子は解放され、FIN は引き続き送信できますが、通常のシャットダウンと変わりはありません。
  2. マシンの再起動: プロセスの終了と同じ状況。
  3. マシンの電源がオフになっている/ネットワーク ケーブルが切断されている: 受信側は、接続がまだ存在していると認識します。受信側が書き込み操作を実行し、接続が存在しないことが分かると、リセットされます。書き込み操作では、TCP 自体にキープアライブ機能が組み込まれており、タイマーによって定期的に相手がいるかどうかを確認し、相手がいない場合は接続も解放されます。
  4. また、アプリケーション層のプロトコルにもこのような検知機構が備わっており、例えばHTTPの長時間接続では定期的に相手の状態を検知し、例えばQQが切断された後も定期的に再接続を試みます。

15.TCPの概要

TCP はなぜこれほど複雑なのですか?パフォーマンスを可能な限り向上させながら信頼性を確保する必要があるためです。
信頼性:

  1. チェックサム
  2. シリアルナンバー(順次入荷)
  3. 応答を確認する
  4. タイムアウトして再送信する
  5. 接続管理
  6. フロー制御
  7. 輻輳制御

性能を上げる:

  1. スライドウィンドウ
  2. 高速再送信
  3. 遅延応答
  4. 便乗返信

その他:
タイマー (タイムアウト再送信タイマー、キープアライブ タイマー、TIME_WAIT タイマーなど)

16. TCPアプリケーション層プロトコルに基づく 

  • HTTP
  • HTTPS
  • SSH
  • Telnet
  • FTP
  • SMTP

もちろん、独自の TCP プログラムを作成する場合には、カスタム アプリケーション層プロトコルも含まれます。

4.TCP/UDPの比較

TCP は信頼性の高い接続であると述べましたが、TCP は UDP よりも確実に優れているのでしょうか? TCP と UDP の長所と短所を単純かつ絶対的に比較することはできません。

  1. TCP は信頼性の高い送信のために使用され、ファイル転送や重要なステータスの更新などのシナリオで使用されます。
  2. UDPは、初期QQや映像伝送など、高速伝送やリアルタイム性が要求される通信分野で使用されています。また、UDPは放送にも使用できます。

結局のところ、TCP と UDP はどちらもプログラマのためのツールであり、いつ使用するか、どのように使用するかは、特定の需要シナリオによって異なります。

UDP を使用して信頼性の高い送信を実現する (典型的な面接の質問)

TCP の信頼性メカニズムを参照し、同様のロジックをアプリケーション層に実装します。
例:

  1. データの順序を保証するためにシリアル番号を導入します。
  2. 確認応答を導入して、ピアがデータを受信したことを確認します。
  3. タイムアウト再送信の導入により、一定時間が経過しても応答がない場合、データが再送信されます。
  4. ……

おすすめ

転載: blog.csdn.net/qq_63943454/article/details/134531451