設計上の問題 - Winsock を使用して TCP を使用して小さなデータ セグメントを送信する

小さなデータ パケットを TCP で送信する必要がある場合、Winsock アプリケーションの設計は特に重要です。 遅延確認、Nagle アルゴリズム、および Winsock バッファー処理の相互作用を考慮しない設計は、パフォーマンスに大きな影響を与える可能性があります。 この記事では、いくつかのケース スタディを使用してこれらの問題について説明します。 また、Winsock アプリケーションから小さなデータ パケットを効率的に送信するための一連の推奨事項も派生します。

元の製品バージョン:   Winsock
元の KB 番号:   214397

背景

Microsoft TCP スタックがデータ パケットを受信すると、200 ミリ秒の遅延タイマーがオフになります。 ACK が送信された場合、遅延タイマーはリセットされ、次のデータ パケットを受信すると、さらに 200 ミリ秒の遅延が開始されます。 インターネット アプリケーションとイントラネット アプリケーションの両方で効率を向上するために、TCP スタックは次の条件を使用して、受信したデータ パケットに対して 1 つの ACK を送信する場合を決定します。

  • 遅延タイマーの有効期限が切れる前に 2 番目のデータ パケットを受信すると、ACK が送信されます。
  • 2 番目のデータ パケットを受信する前に ACK と同じ方向に送信するデータが含まれており、遅延タイマーが期限切れになる場合、ACK はデータ セグメントにピギーバックされ、直ちに送信されます。
  • 遅延タイマーの有効期限が切れると、ACK が送信されます。

ネットワークに小さなデータ パケットが混雑しないようにするために、TCP スタックは Nagle アルゴリズムを既定で有効にし、複数の送信呼び出しから小さなデータ バッファーを合体し、リモート ホストから送信された以前のデータ パケットの ACK が受信されるまで送信を遅延します。 Nagle アルゴリズムの 2 つの例外を次に示します。

  • スタックが最大伝送単位 (MTU) より大きいデータ バッファーを合体した場合、リモート ホストからの ACK を待たずにフルサイズのパケットが直ちに送信されます。 イーサネット ネットワークでは、TCP/IP の MTU は 1460 バイトです。

  • ソケット TCP_NODELAY オプションが適用され、Nagle アルゴリズムを無効にして、小さなデータ パケットが遅延なくリモート ホストに配信されます。

アプリケーション層でのパフォーマンスを最適化するために、Winsock はアプリケーションから Winsock カーネル バッファーへの呼び出しを送信するデータ バッファーをコピーします。 次に、スタックは独自のヒューリスティック (Nagle アルゴリズムなど) を使用して、パケットを実際にワイヤーに置く時間を決定します。 オプションを使用して、ソケットに割り当てられた Winsock カーネル バッファーの量 SO_SNDBUF を変更できます (既定では 8K です)。 必要に応じて、Winsock はバッファー サイズを超えるバッファーを SO_SNDBUF 使用できます。 ほとんどの場合、アプリケーションの送信完了は、アプリケーション送信呼び出しのデータ バッファーが Winsock カーネル バッファーにコピーされ、データがネットワーク メディアにヒットしたとは示されません。 唯一の例外は、Winsock バッファー処理を 0 に設定して無効に SO_SNDBUF した場合です。

Winsock は、次のルールを使用して、アプリケーションへの送信完了を示します (送信の呼び出し方法によっては、完了通知は、ブロック呼び出しから返される関数、イベントの通知、または通知関数の呼び出しなどです)。

  • ソケットがまだクォータ内にあるSO_SNDBUF Winsock は、アプリケーション送信からデータをコピーし、送信完了をアプリケーションに示します。

  • ソケットがクォータを超えていて、スタック カーネル バッファー内にバッファー処理された送信がまだ 1 つしか存在しない場合、Winsock はアプリケーションの送信からデータをコピーし、送信完了をアプリケーションに示します。 SO_SNDBUF

  • ソケットがクォータを超え、スタック カーネル バッファーに以前にバッファーに入れた送信が複数ある場合、Winsock はアプリケーション送信からデータ SO_SNDBUF をコピーします。 Winsock は、スタックがクォータ内にソケットを戻すのに十分な送信を完了するか、未処理の送信条件を 1 つしか送信しないまで、アプリケーションへの送信完了 SO_SNDBUF を示します。

ケース スタディ 1

Winsock TCP クライアントは、データベースに格納する Winsock TCP サーバーに 10000 レコードを送信する必要があります。 レコードのサイズは、20 バイトから 100 バイトの長さまで異なります。 アプリケーション ロジックを簡略化するために、設計は次のとおりです。

  • クライアントは送信のみをブロックします。 サーバーはブロック recv のみを行います。
  • クライアント ソケットは SO_SNDBUF 0 に設定し、各レコードが 1 つのデータ セグメントに出力されます。
  • サーバーはループ recv で呼び出します。 投稿されたバッファーは 200 バイトなので、各レコードを 1 回の呼び出し recv で受信 recv できます。

パフォーマンス

テスト中、開発者は、クライアントがサーバーに 1 秒あたり 5 つのレコードしか送信できません。 合計 1,0000 レコード (最大 976 kb のデータ (10000 * 100/ 1024) は、サーバーに送信するのに 30 分以上かかる。

分析

クライアントはオプションを設定しないので、Nagle アルゴリズムは TCP スタックが ACK を強制的に待機してから、ネットワーク上で別のパケットを TCP_NODELAY 送信できます。 ただし、クライアントはオプションを 0 に設定して Winsock SO_SNDBUF バッファー処理を無効にしています。 したがって、10000 の送信呼び出しを送信し、ACK'ed を個別に送信する必要があります。 サーバーの TCP スタックで次の処理が行われるため、各 ACK は 200 ミリ秒遅れになります。

  • サーバーがパケットを取得すると、200 ミリ秒の遅延タイマーがオフになります。
  • サーバーは何も送り返す必要はないので、ACK をピギーバックすることはできません。
  • 前のパケットが確認されない限り、クライアントは別のパケットを送信しない。
  • サーバーの遅延タイマーの有効期限が切れ、ACK が返されます。

改善方法

この設計には 2 つの問題があります。 まず、遅延タイマーの問題があります。 クライアントは、200 ミリ秒以内に 2 つのパケットをサーバーに送信できる必要があります。 クライアントは既定で Nagle アルゴリズムを使用しますので、既定の Winsock バッファー処理を使用し SO_SNDBUF 、0 には設定しない必要があります。 TCP スタックが最大伝送単位 (MTU) よりも大きいバッファーを合体すると、リモート ホストからの ACK を待たずにフルサイズのパケットが直ちに送信されます。

次に、このデザインでは、このような小さいサイズのレコードごとに 1 つの送信を呼び出します。 この小さいサイズの送信は効率的ではありません。 この場合、開発者は各レコードを 100 バイトにパッドし、1 つのクライアント送信呼び出しから一度に 80 レコードを送信する必要があります。 サーバーに送信されるレコードの総数を知らせた場合、クライアントは、フォローするレコードの数を含む修正サイズのヘッダーとの通信を開始する必要があります。

ケース スタディ 2

Winsock TCP クライアント アプリケーションは、株価サービスを提供する Winsock TCP サーバー アプリケーションとの 2 つの接続を開きます。 最初の接続は、サーバーに株式記号を送信するコマンド チャネルとして使用されます。 2 番目の接続は、株価を受け取るデータ チャネルとして使用されます。 2 つの接続が確立されると、クライアントはコマンド チャネルを介してサーバーに株式記号を送信し、株価がデータ チャネルから戻ってくるのを待ちます。 最初の株価が受信された後にのみ、次の株価記号要求をサーバーに送信します。 クライアントとサーバーは and オプションを SO_SNDBUF 設定 TCP_NODELAY しません。

  • パフォーマンス

    テスト中に、開発者はクライアントが 1 秒あたり 5 つの引用符しか取得しきれない場合を見つける。

  • 分析

    この設計では、一度に 1 つの未処理の株価要求のみを許可します。 最初のストック シンボルはコマンド チャネル (接続) を介してサーバーに送信され、応答は直ちにデータ チャネル (接続) を介してサーバーからクライアントに送り返されます。 次に、クライアントは 2 番目のストック シンボル要求を直ちに送信し、送信呼び出しの要求バッファーが Winsock カーネル バッファーにコピーされた直後に送信が返されます。 ただし、コマンド チャネルを使用した最初の送信がまだ確認されないので、クライアント TCP スタックはカーネル バッファーから要求をすぐに送信できません。 サーバー コマンド チャネルでの 200 ミリ秒の遅延タイマーの有効期限が切れると、最初のシンボル要求の ACK がクライアントに戻されます。 次に、2 番目の見積もり要求は、200 ミリ秒の遅延後にサーバーに正常に送信されます。 2 番目の株価記号の見積もりは、この時点でクライアント データ チャネルの遅延タイマーが期限切れになったため、データ チャネルを通じてすぐに戻ります。 前の見積応答の ACK がサーバーによって受信されます。 (クライアントが 200 ミリ秒の 2 番目の株価要求を送信できないので、クライアントの遅延タイマーの有効期限が切れて ACK をサーバーに送信する時間を与えることに注意してください)。その結果、クライアントは 2 番目の見積もり応答を取得し、同じサイクルの対象となる別の見積もり要求を発行できます。

  • 改善方法

    ここでは、2 つの接続 (チャネル) 設計は不要です。 株価の要求と応答に 1 つの接続のみを使用する場合は、見積依頼の ACK を見積応答でピギーバックし、すぐに戻ってきます。 パフォーマンスをさらに向上させるために、クライアントは複数の株価要求をサーバーへの 1 回の送信呼び出しに多重化し、サーバーは複数の見積応答をクライアントへの 1 回の送信呼び出しに多重化することもできます。 何らかの理由で 2 つの単方向チャネル設計が必要な場合は、前のパケットの ACK を待つことなく、小さなパケットをすぐに送信できるよう、両方の側でオプションを設定する TCP_NODELAY 必要があります。

推奨事項

これら 2 つのケース スタディは作成されましたが、最悪のシナリオを説明するのに役立ちます。 大規模な小さなデータ セグメントが送信され、次のガイドラインを考慮する必要があるアプリケーションを recvs 設計する場合。

  • データ セグメントが時間的に重要ではない場合、アプリケーションはそれらをより大きなデータ ブロックに合体して送信呼び出しに渡す必要があります。 送信バッファーは Winsock カーネル バッファーにコピーされる可能性が高いので、バッファーが大きすぎます。 8K より少し小さい方が有効です。 Winsock カーネルが MTU より大きいブロックを取得する限り、複数のフルサイズ パケットと最後のパケットが残っているパケットを送信します。 送信側は、最後のパケットを除き、200 ミリ秒の遅延タイマーではヒットしない。 最後のパケットが奇数のパケットである場合でも、遅延確認アルゴリズムの対象です。 送信エンド スタックが MTU より大きい別のブロックを取得した場合でも、Nagle アルゴリズムをバイパスできます。

  • 可能であれば、単方向データ フローを使用したソケット接続は避ける必要があります。 単方向ソケット上の通信は、Nagle および遅延受信確認アルゴリズムの影響を受けやすいです。 通信が要求と応答フローに従う場合は、単一のソケットを使用して送信を行い、応答で ACK をピギーバック recvs できます。

  • すべての小さなデータ セグメントをすぐに送信する必要がある場合は、送信端 TCP_NODELAY にオプションを設定します。

  • Winsock によって送信完了が示されている場合に、パケットがワイヤーで送信されるのを保証する場合をしない限り、ゼロに SO_SNDBUF 設定する必要はありません。 実際、既定の 8K バッファーは、ほとんどの状況でうまく動作するとヒューリスティックに決定されています。新しい Winsock バッファー設定によって既定よりもパフォーマンスが向上するテストを行っていない限り、変更する必要があります。 また、ゼロに SO_SNDBUF 設定すると、データの一括転送を行うアプリケーションに対して主に有効です。 それでも、効率を最大限に高めるには、ダブル バッファリング (任意の時点で複数の未処理の送信) と重複する I/O と組み合わせて使用する必要があります。

  • データ配信を保証する必要がない場合は、UDP を使用します。

関連情報

遅延確認と Nagle アルゴリズムの詳細については、以下を参照してください。

Braden, R.[1989], RFC 1122, Requirements for Internet Hosts--Communication Layers, Internet Engineering Task Force.