TCP 再送信とタイムアウト メカニズム: ネットワーク パフォーマンスの秘密を解き明かす

1. TCP 再送信 (TCP 再送信)

1.1 再送信の原則とメカニズム

TCP (伝送制御プロトコル) は、接続指向の信頼性の高いトランスポート層プロトコルです。信頼性の高いデータ転送を保証するために、TCP はデータ パケット再転送の戦略を採用して、ネットワークでの転送プロセス中に発生する可能性があるパケット損失、エラー パケット、および順不同などの問題に対処します。以下に、TCP 再送の原理とメカニズムを詳しく紹介します。

(1) 確認メカニズム

TCP 通信では、受信側はデータパケットを受信した後、受信確認メッセージ (ACK) を返し、データが正常に受信されたことを送信側に通知します。送信者は、データ パケットを送信した後、受信者が確認メッセージを返すのを待ちます。指定された時間内に確認メッセージが受信されない場合、送信者はデータ パケットが失われたと見なし、再送信メカニズムをトリガーします。

(2) 引き窓(Sliding Window)

TCP はスライディング ウィンドウ メカニズムを使用して、フロー制御と輻輳制御を実装します。送信側のウィンドウ サイズによって、送信できるパケット数が決まります。受信者が確認メッセージを返すと、送信者のウィンドウがスライドし、新しいデータ パケットを送信し続けることができます。未確認のデータ パケットがある場合、確認パケットが受信されるか、再送信メカニズムがトリガーされるまで、ウィンドウはスライドしなくなります。

(3) シーケンス番号

TCP は、各データ パケットに独立したシーケンス番号を割り当てて、データ パケットの順序どおりの転送を保証します。データ パケットを受信した後、受信側はシーケンス番号に従ってデータ パケットを並べ替え、最終的な送信結果の正確性を保証します。受信側が不連続なシーケンス番号を持つデータ パケットを検出した場合、途中でパケット損失が発生したことを意味し、送信側に再送信を要求します。

(4) 再送タイマー

TCP 送信側は、再送信タイマーを維持して、送信されたが確認メッセージを受信して​​いない各データ パケットを監視します。タイマーが切れると、送信者は対応するデータ パケットが失われたと見なし、再送信メカニズムをトリガーします。さまざまなネットワーク環境に対処するために、TCP は適応型タイマー調整戦略も採用して、タイムアウトのしきい値を動的に調整します。

概要: TCP 再送信の原理とメカニズムの核心は、確認メカニズム、スライディング ウィンドウ、シーケンス番号、再送信タイマーなどのさまざまな手段を通じて、データの信頼できる送信を保証することです。送信者が受信者から確認メッセージを受信しない場合、データ送信の完全性と正確性を確保するために、再送信メカニズムを通じてデータ パケットを再送信します。

1.2 再送信トリガーの条件

TCP 再送信メカニズムは、信頼性の高いデータ送信を確保するために設計された重要な戦略です。TCP 再送信をよりよく理解するには、再送信をトリガーする特定の条件を理解する必要があります。以下は、TCP 再送信につながる主な状況です。

(1) タイムアウト再送

送信者がデータ パケットを送信すると、再送信タイマーが開始され、受信者が確認メッセージを返すのを待ちます。タイマーが期限切れになる前に確認メッセージが受信されない場合、送信者はデータ パケットが失われたと見なし、タイムアウトの再送信をトリガーします。オーバータイム再送信の時間しきい値は、ネットワークの状態に応じて動的に調整されます。

(2) 高速再送信 (Fast Retransmission)

高速再送信は、TCP パフォーマンスを向上させるための再送信戦略です。受信者が同じシーケンス番号を持つ 3 つの肯定応答メッセージ (Duplicate ACK) を連続して受信すると、送信者は対応するデータ パケットが失われたと見なします。失われたデータ パケットをできるだけ早く再送信するために、送信者は再送信タイマーが期限切れになるのを待たずにすぐに再送信します。このアプローチにより、パケット損失による遅延が減少します。

(3) Selective Acknowledgement Retransmission (選択的確認応答再送信)

Selective Acknowledgement (SACK) は、受信側が送信側にどのパケットが正常に受信され、どのパケットを再送信する必要があるかを通知できるようにする TCP 拡張機能です。SACK を使用すると、TCP のパフォーマンスを向上させることができます。これは、送信者がどのデータ パケットをより正確に再送信する必要があるかを知ることができ、不要な完全な再送信を回避できるためです。

(4) 輻輳による再送

送信者がネットワークの輻輳を検出すると、再送信がトリガーされる場合があります。これは、輻輳状況では、パケット損失の可能性が高くなり、送信者がパケットを再送信する必要が生じるためです。輻輳制御アルゴリズム (TCP Tahoe、Reno、NewReno など) は、送信者のウィンドウ サイズを動的に調整し、輻輳が発生したときに送信レートを制限することで、輻輳の程度を軽減します。

要約すると、TCP 再送のトリガー条件には、タイムアウト再送、高速再送、選択的確認応答再送、輻輳による再送が含まれます。これらのトリガー条件を理解することは、TCP 再送信メカニズムをよりよく理解するのに役立ち、ネットワーク パフォーマンスを最適化するためのアイデアを提供します。

1.3 再送信戦略の最適化

TCP 送信パフォーマンスを向上させ、再送信による遅延を減らすために、次の側面から TCP 再送信戦略を最適化できます。

(1) 再送タイマーの最適化(再送タイマーの最適化)

再送信タイマーのタイムアウトしきい値を調整して、現在のネットワーク環境により適したものにします。たとえば、再送タイムアウト (RTO) 値を往復時間 (RTT) および往復時間変動 (RTTVAR) と組み合わせて動的に調整するために、適応調整戦略が採用されています。不必要な再送信の遅延を減らし、TCP 送信のパフォーマンスを向上させます。

(2) Fast Retransmission and Fast Recovery を有効にする (Fast Retransmission and Fast Recovery を有効にする)

高速再送信と高速回復メカニズムにより、パケット損失による遅延を減らすことができます。送信者は、3 回連続して確認応答メッセージを受信すると、再送信タイマーの期限が切れるのを待たずに、すぐに再送信します。高速回復アルゴリズムにより、送信者は高速再送信後に新しいデータ パケットを送信し続けることができるため、グローバル同期が回避され、ネットワーク スループットが向上します。

(3) 選択的承認を使用する

Selective Acknowledgement (SACK) メカニズムを有効にして、受信者が受信したパケットと再送信が必要なパケットをより正確に送信者に伝えることができるようにします。このようにして、送信者は不必要な完全な再送信を回避し、失われたデータ パケットのみを再送信して、送信効率を向上させることができます。

(4) 輻輳制御アルゴリズムの最適化

現在のネットワーク環境により適した輻輳制御アルゴリズム (CUBIC、BBR など) を選択して、輻輳によって引き起こされる再送信を減らします。これらのアルゴリズムは、送信レートをより効果的に制御し、輻輳によるパケット損失や遅延の増加を回避できます。

(5) 前方誤り訂正技術 (Forward Error Correction、FEC) の使用

前方誤り訂正技術は、再送回数を増やすことなく、データ伝送の信頼性を向上させることができます。冗長情報をデータ パケットに追加することで、部分的に破損したデータ パケットまたは失われたデータ パケットを受信した場合でも、受信側は元のデータの回復を試みることができます。これにより、パケット損失率の高いネットワーク環境においても、データ伝送の完全性と正確性を保証することができます。

再送信戦略を最適化することで、TCP の信頼性に影響を与えることなく、ネットワーク パフォーマンスとユーザー エクスペリエンスを向上させることができます。実際のネットワーク環境とアプリケーションの要件に応じて、特定の最適化方法を選択して調整する必要があります。

2、TCP タイムアウト (TCP タイムアウト)

2.1 タイムアウト検出の原則

TCP タイムアウトとは、データ パケットを送信した後、受信者が確認メッセージを返すのを待っている間に、送信者が所定の時間内に確認メッセージを受信できないことを意味します。タイムアウト検出は、信頼性の高いデータ転送を確保するための TCP プロトコルの重要なメカニズムの 1 つです。このセクションでは、TCP タイムアウト検出の原理について詳しく説明します。

(1) 往復時間 (Round-Trip Time、RTT)

往復時間とは、データ パケットが送信者から送信されてから、受信者が確認メッセージを返すまでの時間を指します。送信者は、ネットワークの状態に応じて適切なタイムアウトしきい値を設定するために、RTT を見積もる必要があります。通常、RTT の見積もりは、パケットが送信されて確認されるまでにかかる時間に基づいています。

(2) 再送タイムアウト (RTO)

RTO は、受信者が受信確認メッセージを返すまで送信者が待機する最大時間です。送信者が RTO 内に確認メッセージを受信しない場合、再送信メカニズムがトリガーされます。さまざまなネットワーク環境に適応するために、送信者は RTT に従って RTO 値を動的に調整する必要があります。

(3) 加重平均往復時間 (Smoothed Round-Trip Time、SRTT) および往復時間変動 (Round-Trip Time Variation、RTTVAR)

TCP 送信者は通常、加重平均ラウンド トリップ時間 (SRTT) とラウンド トリップ時間変動 (RTTVAR) を使用して、現在のネットワークの RTT を推定します。SRTT は過去の RTT の加重平均であり、RTTVAR は過去の RTT の変化の大きさです。これら 2 つの値の組み合わせは、ネットワークの状態をより正確に反映し、送信者が適切な RTO を設定するのに役立ちます。

(4) Karn/Partridge アルゴリズム

Karn/Partridge アルゴリズムは、再送信されたパケットの RTT 推定問題を処理する方法です。元の RTT を直接使用すると、パケットが再送信されたときに RTO を誤って見積もる可能性があります。Karn/Partridge アルゴリズムは、再送信プロセス中に SRTT と RTTVAR の値を更新しないことで、誤推定の問題を回避し、タイムアウト検出の精度を向上させます。

要約すると、TCP タイムアウト検出の原則には、主に RTT 推定、RTO 設定、SRTT および RTTVAR 計算、Karn/Partridge アルゴリズムが含まれます。これらの原則を理解することは、TCP タイムアウト メカニズムをよりよく理解し、ネットワーク パフォーマンスを最適化するための基礎を提供するのに役立ちます。

2.2 タイムアウト検出の最適化

ネットワーク パフォーマンスに対する TCP タイムアウトの影響を軽減するために、次の側面からタイムアウト検出メカニズムを最適化できます。

(1) 往復時間の正確な見積もり (Accurate RTT Estimation)

ラウンドトリップ時間の見積もりの​​精度を向上させると、適切な再送信タイムアウトのしきい値を設定するのに役立ちます。送信者は、過去の RTT に重みを付けて RTT 変動範囲を計算することにより、現在のネットワークの RTT を推定できます。より正確な RTT 推定により、早すぎたり遅すぎたりする再送信のトリガーを回避し、ネットワーク パフォーマンスを向上させることができます。

(2) 再送タイムアウトを動的に調整する (Dynamic RTO Adjustment)

ネットワークの状態に応じて、再送信タイムアウトのしきい値を動的に調整します。送信者は、SRTT と RTTVAR の値を組み合わせて、適切な RTO を計算できます。RTO を動的に調整することで、送信者はネットワークの変化により敏感に反応し、不要な再送信の遅延を減らすことができます。

(3) より正確なタイムアウト検出アルゴリズムを使用する (より正確なタイムアウト検出アルゴリズムの採用)

Karn/Partridge アルゴリズムなど、より正確なタイムアウト検出アルゴリズムを選択して、タイムアウト検出の精度を向上させます。Karn/Partridge アルゴリズムは、再送信パケットを処理する際の推定ミスの問題を回避できるため、タイムアウト検出の精度とネットワーク パフォーマンスが向上します。

(4) より高度なトランスポート プロトコルを使用してみる (高度なトランスポート プロトコルの探索)

QUIC など、より優れたタイムアウト検出メカニズムを備えた高度なトランスポート プロトコルの使用を検討してください。QUIC プロトコルは、単一の暗号化された接続を使用して、ハンドシェイクとタイムアウトの遅延を減らします。さらに、QUIC は、再送信タイムアウトに依存せずにパケット再送信を実行できる新しいパケット損失回復メカニズムも採用しているため、遅延が減少します。

タイムアウト検出メカニズムを最適化することで、TCP の信頼性を維持しながら、タイムアウトがネットワーク パフォーマンスに与える影響を減らすことができます。実際のネットワーク環境とアプリケーションの要件に応じて、特定の最適化方法を選択して調整する必要があります。

2.3 ネットワーク パフォーマンスに対するタイムアウトの影響

データ転送の信頼性を確保する一方で、TCP タイムアウト メカニズムはネットワーク パフォーマンスに一定の影響を与えます。このセクションでは、タイムアウトがネットワーク パフォーマンスに与える影響について説明します。

(1) レイテンシーの増加

TCP タイムアウトが発生すると、送信者はデータ パケットを再送信する前に、再送信タイマーが期限切れになるまで待機する必要があります。これにより、データ転送の全体的な時間が増加し、ネットワークの遅延が増加します。パケット損失率が高いネットワーク環境では、遅延の問題がより深刻になる可能性があります。

(2) スループットの低下

TCP タイムアウトにより、送信者の送信ウィンドウが減少し、送信速度が制限される可能性があります。送信者は確認メッセージを待つ必要があるため、タイムアウトが長いと、送信者が長時間待機し、ネットワークのスループットがさらに低下する可能性があります。

(3) 影響を受ける輻輳制御

TCP タイムアウトは、輻輳制御と密接に関連しています。タイムアウトが発生すると、送信者は通常、ネットワークが輻輳していると考え、輻輳制御アルゴリズムをトリガーします。これにより、送信者は送信レートを下げてネットワークの輻輳を緩和します。ただし、場合によっては、タイムアウトが早すぎたり遅すぎたりすると、輻輳制御アルゴリズムの判断ミスにつながり、ネットワーク パフォーマンスに影響を与える可能性があります。

(4) グローバル同期 (Global Synchronization)

TCP タイムアウトにより、グローバル同期現象が発生する場合があります。複数の TCP 接続で同時にタイムアウトと再送信が発生すると、それらの送信ウィンドウ サイズと送信速度が同期して減少し、ネットワーク スループットが変動する可能性があります。グローバル同期の現象は、ネットワーク リソースの浪費とパフォーマンスの低下につながる可能性があります。

タイムアウトがネットワーク パフォーマンスに与える影響を理解することで、TCP プロトコルを最適化し、ネットワーク パフォーマンスを向上させることができます。実際のアプリケーションでは、タイムアウト検出戦略を調整し、輻輳制御アルゴリズムを最適化し、高度なトランスポート プロトコルの使用を試みることで、ネットワーク パフォーマンスに対するタイムアウトの影響を減らすことができます。

2.4 Linux C++ TCP タイムアウト検出コード

例 1: Setsockopt はタイムアウト期間を設定します

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdexcept>

int main() {
    
    
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
    
    
        perror("socket creation failed");
        return -1;
    }

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
    
    
        perror("connect failed");
        close(sockfd);
        return -1;
    }

    // 设置接收超时
    struct timeval timeout;
    timeout.tv_sec = 3;  // 超时时间设置为3秒
    timeout.tv_usec = 0;
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
    
    
        perror("setsockopt failed");
        close(sockfd);
        return -1;
    }

    char buffer[1024];
    ssize_t recv_len = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
    if (recv_len < 0) {
    
    
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
    
    
            std::cout << "TCP receive timeout" << std::endl;
        } else {
    
    
            perror("recv failed");
        }
        close(sockfd);
        return -1;
    }

    buffer[recv_len] = '\0';
    std::cout << "Received message: " << buffer << std::endl;

    close(sockfd);
    return 0;
}

例 2: select でタイムアウト期間を設定する

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>

int main() {
    
    
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
    
    
        perror("socket creation failed");
        return -1;
    }

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(12345);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
    
    
        perror("connect failed");
        close(sockfd);
        return -1;
    }

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(sockfd, &read_fds);

    // 设置超时时间为3秒
    struct timeval timeout;
    timeout.tv_sec = 3;
    timeout.tv_usec = 0;

    int max_fd = sockfd + 1;
    int result = select(max_fd, &read_fds, nullptr, nullptr, &timeout);

    if (result < 0) {
    
    
        perror("select failed");
        close(sockfd);
        return -1;
    } else if (result == 0) {
    
    
        std::cout << "TCP receive timeout" << std::endl;
        close(sockfd);
        return -1;
    } else {
    
    
        if (FD_ISSET(sockfd, &read_fds)) {
    
    
            char buffer[1024];
            ssize_t recv_len = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
            if (recv_len < 0) {
    
    
                perror("recv failed");
                close(sockfd);
                return -1;
            }
            buffer[recv_len] = '\0';
            std::cout << "Received message: " << buffer << std::endl;
        }
    }

    close(sockfd);
    return 0;
}

3. TCP 輻輳制御と再送信

3.1 再送とタイムアウトの実際の適用シナリオ

TCP 再送信とタイムアウト メカニズムは、さまざまな種類のネットワーク アプリケーション シナリオで重要な役割を果たします。このセクションでは、いくつかの実用的なアプリケーション シナリオを紹介し、これらのシナリオにおける再送信とタイムアウトの重要性を示します。

(1) ファイル転送

FTP や HTTP ファイルのダウンロードなどのファイル転送アプリケーションでは、データの整合性と正確性が最も重要です。TCP 再送信およびタイムアウト メカニズムにより、送信プロセス中のデータ パケットの信頼性が保証され、ネットワーク環境が不安定な場合でも、ファイルの完全でエラーのない送信が保証されます。

(2) リアルタイム通信

VoIP やビデオ会議などのリアルタイム通信アプリケーションには、遅延とデータの整合性に対する高い要件があります。TCP 再送信およびタイムアウト メカニズムにより、データ送信の信頼性をある程度確保できます。ただし、リアルタイム通信は遅延に非常に敏感なため、過度の再送やタイムアウトにより通信品質が低下する可能性があります。したがって、リアルタイム通信シナリオでは、信頼性と遅延を比較検討する必要があり、UDP などの他のプロトコルを使用してリアルタイム データを送信することができます。

(3) オンラインゲーム

通常、オンライン ゲームには、レイテンシとデータの整合性に対する高い要件があります。プレーヤーの操作やステータスの更新など、ゲーム内の重要なデータは、TCP 再送信とタイムアウト メカニズムによって確実に送信される必要があります。同時に、遅延を減らすために、ゲーム開発者は再送信戦略とタイムアウト検出を最適化して、ゲームのパフォーマンスとユーザー エクスペリエンスを向上させる必要があります。

(4) ストリーミングメディア

オンライン ビデオやオーディオ再生などのストリーミング メディア転送アプリケーションでは、通常、データ転送の信頼性を確保しながら、待ち時間とバッファリングを削減する必要があります。TCP の再送信とタイムアウトのメカニズムにより、ストリーミング メディア データの正しい送信が保証されますが、再送信とタイムアウトが多すぎると、再生がフリーズする可能性があります。これらのシナリオでは、アダプティブ ストリーミング テクノロジを使用し、TCP や UDP などの他のプロトコルと組み合わせて、効率的なデータ転送を実現できます。

要約すると、TCP 再送信とタイムアウト メカニズムは、さまざまな種類のネットワーク アプリケーション シナリオで重要な役割を果たします。これらのアプリケーション シナリオと、再送信とタイムアウトの要件を理解することは、ネットワーク パフォーマンスの最適化とユーザー エクスペリエンスの向上に役立ちます。

3.2 再送とタイムアウトの課題と改善方向

TCP 再送信とタイムアウト メカニズムは、データ送信の信頼性を確保する上で重要な役割を果たしますが、実際のアプリケーションではまだいくつかの課題に直面しています。このセクションでは、これらの課題と改善の方向性について説明します。

(1) パケットロス原因の正確な特定

TCP 送信中のパケット損失は、ネットワークの輻輳またはリンク エラーなどの他の理由が原因である可能性があります。送信者は、適切な再送信戦略を採用するために、パケット損失の原因を正確に特定する必要があります。しかし、現在の TCP 再送とタイムアウトのメカニズムでは、パケット損失の原因を正確に特定することが難しく、誤判断やパフォーマンスの低下につながる可能性があります。今後の改善の方向性には、再送信戦略のインテリジェンスを向上させるための、より正確なパケット損失識別技術に関する研究が含まれます。

(2) 異なるアプリケーション シナリオ要件の区別

ネットワーク アプリケーションの種類によって、再送信とタイムアウトの要件が異なります。たとえば、リアルタイム コミュニケーションとオンライン ゲームは遅延の影響を受けやすいのに対し、ファイル転送とストリーミングはデータの整合性に関心があります。今後の TCP プロトコルの改善では、さまざまなアプリケーション シナリオに対して、より柔軟な再送信およびタイムアウト戦略を提供する必要があります。

(3) ネットワークの輻輳制御効果の向上(ネットワーク輻輳制御の強化)

TCP の再送信とタイムアウトのメカニズムは、ネットワークの輻輳制御と密接に関連しています。現在の輻輳制御アルゴリズムは、場合によっては判断を誤る可能性があり、ネットワークのパフォーマンスに影響を与えます。今後の改善の方向性には、ネットワーク パフォーマンスに対する再送信とタイムアウトの影響を軽減するための、より正確で効率的な輻輳制御技術に関する研究が含まれます。

(4) 新しいトランスポート プロトコルの探索

TCP プロトコルの限界を考慮して、研究者とエンジニアは、ネットワーク パフォーマンスを向上させるための新しいトランスポート プロトコルを開発しています。たとえば、従来の TCP プロトコルに基づいて、QUIC プロトコルは、暗号化された接続、より効果的なパケット損失回復メカニズムなど、一連の改善策を導入しています。新しいトランスポート プロトコルを調査することで、TCP 再送信とタイムアウト メカニズムが直面する課題を解決し、ネットワーク パフォーマンスを向上させることができます。

これらの課題に対処し、対応する改善策を講じることで、TCP 再送信とタイムアウト メカニズムのパフォーマンスをさらに改善し、さまざまなネットワーク アプリケーション シナリオのニーズをより適切に満たすことができます。

3.3 再送とタイムアウトの評価と監視方法

TCP 再送信とタイムアウト メカニズムを最適化するには、それを評価して監視する必要があります。このセクションでは、再送信とタイムアウトを評価および監視するためのいくつかの方法について説明します。

(1) パケット盗聴ツール

Wireshark などのネットワーク パケット キャプチャ ツールは、ネットワーク インターフェイスを通過するデータ パケットをキャプチャし、TCP 接続での再送信とタイムアウトの現象を分析するのに役立ちます。キャプチャされたデータ パケットを分析することで、パケット損失率、再送時間、再送タイムアウトなどの指標を計算し、TCP 再送とタイムアウト メカニズムの影響を評価できます。

(2) ネットワーク性能試験ツール

Iperf などのネットワーク パフォーマンス テスト ツールを使用すると、2 つのネットワーク ノード間に TCP 接続を確立して、ネットワークの遅延やスループットなどのパフォーマンス インジケーターを測定できます。これらのツールを使用して、ネットワーク パフォーマンスに対する TCP 再送信とタイムアウト メカニズムの影響を評価し、最適化することができます。

(3) アプリケーション層のパフォーマンス監視

アプリケーション層では、アプリケーションの応答時間、スループット、その他の指標を監視することで、TCP 再送信とタイムアウト メカニズムの影響を評価できます。たとえば、APM (Application Performance Management) ツールを使用してアプリケーションのパフォーマンス データを収集および分析し、再送信とタイムアウトに関連するパフォーマンスのボトルネックを見つけることができます。

(4) 機械学習とビッグデータ分析

機械学習とビッグデータ分析技術を利用して、大量のネットワーク データから TCP 再送信とタイムアウトに関する情報をマイニングできます。機械学習モデルをトレーニングすることで、ネットワークで発生する可能性のある再送信やタイムアウトを予測し、事前に対策を講じて最適化することができます。

(5) シミュレーションとモデリング

TCP プロトコルのシミュレーション モデルを確立することにより、制御された環境でのネットワーク パフォーマンスに対するさまざまな再送信およびタイムアウト戦略の影響を評価できます。たとえば、NS-3 などのネットワーク シミュレータを使用して、TCP 接続をシミュレートし、さまざまなネットワーク条件下での再送信とタイムアウトの動作を調べることができます。

これらの評価および監視方法を採用することにより、TCP 再送信およびタイムアウト メカニズムの動作をよりよく理解し、的を絞った最適化を行い、ネットワーク パフォーマンスを向上させることができます。

3.4 Linux C++ コード例

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <ctime>
#include <chrono>

#define SERVER_IP "127.0.0.1"
#define PORT 8080
#define BUFFER_SIZE 1024
#define TIMEOUT_DURATION 500 // ms
bool send_with_timeout(int sockfd, const char *buffer, size_t len, int flags, unsigned int max_retries) {
    
    
    auto start = std::chrono::steady_clock::now();
    auto end = std::chrono::steady_clock::now();
    std::chrono::milliseconds timeout(TIMEOUT_DURATION);

    // 当前重传次数
    unsigned int retries = 0;

    while (retries < max_retries) {
    
    
        ssize_t sent = send(sockfd, buffer, len, flags);

        // 数据发送成功,返回 true
        if (sent == len) {
    
    
            return true;
        }

        end = std::chrono::steady_clock::now();

        // 超时,增加重传次数,重置计时
        if (std::chrono::duration_cast<std::chrono::milliseconds>(end - start) > timeout) {
    
    
            retries++;
            start = std::chrono::steady_clock::now();
        }
    }

    // 达到最大重传次数仍未成功发送,返回 false
    return false;
}

int main() {
    
    
    int client_fd;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE];

    // 创建socket
    client_fd = socket(AF_INET, SOCK_STREAM, 0);

    // 设置服务器地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    server_addr.sin_port = htons(PORT);

    // 连接到服务器
    connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));

    // 与服务器进行数据通信
    size_t window_size = 1;
    size_t num_packets_sent = 0;
    size_t num_acks_received = 0;

    while (true) {
    
    
        // 发送数据
        for (size_t i = 0; i < window_size && num_packets_sent < 10; ++i) {
    
    
            std::string packet = "Packet " + std::to_string(num_packets_sent);
            std::cout << "Sending: " << packet << std::endl;
            send_with_timeout(client_fd, packet.c_str(), packet.size(), 0, 3);
            ++num_packets_sent;
        }

        // 接收ACK
        memset(buffer, 0, BUFFER_SIZE);
        int read_size = recv(client_fd, buffer, BUFFER_SIZE, 0);
        if (read_size <= 0) {
    
    
            break;
        }
        std::cout << "Received ACK: " << buffer << std::endl;
        ++num_acks_received;

        // 调整窗口大小
        if (num_acks_received == num_packets_sent) {
    
    
            window_size *= 2;
        } else {
    
    
            window_size = 1;
        }
    }

    // 关闭连接
    close(client_fd);

    return 0;
}

4. TCP フロー制御と再送信

4.1 選択的確認応答(SACK)機構(選択的確認応答メカニズム)

TCP 再送信とタイムアウト メカニズムをさらに最適化するために、研究者とエンジニアは選択的確認応答 (SACK) メカニズムを導入しました。このセクションでは、SACK メカニズムの基本原理とその利点を紹介します。

(1) SACK機構の基本原理(SACK機構の基本原理)

従来の TCP 確認メカニズムは累積確認を採用しており、受信側は順番に受信した最後のデータ パケットのみを確認します。ネットワーク環境が悪い場合、この仕組みにより不要な再送が何度も発生する可能性があります。SACK メカニズムにより、受信したパケットが順不同で受信された場合でも、受信者はより正確に受信確認を行うことができます。

SACKメカニズムでは、受信者はSACKオプション(SACKオプション)を使用して、受信した不連続データセグメント情報をTCPメッセージで運び、送信者に正しく受信したデータパケットを通知できます。送信者は、受信した SACK 情報に従って未確認のデータ パケットのみを再送信するため、不要な再送信を減らすことができます。

(2) SACK機構のメリット(SACK機構のメリット)

  • 不要な再送信の削減 (不要な再送信の削減): SACK メカニズムは、受信者が受信したデータ パケットを送信者が正確に理解するのに役立ちます。これにより、不要な再送信が削減され、ネットワーク パフォーマンスが向上します。
  • 輻輳制御の強化: SACK メカニズムは、再送信によって引き起こされるネットワークの輻輳を軽減できるため、輻輳制御が改善されます。
  • 順不同のパケット伝送環境への適応 (Adapting to Out-of-order Packet Transmission Environments): SACK メカニズムは、順不同のパケットが多い伝送環境でより優れたパフォーマンスを発揮します。データ伝送の信頼性と効率。

SACK メカニズムを導入することにより、TCP プロトコルは、データ パケットの損失や順不同の問題をより効果的に処理できるようになり、データ転送の信頼性とパフォーマンスがさらに向上します。

4.2 高速再送信と高速回復

高速再送信と高速回復は、TCP プロトコルの 2 つの最適化メカニズムであり、不必要な再送信タイムアウトの待機を減らすために使用され、それによってネットワーク パフォーマンスが向上します。このセクションでは、高速再送信と高速回復の基本原理と利点を紹介します。

(1) 高速再送

高速再送信メカニズムの目的は、再送信タイムアウトを待つ前にパケット損失を検出し、できるだけ早く再送信することです。TCP 接続では、受信側が 3 回連続して ACK を受信すると、送信側は、未確認のデータ パケットが失われたと見なし、再送信タイマーの期限が切れるのを待たずにすぐに再送信します。このようにして、データパケットが失われたときの待ち時間を減らすことができ、データ伝送の効率を向上させることができる。

(2) ファーストリカバリー (Fast Recovery)

再送信のラウンド中に輻輳ウィンドウをより迅速に回復するために、高速再送信の後に高速回復メカニズムが開始されます。送信者が高速回復フェーズに入ると、従来の輻輳制御のように輻輳ウィンドウを 1 にリセットする代わりに、輻輳ウィンドウを半分にします。これにより、ウィンドウ サイズが小さすぎることによるネットワーク スループットの低下が回避されます。同時に、送信者は、受信した新しい ACK 情報に従って輻輳ウィンドウを調整し、回復中にデータを送信し続けるようにします。

(3) Fast Retransmit と Fast Recovery の利点

  • 再送遅延の短縮: 高速再送メカニズムは、再送タイマーが期限切れになる前にパケット損失を検出できるため、再送遅延が短縮されます。
  • グローバル同期の回避: 高速リカバリ メカニズムにより、ウィンドウ サイズのリセットによって発生するグローバル同期を回避し、ネットワーク スループットの安定性を維持できます。
  • 輻輳制御の強化: 高速再送信および高速回復メカニズムにより、輻輳制御がネットワーク パフォーマンスに与える悪影響をある程度軽減できるため、ネットワーク パフォーマンスが向上します。

要約すると、TCP プロトコルの最適化メカニズムである高速再送信と高速回復は、ネットワーク パフォーマンスを大幅に向上させ、データ パケットが失われたときの待ち時間を短縮し、データ送信の信頼性と効率を向上させることができます。

4.3 輻輳ウィンドウ検証済み高速再送信 (TCP NewReno)

Congestion Window Authenticated Fast Retransmission (TCP NewReno) は、TCP Reno の改良版で、複数のパケットが同時に失われた場合のパフォーマンスの問題に対処します。このセクションでは、TCP NewReno の基本原理とその利点を紹介します。

(1) TCP NewReno の基本原則 (TCP NewReno の基本原則)

TCP Reno は、タイムアウト期間の 1 ラウンド内で失われた 1 つのパケットしか回復できないため、複数のパケット損失に対処する場合、多少制限があります。TCP NewReno はこれを改善し、複数の失われたパケットを 1 つのタイムアウト期間内に回復できるようにします。

TCP NewReno では、送信者が 3 つの重複 ACK を受信すると、高速回復フェーズに入り、同時に「部分 ACK」フラグを設定します。送信者が部分的な ACK を受信すると、再送信タイマーが期限切れになるのを待つ代わりに、未確認のパケットを再送信します。このようにして、1 つのタイムアウト期間内に、TCP NewReno は複数の失われたパケットを回復できます。

(2) TCP NewRenoのメリット(TCP NewRenoのメリット)

  • より効率的なパケット損失回復 (より効率的なパケット損失回復): TCP NewReno は、タイムアウト期間内に複数の失われたパケットを回復できるため、パケット損失回復の効率が向上します。
  • 再送遅延の削減: TCP NewReno は、部分的な ACK を受信するとすぐに未確認のパケットを再送信することで、再送信遅延を削減できます。
  • 改善された輻輳制御 (改善された輻輳制御): TCP NewReno は、複数のパケット損失をより適切に処理できるため、輻輳制御の効果が向上します。

結論として、TCP Reno の改良版である TCP NewReno は、複数のデータ パケット損失をより効果的に処理し、ネットワーク パフォーマンスとデータ伝送の信頼性を向上させることができます。TCP NewReno を採用することで、TCP 再送信とタイムアウト メカニズムをさらに最適化し、さまざまなネットワーク アプリケーション シナリオのニーズを満たすことができます。

4.4 フロー制御と再送の実装手順

  1. 必要なライブラリのインポート: C++ を使用して TCP フロー制御と再送信を実装する場合、次のライブラリをインポートする必要があります。
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/tcp.h>
    #include <fcntl.h>
    #include <errno.h>
    
  2. ソケットの作成: TCP ソケットを作成し、指定したアドレスとポートにバインドします。次に、listen()関数を使用して接続要求をリッスンします。
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
  3. ソケット オプションの設定: フロー制御と再送信を実現するには、TCP_NODELAY (Nagle アルゴリズムを無効にする) や TCP_QUICKACK (クイック確認を有効にする) など、いくつかのソケット オプションを設定する必要があります。
    int flag = 1;
    setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
    setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, (char *)&flag, sizeof(int));
    
  4. データの接続と受信:accept()関数を使用して、クライアント接続を受信し、新しいソケットを作成します。次に、recv()関数を使用してデータを受信します。
    int client_sock = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
    char buffer[1024];
    ssize_t bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
    
  5. フロー制御の実装: スライディング ウィンドウ アルゴリズムを使用してフロー制御を実装します。(送信バッファ サイズ) および(受信バッファ サイズ) オプションは、スライディング ウィンドウのサイズを調整するsetsockopt()関数を使用して設定できます。SO_SNDBUFSO_RCVBUF
    int send_buffer_size = 4096;
    int recv_buffer_size = 4096;
    setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &send_buffer_size, sizeof(send_buffer_size));
    setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recv_buffer_size, sizeof(recv_buffer_size));
    
  6. 再送信メカニズムを実装する:select()関数またはpoll()関数を使用して、ソケットの読み取り可能性と書き込み可能性を監視し、タイムアウトと再送信メカニズムを実装できます。同時に、再送信タイムアウトを指定するオプションをsetsockopt()設定する機能を使用できます。TCP_USER_TIMEOUT
    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(client_sock, &read_fds);
    
    int user_timeout = 5000;
    setsockopt(client_sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout, sizeof(user_timeout));
    
    int ret = select(client_sock + 1, &read_fds, nullptr, nullptr, &timeout);
    if (ret < 0) {
          
          
        perror("select");
    } else if (ret == 0) {
          
          
    std::cout << "Timeout, retransmitting data..." << std::endl;
    } else {
          
          
    if (FD_ISSET(client_sock, &read_fds)) {
          
          
        ssize_t bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
        // 处理接收到的数据
    }
    }
    
    
  7. データ送信:send()関数を使用してデータを送信します。データを送信する前に、ソケットの書き込み可能性をチェックして、ブロックを防ぐことができます。
    ssize_t bytes_sent = send(client_sock, buffer, strlen(buffer), 0);
    
  8. ソケットを閉じる: この関数を使用して、データ転送が完了した後にソケットを閉じますclose()
    close(client_sock);
    close(sockfd);
    

上記のコードは、TCP フロー制御と再送信を実装するための簡単な概要を提供するだけです。実際のアプリケーションでは、特定の要件に従ってコードを調整および最適化する必要があります。

4.5 フロー制御と再送信の完全なコード

コードでは、unacked_packets未確認のパケットを格納するために呼び出されるベクトルを使用します。各データ パケットには、送信側と受信側の間で特定のデータ パケットを識別するために使用されるシーケンス番号があります。データ パケットを正常に受信した後、受信者は受信確認メッセージ (ACK) を送信者に送信します。このメッセージには、受信を確認するためのデータ パケットのシーケンス番号が含まれています。

送信側は ACK を受信すると、unacked_packetsベクターをチェックし、受信した ACK のシーケンス番号に基づいて更新します。ACK シーケンス番号以下のシーケンス番号を持つベクトル内のすべてのパケットは、受信者が既にそれらを受信したことを意味するため、確認済みと見なされます。

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/select.h>
#include <string>
#include <algorithm>
#include <vector>
#include <thread>
#define USE_SEPARATE_SEQUENCE_NUMBERS 1

struct Packet {
    
    
#if USE_SEPARATE_SEQUENCE_NUMBERS
    uint32_t seq_number;
#endif
    char data[1024];
    int retry_count;
};


void retransmit_data(int client_sock, std::vector<Packet>& unacked_packets) {
    
    
    const int max_retries = 5; // Maximum number of retransmission attempts
    const int initial_backoff = 1000; // Initial backoff time in milliseconds (1 second)
    
    std::vector<Packet> new_unacked_packets;

    for (auto& packet : unacked_packets) {
    
    
        // Check if the retry counter has reached the maximum number of attempts
        if (packet.retry_count >= max_retries) {
    
    
#if USE_SEPARATE_SEQUENCE_NUMBERS
            std::cerr << "Maximum number of retransmission attempts reached for sequence number: "
                      << packet.seq_number << std::endl;
#else
            std::cerr << "Maximum number of retransmission attempts reached for the packet" << std::endl;
#endif
            continue;
        }
        
        // Send the packet
        ssize_t bytes_sent = send(client_sock, &packet, sizeof(packet), 0);
        
        // Error in send()
        if (bytes_sent < 0) {
    
    
            std::cerr << "Error in send: " << strerror(errno) << std::endl;
        } else {
    
    
#if USE_SEPARATE_SEQUENCE_NUMBERS
            std::cout << "Data retransmitted. Sequence number: " << packet.seq_number << std::endl;
#else
            std::cout << "Data retransmitted." << std::endl;
#endif
            // Increment the retry counter
            ++packet.retry_count;

            // Apply exponential backoff
            int backoff_time = initial_backoff * (1 << (packet.retry_count - 1));
            std::this_thread::sleep_for(std::chrono::milliseconds(backoff_time));

            new_unacked_packets.push_back(packet);
        }
    }

    unacked_packets.swap(new_unacked_packets);
}




int main(int argc, char *argv[]) {
    
    
    std::vector<Packet> unacked_packets;

    // 1. 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0) {
    
    
        std::cerr << "Error in socket: " << strerror(errno) << std::endl;
        return -1;
    }

    // 2. 绑定地址和端口
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(8888);

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
    
    
        std::cerr << "Error in bind: " << strerror(errno) << std::endl;
        close(sockfd);
        return -1;
    }

    // 3. 监听连接
    if (listen(sockfd, 5) < 0) {
    
    
        std::cerr << "Error in listen: " << strerror(errno) << std::endl;
        close(sockfd);
        return -1;
    }

    // 4. 接受连接
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);
    int client_sock = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
    if (client_sock < 0) {
    
    
        std::cerr << "Error in accept: " << strerror(errno) << std::endl;
        close(sockfd);
        return -1;
    }

    // 5. 设置流量控制
    int send_buffer_size = 4096;
    int recv_buffer_size = 4096;
    setsockopt(client_sock, SOL_SOCKET, SO_SNDBUF, &send_buffer_size, sizeof(send_buffer_size));
    setsockopt(client_sock, SOL_SOCKET, SO_RCVBUF, &recv_buffer_size, sizeof(recv_buffer_size));

    // 6. 初始化select
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(client_sock, &read_fds);

    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;

    // 7. 使用select等待数据
    // Initialize a temporary file descriptor set for select()
    fd_set temp_fds;

    // Main loop
    while (true) {
    
    
        // Update the timeout structure
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        // Copy the original file descriptor set (read_fds) to a temporary set (temp_fds)
        temp_fds = read_fds;

        // Use select() to wait for events on the client socket (data available to read or timeout)
        // The last argument (timeout) specifies the maximum time select() should wait for an event
        int ret = select(client_sock + 1, &temp_fds, nullptr, nullptr, &timeout);

        // Error in select()
        if (ret < 0) {
    
    
            std::cerr << "Error in select: " << strerror(errno) << std::endl;
            break;
        } 
        // Timeout occurred, indicating that no ACK was received during the specified time interval
        // This is used to detect lost packets and trigger retransmission
        else if (ret == 0) {
    
    
            std::cout << "Timeout, retransmitting data..." << std::endl;
            retransmit_data(client_sock, unacked_packets);
            continue;
        }

      // If data is available to read on the client socket, process the ACK
      if (FD_ISSET(client_sock, &temp_fds)) {
    
    
          //从客户端接收到的ACK序列号
          uint32_t ack_seq_number;
          ssize_t bytes_received;
      
      #if USE_SEPARATE_SEQUENCE_NUMBERS
          // Receive the ACK sequence number from the client
          bytes_received = recv(client_sock, &ack_seq_number, sizeof(ack_seq_number), 0);
          if(bytes_received<=0) goto ret_process;
      #endif
          Packet data_packet;
          // Receive the ACK sequence number and data from the client
          bytes_received = recv(client_sock, &data_packet, sizeof(data_packet), 0);
          ack_seq_number = data_packet.seq_number;
          
  ret_process:
      
          if (bytes_received < 0) {
    
    
              std::cerr << "Error in recv: " << std::strerror(errno) << std::endl;
              if (errno == EAGAIN || errno == EWOULDBLOCK) {
    
     // No data available for reading temporarily
                  std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait for 100ms before continuing the loop
                  continue;
              } else if (errno == ECONNRESET) {
    
     // Connection reset by the other party
                std::cout << "Connection reset by peer." << std::endl;
                close(client_sock); // Close the socket
                client_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Create a new socket and reconnect
                if (connect(client_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
    
    
                    std::cerr << "Error in connect: " << strerror(errno) << std::endl;
                    close(client_sock);
                    return -1;
                }
                continue;
              } else {
    
     // Other error codes
                  std::cerr << "recv error: " << std::strerror(errno) << std::endl;
                  break;
              }
          }
          // Client disconnected
          else if (bytes_received == 0) {
    
    
              std::cout << "Client disconnected." << std::endl;
              break;
          }
          // Valid ACK received
          else {
    
    
              // Convert the received sequence number to host byte order
              ack_seq_number = ntohl(ack_seq_number);
      
              // Remove the acknowledged packets from the unacked_packets vector
      #ifdef USE_SEPARATE_SEQUENCE_NUMBERS
              // Handle separate sequence numbers for each packet
              unacked_packets.erase(std::remove_if(unacked_packets.begin(), unacked_packets.end(),
                  [&ack_seq_number](const Packet& packet) {
    
    
                      return packet.seq_number == ack_seq_number;
                  }), unacked_packets.end());
      #else
              // Handle cumulative sequence numbers
              unacked_packets.erase(std::remove_if(unacked_packets.begin(), unacked_packets.end(),
                  [&ack_seq_number](const Packet& packet) {
    
    
                      return packet.seq_number <= ack_seq_number;
                  }), unacked_packets.end());
      #endif
      
              std::cout << "Received ACK for sequence number: " << ack_seq_number << std::endl;
     
              std::cout << "Received data: " << data_packet.data << std::endl;
      
          }
      }
    }


    // 8. 关闭套接字
    close(client_sock);
    close(sockfd);

    return 0;
}



クライアント:

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string>
#include <thread>
#define USE_SEPARATE_SEQUENCE_NUMBERS 1

int main(int argc, char *argv[]) {
    
    
    // 1. 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0) {
    
    
        std::cerr << "Error in socket: " << strerror(errno) << std::endl;
        return -1;
    }

    // 2. 连接到服务器
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(8888);

    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
    
    
        std::cerr << "Error in connect: " << strerror(errno) << std::endl;
        close(sockfd);
        return -1;
    }

    // 3. 发送数据和ACK序列号给服务器
    uint32_t seq_number = 1;

    while (true) {
    
    
        std::string input;
        std::cout << "Enter data to send: ";
        std::getline(std::cin, input);

        if (input == "exit") {
    
    
            break;
        }

        // Convert the sequence number to network byte order
        uint32_t network_seq_number = htonl(seq_number);

    #if USE_SEPARATE_SEQUENCE_NUMBERS
        // Send the ACK sequence number to the server
        ssize_t bytes_sent = send(sockfd, &network_seq_number, sizeof(network_seq_number), 0);
        if (bytes_sent < 0) {
    
    
            std::cerr << "Error in send: " << strerror(errno) << std::endl;
            break;
        }

        // Send the data to the server
        bytes_sent = send(sockfd, input.c_str(), input.size() + 1, 0);
    #else
        struct Packet {
    
    
            uint32_t seq_number;
            char data[1024];
        };

        Packet data_packet;
        data_packet.seq_number = network_seq_number;
        strncpy(data_packet.data, input.c_str(), sizeof(data_packet.data));

        // Send the data packet to the server
        ssize_t bytes_sent = send(sockfd, &data_packet, sizeof(data_packet), 0);
    #endif

        if (bytes_sent < 0) {
    
    
            std::cerr << "Error in send: " << strerror(errno) << std::endl;
            break;
        }

        std::cout << "Data sent. Sequence number: " << seq_number << std::endl;

        // Increment the sequence number
        ++seq_number;
    }

    // 4. 关闭套接字
    close(sockfd);

    return 0;
}

5. TCP 再送信とタイムアウトの最適化の実践

5.1 TCP の比較的新しい輻輳制御アルゴリズムの導入 (より新しい TCP 輻輳制御アルゴリズムの採用)

TCP 再送信とタイムアウト メカニズムをさらに最適化するために、比較的新しい輻輳制御アルゴリズムを使用してみることができます。これらのアルゴリズムは、さまざまなネットワーク環境で優れたパフォーマンスを発揮し、再送信の遅延を効果的に減らし、ネットワーク スループットを向上させることができます。このセクションでは、いくつかの新しい TCP 輻輳制御アルゴリズムを紹介します。

(1) CUBIC: CUBIC アルゴリズムは、高帯域幅ネットワークおよび長距離伝送用の輻輳制御アルゴリズムであり、3 次輻輳ウィンドウ成長関数を導入することにより、ネットワークのスループットと遅延のバランスを改善することができます。CUBIC アルゴリズムは、高速ネットワークで特に優れたパフォーマンスを発揮し、データ伝送パフォーマンスを大幅に向上させることができます。

(2) BBR (ボトルネック帯域幅と RTT): BBR アルゴリズムは、ボトルネック帯域幅とネットワークの往復遅延を推定することによって輻輳ウィンドウを調整し、従来の輻輳制御アルゴリズムのパケット損失に対する過度の感度を回避します。BBR アルゴリズムは、ネットワークが混雑し、パケット損失率が高い環境でより優れたパフォーマンスを発揮します。

(3) 複合 TCP: 複合 TCP は、輻輳ウィンドウと受信ウィンドウ調整を組み合わせて高帯域幅ネットワークのパフォーマンスを向上させる輻輳制御アルゴリズムです。輻輳ウィンドウと受信ウィンドウの両方を考慮することで、Compound TCP はネットワーク スループットとレイテンシのバランスを改善できます。

(4) Vegas: Vegas アルゴリズムは、往復遅延を測定することによって輻輳を予測し、事前に輻輳ウィンドウを調整します。Vegas アルゴリズムは、遅延とパケット損失率が低いネットワークでより優れたパフォーマンスを発揮し、輻輳と再送信の遅延を効果的に減らすことができます。

これらの新しい TCP 輻輳制御アルゴリズムを導入することで、さまざまなネットワーク環境とアプリケーションの要件に応じて適切なアルゴリズムを選択できるため、TCP 再送信とタイムアウト メカニズムが最適化され、ネットワーク パフォーマンスが向上します。

5.2 前方誤り訂正 (前方誤り訂正、FEC) の使用

前方誤り訂正 (FEC) は、冗長データを追加することによってデータ伝送の信頼性を向上させる方法です。FEC を使用することで、TCP 再送信の回数をある程度減らすことができるため、再送信の遅延が減少し、ネットワーク パフォーマンスが向上します。このセクションでは、FEC の基本原理と、TCP 再送信およびタイムアウト メカニズムにおけるそのアプリケーションを紹介します。

(1) FECの基本原則(FECの基本原則)

FEC は、元のデータに冗長情報を追加するため、受信側は、破損したデータ パケットや失われたデータ パケットを受信した場合でも、元のデータを再構築できます。FEC コーディングには、ハミング コード、リードソロモン コードなど、さまざまな方法があります。これらの符号化方法は、余分なオーバーヘッドを追加することなく、データ伝送にある程度のエラー訂正を提供できます。

(2) TCP 再送およびタイムアウト メカニズムにおける FEC の適用 (TCP 再送およびタイムアウト メカニズムにおける FEC の適用)

TCP 転送では、FEC テクノロジと従来の TCP 再転送メカニズムを組み合わせることで、再転送の回数を減らし、ネットワーク パフォーマンスを向上させることができます。たとえば、送信側はデータ パケットを送信するときに FEC エンコードされた冗長データを追加でき、受信側は部分的に破損したデータ パケットまたは失われたデータ パケットを受信するときに、これらの冗長データをエラー訂正に使用して、場合によってはエラーを回避できます。

FEC テクノロジは TCP 再送信をある程度減らすことができますが、計算と帯域幅のオーバーヘッドが増加することに注意してください。したがって、実際のアプリケーションでは、特定のシナリオに最適なソリューションを見つけるために、FEC によってもたらされるパフォーマンスの向上とそのオーバーヘッドの関係を比較検討する必要があります。

つまり、データ転送の信頼性を向上させる方法として、FEC テクノロジを TCP 再転送およびタイムアウト メカニズムと組み合わせて、ネットワーク パフォーマンスをさらに最適化することができます。FEC を使用することで、データ伝送の信頼性を確保しながら、再送回数を減らし、再送遅延を減らすことができます。

5.3 マルチパス伝送の使用

マルチパス伝送とは、ネットワーク内の複数のパスを利用してデータを同時に伝送する方法で、ネットワークのパフォーマンスと信頼性を向上させます。TCP の再送とタイムアウトのメカニズムでは、マルチパス伝送を使用することでデータ パケットの損失の可能性を減らし、再送の回数と遅延を減らすことができます。このセクションでは、マルチパス伝送の基本原理と、TCP 再伝送およびタイムアウト メカニズムへの応用について説明します。

(1) マルチパス伝送の基本原理

マルチパス伝送では、送信者がデータ パケットを複数のネットワーク パスに分散して伝送し、受信者がこれらのパケットを異なるパスから受信して再構成します。これにより、ある経路で問題が発生してデータパケットが失われた場合でも、他の経路のデータパケットを利用して確実にデータを伝送することができます。

(2) TCP 再送およびタイムアウト機構におけるマルチパス伝送の適用 (TCP 再送およびタイムアウト機構におけるマルチパス伝送の適用)

TCP 伝送では、マルチパス TCP (MPTCP) を実装することで、マルチパス伝送をサポートできます。MPTCP は従来の TCP に基づいて拡張されており、複数のネットワーク パスで TCP 接続を同時に確立できるため、データ転送の信頼性とパフォーマンスが向上します。

マルチパス伝送を使用することで、1 つのパスでのパケット損失の可能性を減らすことができるため、TCP 再送の回数と遅延を減らすことができます。同時に、マルチパス伝送は、異なるパス間の負荷分散を実現し、ネットワークのスループットを向上させることもできます。

マルチパス伝送は、既存の TCP プロトコルを拡張する必要があることに注意してください。これにより、特定の複雑さが生じる可能性があります。さらに、マルチパス伝送には、ネットワーク内のパス選択と負荷分散戦略に関するより高い要件もあります。したがって、実際のアプリケーションでは、特定のシナリオと要件に従ってマルチパス伝送技術を使用するかどうかを選択する必要があります。

つまり、データ転送の信頼性とパフォーマンスを向上させる方法として、マルチパス転送を TCP 再転送およびタイムアウト メカニズムと組み合わせて、ネットワーク パフォーマンスをさらに最適化することができます。マルチパス伝送を採用することで、データ伝送の信頼性を確保しながら、再送回数と遅延を低減することができます。

5.3 マルチパス伝送の使用

マルチパス伝送とは、ネットワーク内の複数のパスを利用してデータを同時に伝送する方法で、ネットワークのパフォーマンスと信頼性を向上させます。TCP の再送とタイムアウトのメカニズムでは、マルチパス伝送を使用することでデータ パケットの損失の可能性を減らし、再送の回数と遅延を減らすことができます。このセクションでは、マルチパス伝送の基本原理と、TCP 再伝送およびタイムアウト メカニズムへの応用について説明します。

(1) マルチパス伝送の基本原理

マルチパス伝送では、送信者がデータ パケットを複数のネットワーク パスに分散して伝送し、受信者がこれらのパケットを異なるパスから受信して再構成します。これにより、ある経路で問題が発生してデータパケットが失われた場合でも、他の経路のデータパケットを利用して確実にデータを伝送することができます。

(2) TCP 再送およびタイムアウト機構におけるマルチパス伝送の適用 (TCP 再送およびタイムアウト機構におけるマルチパス伝送の適用)

TCP 伝送では、マルチパス TCP (MPTCP) を実装することで、マルチパス伝送をサポートできます。MPTCP は従来の TCP に基づいて拡張されており、複数のネットワーク パスで TCP 接続を同時に確立できるため、データ転送の信頼性とパフォーマンスが向上します。

マルチパス伝送を使用することで、1 つのパスでのパケット損失の可能性を減らすことができるため、TCP 再送の回数と遅延を減らすことができます。同時に、マルチパス伝送は、異なるパス間の負荷分散を実現し、ネットワークのスループットを向上させることもできます。

マルチパス伝送は、既存の TCP プロトコルを拡張する必要があることに注意してください。これにより、特定の複雑さが生じる可能性があります。さらに、マルチパス伝送には、ネットワーク内のパス選択と負荷分散戦略に関するより高い要件もあります。したがって、実際のアプリケーションでは、特定のシナリオと要件に従ってマルチパス伝送技術を使用するかどうかを選択する必要があります。

つまり、データ転送の信頼性とパフォーマンスを向上させる方法として、マルチパス転送を TCP 再転送およびタイムアウト メカニズムと組み合わせて、ネットワーク パフォーマンスをさらに最適化することができます。マルチパス伝送を採用することで、データ伝送の信頼性を確保しながら、再送回数と遅延を低減することができます。

6. 結論

このブログでは、TCP 再送信、タイムアウト、輻輳制御、フロー制御などについて詳細な議論を行い、TCP プロトコルがネットワーク送信中にデータの信頼性と効率をどのように保証するかを読者がよりよく理解できるようにします。さまざまな再送信の原則、トリガー条件、最適化戦略を詳細に分析し、タイムアウト検出、動的調整、再送信との関係も分析しました。同時に、ネットワークの伝送速度を調整し、ネットワークの安定性を確保するための相乗効果における輻輳制御とフロー制御の重要性についても説明します。

実際には、さまざまなネットワーク環境に最適化戦略を提供し、一般的なパフォーマンスの問題を分析して解決します。さらに、今後の TCP 再送信とタイムアウトの開発動向にも期待し、ネットワーク パフォーマンスをさらに改善するためのガイダンスを提供します。

このブログは、ネットワーク トランスポートの課題を解決するために TCP プロトコルを深く理解したいと考えている読者にとって、非常に価値のあるものになると信じています。より多くの人がこの知識から恩恵を受けることができるように、ブックマーク、フォロー、いいねをお願いします。今後の学習と実践のさらなる成功を期待しています。

おすすめ

転載: blog.csdn.net/qq_21438461/article/details/130442706