TCPパケットが部分的に確認された場合、再送信メカニズムはどのように反応しますか?


12

TCPクライアントがシーケンス番号10000から20000のパケットをTCPサーバーに送信する場合。tcpはseq_ack 20001でACKで応答します。

クライアントからTCPパケットをインターセプトし、パケットを2つのTCPセグメントに分割すると、1つは10000から15000までのseq、もう1つは15001から20000のseqを持ちます。そして、これら2つのTCPセグメントはTCPサーバーに送信されます。2番目のセグメントがパスで失われたと仮定します。TCPサーバーはseq_ack 15001でACKに応答します。

TCPクライアントはseq 10000から20000でインテグラルパケットを送信しますが、クライアントの観点からは15001でACKを取得するため、これは奇妙です。どのように反応しますか?理論的には、クライアントはバイトをseq 15001から20000に再送信する必要があります。つまり、クライアントはseq 15001から新しいパケットを送信します。

TCP送信バッファでは、tcpセグメントが送信されたときに、ACKまでセグメントがそこにとどまると思います。ACKが来ると、セグメントのこれらのバイトはバッファからクリアされます。送信バッファーにポインターがあり、ACKが来ると、ポインターはack_seqが対応する場所を指します。ack_seqの下にあるバイトはクリアされます。このようにして、セグメント全体を再送信する必要はありませんか?

回答:


8

これは選択的確認応答と呼ばれ、RFC 2018で定義されているTCP仕様にすでに含まれています。これにより、クライアントは実際にバイト15001から20000だけを再送信できます(言うまでもなく、それらは異なるパケット/セグメントにあるためそれらを分割した場合)

RFC 2018から:

SACKオプションを含むACKを受信する場合、データ送信者は、将来の参照のために選択的確認応答を記録する必要があります。データ送信者には、送信されたがまだ確認されていないセグメントをシーケンス番号順に含む再送信キューがあると想定されます。

サポートSACKは、TCP仕様で必要ありません。クライアントまたはサーバーのいずれかが選択的確認応答をサポートしていなかった場合、実際には10000〜20000のすべてのバイトを再送信する必要があります。

TCPスタックの実装では、理論と同じですか?

特にインターネットのようなネットワークでは、パフォーマンス、効率、およびレイテンシーの向上が重要であるため、通常SACK サポートされています。

ただし、実際には、述べたように手動でパケットを操作した場合でも、これらの仮定は当てはまります。あたりとしてRFC 793、最低でも、全体のデータウィンドウは再送信する必要がありますが、レシーバはない受け取ったデータは、少なくともであることを知っている有効。実装の詳細については、セクション3.3- RFC 793のシーケンス番号

選択的確認のサポートの有無にかかわらず、プロセス全体の概要については、この記事を参照してください(非常に役立つ図が含まれています)。


TCPはストリームベースのバイト指向のプロトコルであるため、少し奇妙です。セグメント全体を再送信する必要があるのはなぜですか?SAKCを使用しないTCPはセグメント指向のストリームプロトコルであるように思えますが、Sackを使用するTCPは実際のバイト指向です。RFCはこれについて具体的に詳述していないと思います。
ミステリー

TCPスタックが送信バッファーを管理する方法は、更新された質問で書いたものと同じですか。
ミステリー

@misteryes この記事では、プロセスの概要を説明します(素晴らしい図もいくつかあります!)。
ブレークスルー

あなたが推奨したリンクでは、著者はまだ実際のバイト指向の方法ではなく、セグメント指向の方法で問題を議論しているようです。そうじゃない?
ミステリー

1
この質問を投稿する前にSACKを知っていました。最初は、SACKがこの質問と関係があるとは思いません。私の意見では、TCPがバイト指向ではなくセグメント指向であれば、SACKも同じでなければなりません。SACK対応とSACK無効の違いは、SACKでは、TCPがack_seqのシーケンスホールを許可することです。しかし、シーケンスホールはセグメントに対応すると考えました。あなたの言い伝えによれば、穴はセグメントの半分/部分にすることができます。
ミステリー

3

セグメントサイズは、接続の存続期間にわたって変化する可能性があります(実際に変化します)。幸いなことに、TCPは個々のパケットが以前に送信されたセグメントサイズを記録する必要がありません。したがって、次のことを行います。

  1. ACKが到着するたびに、それに応じて最初の未確認バイトへのポインタを進め、不要になったバッファを破棄します。
  2. 再送信の必要性が生じた場合(高速再送信またはタイムアウト。最初のACKの受信直後ではありません!)、最初の未確認バイトへのポインタから始まる現在有効なセグメントサイズで再送信されます。

両方の操作は、これらのバイトが最初に送信されたセグメントサイズとは無関係に行われます。したがって、理論はほとんどの実装と一致するはずです。

説明するための背景を説明しましょう。

TCPはバイトまたはセグメントを使用しますか?アプリケーションに対して、TCPはバイトストリームインターフェイスを公開します。また、すべてのヘッダーフィールドと内部変数はバイト単位です。ただし、情報を送信するために、TCPはバイトを1つずつ送信するとかなり無駄になるため、セグメントに分割します:-)。どこでもバイトカウンタを使用することには、接続の存続期間にわたってセグメントサイズを一定に保つ必要がないという利点があります。

  • たとえば、再送信時にSACKをピギーバックするオプションが導入されています(実際の実装では、これが発生することはめったにありません)
  • パスMTUが変更されます。たとえば、パスに沿った1つのリンクがより低いMTUに変更されるか、ボトルネックMTUリンクが発生します。これは、トンネルが確立された場合(VPN、PPPoE)、またはルーティングプロトコルが異なるMTUリンクを選択した場合に発生します。これは、Do n't Fragmentが設定されたIPv4で発生します(最新のTCPのほとんどに当てはまります)。常にTCPv6で)。

ところで、SACKはここでの答えではありません。受信者は(通常)バイトストリームの穴を認識する場合(つまり、パケットが失われたが後続のパケットが到着した場合)のみSACKを使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.