Windows ソケット API プログラムを使用して TCP サーバーにデータをコピーすると、パフォーマンスが低下する
この記事では、Windows ソケット API プログラムを使用して TCP サーバーにデータをコピーするときにパフォーマンスが低下する問題の回避策について説明します。
元の KB 番号: 823764
現象
Windows ソケット API を使用するプログラムを実行すると、TCP サーバーにデータをコピーするときにパフォーマンスが低下する可能性があります。
Microsoft ネットワーク モニターなどのネットワーク スニファを使用してネットワーク トレースを作成した場合、TCP サーバーは、遅延受信確認タイマー (遅延 ACK タイマーとも呼ばれます) 内の TCP データ ストリーム内の最後の TCP セグメントに TCP ACK セグメントを送信します。 既定では、Windows オペレーティング システムの場合、このタイマーの値は 200 ミリ秒 (ミリ秒) です。 64 キロバイト (KB) のデータを送信する一般的なデータ フローは、次のシーケンスのようになります。
Client->Server 1460 バイト
Client->Server 1460 バイト
Server->Client ACK
Client->Server 1460 バイト
Client->Server 1460 バイト
Server->Client ACK
....
Client->Server 1460 バイト
Client->Server 1460 バイト
Server->Client ACK-PUSH
Client->Server 1296 バイト
-> 遅延 ACK 200 ミリ秒
原因
この問題は、Windows ソケット API とafd.sysのアーキテクチャの動作が原因で発生します。 この問題は、次のすべての条件に該当する場合に発生します。
Windows ソケット プログラムは、非ブロッキング ソケットを使用します。
単一の送信呼び出しまたは WSASend 呼び出しは、基になるソケット送信バッファー全体を満たします。
たとえば、プログラムは、Windows ソケット
setsockopt
関数を使用して、ソケットの初期化ルーチン中に既定のソケット送信バッファーを 32 KB に変更します。setsockopt( sock, SOL_SOCKET, 32768, (char *) &val, sizeof( int ));
その後、プログラムはデータを送信するときに、送信呼び出しまたは WSASend 呼び出しを発行し、各送信時に 64 KB のデータを送信します。
send(socket, pWrBuffer, 65536, 0);
このシナリオでは、プログラムが 64 KB のデータの送信呼び出しを発行するたびに、基になる 32 KB のソケット バッファーがいっぱいになった場合、プログラムはSOCKET_ERRORエラー コードを返します。 WSAGetLastError 関数を呼び出した後、プログラムは WSAEWOULDBLOCK エラー コードを受け取ります。 ほとんどのプログラムでは、Windows ソケット選択機能を使用してソケットの状態を確認します。 このシナリオでは、クライアントが未処理の TCP ACK セグメントを受信するまで、select 関数はソケットを書き込み可能として報告しません。 Windows 環境では、遅延受信確認アルゴリズムのため、既定で 200 ミリ秒かかる場合があります。
リモート TCP サーバーは、クライアントがプッシュ ビットが設定された最後の TCP セグメントを送信する前に、すべての TCP セグメントを確認します。
回避策
この問題を回避するには、次のいずれかの方法を使用します。
方法 1: ブロッキング ソケットを使用する
この問題は、非ブロッキング ソケットでのみ発生します。 ブロッキング ソケットを使用する場合、ソケット バッファーの処理方法afd.sys異なるため、この問題は発生しません。 ブロックおよび非ブロッキング ソケット プログラミングの詳細については、Microsoft Platform SDK のドキュメントを参照してください。
方法 2: ソケット送信バッファー サイズをプログラム送信バッファー サイズより大きくする
ソケット送信バッファーを変更するには、Windows Sockets getsockopt
関数を使用して現在のソケット送信バッファー サイズ (SO_SNDBUF) を決定し、 setsockopt
関数を使用してソケット送信バッファー サイズを設定します。 完了したら、SO_SNDBUF値は、プログラム送信バッファー サイズより少なくとも 1 バイト大きい必要があります。
SO_SNDBUF値より少なくとも 1 バイト小さいバッファー サイズを指定するように、送信呼び出しまたは WSASend 呼び出しを変更します。 この記事の「原因」セクションの前の例では、setsockopt 呼び出しを次の値に変更できます。
setsockopt( sock, SOL_SOCKET, 65537, (char *) &val, sizeof( int ));
または、送信呼び出しを次の値に変更できます。
send(socket, pWrBuffer, 32767, 0);
これらの値の任意の組み合わせを使用することもできます。
方法 3: TCP サーバーの TCP/IP 設定を変更する
重要
このセクション、方法、またはタスクには、レジストリの編集方法が記載されています。 レジストリを誤って変更すると、深刻な問題が発生することがあります。 したがって、次の手順を注意深く実行してください。 保護のために、レジストリを変更する前に、バックアップします。 その後、問題が起こった場合は、レジストリを復元できます。 レジストリをバックアップおよび復元する方法の詳細については、次の記事の番号をクリックして表示される Microsoft サポート技術情報の記事を参照してください。
322756 Windows でレジストリをバックアップおよび復元する方法
TCP サーバーの TCP/IP 設定を変更して、受信 TCP セグメントをすぐに確認します。 この回避策は、クライアント インストール ベースが大きく、プログラムの動作を変更できない環境で最適に動作します。 リモート TCP サーバーが Windows ベースのサーバーで実行されるシナリオでは、リモート サーバーのレジストリを変更する必要があります。 その他のオペレーティング システムについては、遅延受信確認タイマーを変更する方法については、オペレーティング システムのドキュメントを参照してください。
Windows 2000 を実行するサーバーで、次の手順に従います。
- 登録エディター (Regedit.exe) を起動します。
- 次のレジストリ サブキーを見つけてクリックします。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
- [ Edit ] メニューの [値の追加 ] をクリックし次のレジストリ値を作成します。
値の名前: TcpDelAckTicks
データ型: REG_DWORD
値のデータ: 0 - レジストリ エディターを終了します。
- この変更を有効にするには、Windows を再起動します。
Windows XP または Windows Server 2003 を実行するサーバーで、次の手順を実行します。
- レジストリ エディターを起動します。
- 次のレジストリ サブキーを見つけてクリックします。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
- [編集] メニューの [新規] をポイントし、[DWORD 値] をクリックします。
- 新しい値 TcpAckFrequencyに名前を付け、値 1 を割り当てます。
- レジストリ エディターを終了します。
- この変更を有効にするには、Windows を再起動します。
方法 4: 非ブロッキング ソケットのafd.sysのバッファリング動作を変更する
重要
このセクション、方法、またはタスクには、レジストリの編集方法が記載されています。 レジストリを誤って変更すると、深刻な問題が発生することがあります。 したがって、次の手順を注意深く実行してください。 保護のために、レジストリを変更する前に、バックアップします。 その後、問題が起こった場合は、レジストリを復元できます。 レジストリをバックアップおよび復元する方法の詳細については、マイクロソフト サポート技術情報の記事を表示するには、次の資料番号をクリックします。 322756 Windows でレジストリをバックアップおよび復元する方法
Note
このレジストリ キーは、Windows Server 2003 Service Pack 1 以降のサービス パックでのみ使用できます。
- [ 開始をクリックし、「 regedit.exe」と入力し、[ OK] をクリック。
- 次のレジストリ サブキーを見つけてクリックします。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
- [編集] メニューの [新規] をポイントし、[DWORD 値] をクリックします。
- 新しい値 NonBlockingSendSpecialBufferingに名前を付け、値 1 を割り当てます。
- Registry エディターを終了します。
- この変更を有効にするには、Windows を再起動します。
状態
マイクロソフトでは、この問題をこの資料の対象製品として記載されているマイクロソフト製品の問題として認識しています。
関連情報
328890 Windows XP および Windows Server 2003 で TCP 受信確認 (ACK) の動作を制御するための新しいレジストリ エントリ