設計上の問題 - Winsock と TCP 経由で小さなデータ セグメントを送信する

文書翻訳 文書翻訳
文書番号: 214397 - 対象製品
すべて展開する | すべて折りたたむ

目次

概要

TCP 経由で小さなデータ パケットを送信する必要がある場合、Winsock アプリケーションの設計が特に重要です。遅延確認応答の送受信、Nagle アルゴリズム、および Winsock バッファリングを考慮せずに設計すると、パフォーマンスに大きな影響が出ることがあります。この資料では、2 つのケース スタディを使用して設計上の問題点を説明し、Winsock アプリケーションから小さなデータ パケットを効果的に送信するための一連の推奨事項を説明します。

詳細

背景

Microsoft TCP スタックはデータ パケットを 1 つ受信すると、200 ミリ秒の遅延タイマを作動させます。最終的に ACK が送信されると、その遅延タイマがリセットされ、次のデータ パケットを受信したときに、新たに 200 ミリ秒の遅延タイマを作動させます。Microsoft TCP スタックでは、インターネット アプリケーションとイントラネット アプリケーションの両方の効率を高めるために、次の条件を使用して、受信したデータ パケットに対して、任意の ACK を送信するタイミングが決定されます。
  • 遅延タイマが経過する前に 2 つ目のデータ パケットを受信した場合は、ACK が送信されます。
  • 2 つ目のデータ パケットを受信する前に、ACK と同じ方向に送信されるデータが存在し、遅延タイマが経過すると、ACK はデータ セグメントに上乗せされて、直ちに送信されます。
  • 遅延タイマが経過すると、ACK が送信されます。
Microsoft TCP スタックでは、小さなデータ パケットによってネットワークが混雑しないように、デフォルトで Nagle アルゴリズムが有効になっています。Nagle アルゴリズムは、複数の送信呼び出しからの小さなデータ バッファを結合し、以前に送信されたデータ パケットに対する ACK がリモート ホストから受信されるまで、結合したデータ バッファの送信を遅延します。ただし、次の 2 つの場合は、Nagle アルゴリズムの例外となります。
  • スタックによって MTU (最大転送ユニット) よりも大きなデータ バッファが結合された場合は、リモート ホストからの ACK を待たずに、直ちにフル サイズのパケットが送信されます。イーサネット ネットワークの場合、TCP/IP の MTU は 1,460 バイトです。
  • TCP_NODELAY ソケット オプションが適用されて、Nagle アルゴリズムが無効にされている場合は、小さなデータ パケットが遅延されずにリモート ホストに配信されます。
Winsock では、アプリケーション層でのパフォーマンスを最適化するために、データ バッファがアプリケーション送信呼び出しから Winsock カーネル バッファにコピーされます。その後、スタックによって、(Nagle アルゴリズムなどの) スタック独自の経験則が使用され、実際に回線上にパケットを送り出すタイミングが決定されます。SO_SNDBUF オプションを使用することにより、ソケットに割り当てられる Winsock カーネル バッファの大きさを変更できます (この大きさは、デフォルトでは 8 KB です)。Winsock では、必要に応じて SO_SNDBUF で指定されたバッファ サイズよりもはるかに大きなデータをバッファリングできます。多くの場合、アプリケーションでの送信完了は、アプリケーション送信呼び出しのデータ バッファが Winsock カーネル バッファにコピーされたことだけを示し、実際にデータがネットワーク メディアに送り出されたかどうかは示しません。唯一の例外は、SO_SNDBUF を 0 に設定して Winsock バッファリングを無効にしたときです。

Winsock では、以下の規則を使用して、アプリケーションに送信完了を示します (完了通知は、ブロッキング呼び出しから返される関数、イベントを通知する関数、または通知関数を呼び出す関数などになる場合があり、これは送信が呼び出される方法によって異なります)。
  • ソケットが SO_SNDBUF クォータにまだある場合、Winsock によって、アプリケーション送信からデータがバッファにコピーされ、アプリケーションに送信完了が示されます。
  • ソケットが SO_SNDBUF クォータを超え、以前にバッファリングされた送信がスタック カーネル バッファにまだ 1 つしかない場合は、Winsock によって、アプリケーション送信からデータがバッファにコピーされ、アプリケーションに送信完了が示されます。
  • ソケットが SO_SNDBUF クォータを超え、以前にバッファリングされた送信がスタック カーネル バッファに複数存在する場合は、Winsock によって、アプリケーション送信からデータがバッファにコピーされます。スタックが SO_SNDBUF クォータ内にソケットを戻せるだけの送信を完了するか、未処理の送信状態が 1 つのみになるまで、Winsock からアプリケーションに送信完了が示されることはありません。

ケース スタディ 1

概要

Winsock TCP クライアントが、Winsock TCP サーバーに 10,000 件のレコードを送信し、データベースに格納する必要があるとします。レコードのサイズは、20 〜 100 バイト長まで変化します。アプリケーション ロジックを簡素化するために、設計を以下のようにします。
  • クライアントは、ブロッキング送信のみを行います。サーバーは、ブロッキング受信のみを行います。
  • クライアント ソケットは、各レコードが 1 つのデータ セグメントで送信されるように、SO_SNDBUF を 0 に設定します。
  • サーバーは、1 つのループ内で recv を呼び出します。recv に用意されるバッファは、各レコードが 1 回の recv 呼び出しで受信されるように、200 バイトにします。

パフォーマンス

クライアントがサーバーに送信できるレコードが 1 秒間に 5 つのみであることがテストで判明します。合計 10,000 件のレコード、つまり、最大で 976 KB のデータ (10,000 * 100 / 1,024) をサーバーに送信するには、30 分以上かかることになります。

分析

クライアントが TCP_NODELAY オプションを設定しないため、Nagle アルゴリズムによって、TCP スタックは回線上に新たなパケットを送信できるようになる前に、ACK を待つ必要があります。しかし、クライアントでは SO_SNDBUF オプションが 0 に設定されています。そのため、10,000 件の送信呼び出しは個別に送信され、個別に ACK を受信する必要があります。サーバーの 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 つの接続を確立した後、クライアントはコマンド チャネル経由でサーバーに銘柄記号を送信し、データ チャネル経由で株価情報が返信されるのを待ちます。クライアントは、最初の株価情報を受信した後にのみ、次の銘柄記号要求をサーバーに送信します。クライアントとサーバーは、SO_SNDBUF と TCP_NODELAY オプションを設定しません。

パフォーマンス

クライアントが 1 秒間に取得できる株価情報が 5 つのみであることがテストで判明します。

分析

このデザインでは、未処理の株価情報要求は一度に 1 つしか許可されません。最初の銘柄記号は、コマンド チャネル (接続) 経由でサーバーに送信され、応答がデータ チャネル (接続) 経由ですぐにサーバーからクライアントに送信されます。その後、クライアントがすぐに 2 つ目の銘柄記号要求を送信すると、送信呼び出しの要求バッファが Winsock カーネル バッファにコピーされるとすぐに、その送信は戻ります。しかし、コマンド チャネル経由の最初の送信の ACK を受信していないため、クライアント TCP スタックはカーネル バッファからすぐに要求を送信できません。サーバー コマンド チャネルで、200 ミリ秒の遅延タイマが経過した後、最初の記号要求に対する ACK がクライアントに戻ってきます。そして、200 ミリ秒の遅延の後、2 つ目の株価情報要求が正常にサーバーに送信されます。2 つ目の銘柄記号に対する株価情報が、データ チャネル経由ですぐに返信されます。これは、この時点でクライアント データ チャネルでの遅延タイマの有効期限が経過したためです。以前の株価情報応答に対する ACK がサーバーによって受信されます (クライアントは 2 つ目の株価情報要求を 200 ミリ秒間送信できないため、クライアント側の遅延タイマが経過するまでの時間を与えられ、サーバーに ACK が送信されます)。その結果、クライアントは 2 つ目の株価情報応答を取得して、新たな株価情報要求を発行できます。これ以降は、同じサイクルで処理が行われることになります。

パフォーマンスを向上させる方法

この設計では、2 つの接続 (チャネル) を使用する必要はありません。株価情報の要求と応答に対して接続を 1 つだけ使用すると、株価情報要求に対する ACK は株価情報応答に上乗せされ、すぐに戻ることができます。パフォーマンスをさらに向上させるためには、クライアントは複数の株価情報要求をサーバーへの 1 つの送信呼び出しに "多重化" し、サーバーも複数の株式情報応答をクライアントへの 1 つの送信呼び出しに "多重化" することができます。設計上何らかの理由で、単方向チャネルを 2 つ使用する必要がある場合は、以前のパケットに対する ACK を待たずに小さなパケットをすぐに送信できるように、双方が TCP_NODELAY オプションを設定する必要があります。

推奨事項

これらの 2 つのケース スタディは架空のものですが、最悪の事例を説明するために役立ちます。多数の小さなデータ セグメントの send と recv を含むアプリケーションを設計するときは、以下のガイドラインを考慮する必要があります。
  • データ セグメント送受信のタイミングが重要ではない場合は、アプリケーションでデータ セグメントをより大きなデータ ブロックに結合したうえで、送信呼び出しに渡す必要があります。多くの場合、送信バッファは Winsock カーネル バッファにコピーされるため、バッファがあまり大きくならないようにします。通常、8 KB よりやや小さなサイズが効果的です。Winsock カーネルは、MTU より大きなブロックを取得すると、複数のフル サイズのパケットと、残りのデータを含む最後のパケットを送信します。最後のパケットを送信する場合を除き、送信側は 200 ミリ秒の遅延タイマの影響を受けません。偶然、最後のパケットが奇数のパケットだった場合は、そのパケットは遅延確認応答アルゴリズムの影響を受けます。送信側のスタックが MTU より大きな別のブロックを取得した場合には、そのスタックは Nagle アルゴリズムを回避できます。
  • 可能な場合、データ フローが単方向のソケット接続を使用しないようにします。単方向ソケット経由の通信を使用すると、Nagle アルゴリズムと遅延確認応答アルゴリズムの影響を受けやすくなります。通信に要求と応答の送受信が必要な場合、応答に ACK が上乗せできるように、1 つのソケットを使用して send と recv の両方を行う必要があります。
  • 多数の小さなデータ セグメントを一度に送信する必要がある場合、送信側で TCP_NODELAY オプションを設定します。
  • Winsock によって送信完了が示されたときに、回線上に確実にパケットが送信されたことを保証する必要がある場合を除いて、SO_SNDBUF をゼロに設定することは避けます。実際、デフォルトの 8 KB バッファは、大部分の状況で正常に機能するように経験則によって決定されているもので、新しい Winsock バッファの設定でデフォルトの設定よりも優れたパフォーマンスが得られることがテスト済みでない限り、デフォルトの値を変更する必要はありません。また、SO_SNDBUF をゼロに設定することが最も適しているのは、データを一括転送するアプリケーションの場合です。その場合でも、効率を最高にするには、その設定は、二重バッファ処理 (任意の特定の時点で複数の未処理の send が存在する) およびオーバーラップした入出力と組み合わせて使用する必要があります。
  • データ配信を保証する必要がない場合は、UDP を使用します。

関連情報

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

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

関連情報

この資料は米国 Microsoft Corporation から提供されている Knowledge Base の Article ID 214397 (最終更新日 2005-07-11) を基に作成したものです。

プロパティ

文書番号: 214397 - 最終更新日: 2005年8月22日 - リビジョン: 3.1
この資料は以下の製品について記述したものです。
  • Microsoft Platform SDK January 2000 Edition
キーワード:?
kbdswnet2003swept kbapi kbinfo kbip kbnetwork kbwinsock KB214397
"Microsoft Knowledge Baseに含まれている情報は、いかなる保証もない現状ベースで提供されるものです。Microsoft Corporation及びその関連会社は、市場性および特定の目的への適合性を含めて、明示的にも黙示的にも、一切の保証をいたしません。さらに、Microsoft Corporation及びその関連会社は、本文書に含まれている情報の使用及び使用結果につき、正確性、真実性等、いかなる表明・保証も行ないません。Microsoft Corporation、その関連会社及びこれらの権限ある代理人による口頭または書面による一切の情報提供またはアドバイスは、保証を意味するものではなく、かつ上記免責条項の範囲を狭めるものではありません。Microsoft Corporation、その関連会社 及びこれらの者の供給者は、直接的、間接的、偶発的、結果的損害、逸失利益、懲罰的損害、または特別損害を含む全ての損害に対して、状況のいかんを問わず一切責任を負いません。(Microsoft Corporation、その関連会社 またはこれらの者の供給者がかかる損害の発生可能性を了知している場合を含みます。) 結果的損害または偶発的損害に対する責任の免除または制限を認めていない地域においては、上記制限が適用されない場合があります。なお、本文書においては、文書の体裁上の都合により製品名の表記において商標登録表示、その他の商標表示を省略している場合がありますので、予めご了解ください。"

フィードバック

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com