web-dev-qa-db-ja.com

Windowsのソケット送信バッファーのサイズは?

私の理解に基づいて、各ソケットは送信バッファーと受信バッファーの2つのバッファーに関連付けられているため、send()関数を呼び出すと、送信するデータが送信バッファーに配置されますこの送信バッファの内容をもう一方の端に送信するのは、現在Windowsの責任です。

ブロッキングソケットでは、send()関数は、供給されたデータ全体が送信バッファに配置されるまで戻りません。

では、送信バッファーのサイズはどのくらいですか?

次のテストを実行しました(1 GBのデータを送信します)。

_#include <stdio.h>

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#include <Windows.h>

int main()
{
    // Initialize Winsock
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);

    // Create socket
    SOCKET s = socket(AF_INET, SOCK_STREAM, 0);

    //----------------------

    // Connect to 192.168.1.7:12345
    sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("192.168.1.7");
    address.sin_port = htons(12345);
    connect(s, (sockaddr*)&address, sizeof(address));

    //----------------------

    // Create 1 GB buffer ("AAAAAA...A")
    char *buffer = new char[1073741824];
    memset(buffer, 0x41, 1073741824);

    // Send buffer
    int i = send(s, buffer, 1073741824, 0);

    printf("send() has returned\nReturn value: %d\nWSAGetLastError(): %d\n", i, WSAGetLastError());

    //----------------------

    getchar();
    return 0;
}
_

出力:

_send() has returned
Return value: 1073741824
WSAGetLastError(): 0
_

send()がすぐに返されました。これは、送信バッファーのサイズが少なくとも1 GBであることを意味しますか?

これはテストに関するいくつかの情報です:

  • TCPブロッキングソケットを使用しています。
  • LANマシンに接続しました。
  • クライアントWindowsバージョン:Windows 7 Ultimate 64ビット。
  • サーバーWindowsバージョン:Windows XP SP2 32-bit(Virtual Boxにインストール)。

編集:私もGoogle(173.194.116.18:80)に接続しようとしましたが、同じ結果が得られました。

編集2:奇妙な問題を発見しました。送信バッファーを64 KBから130 KBの値に設定すると、send()が期待どおりに機能します

_int send_buffer = 64 * 1024;    // 64 KB
int send_buffer_sizeof = sizeof(int);
setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)send_buffer, send_buffer_sizeof);
_

編集3:誤った方法でsetsockopt()を使用したことが判明しました(ハリージョンストンのおかげです)。これは次のように使用されます。

_setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&send_buffer, send_buffer_sizeof);
_

送信バッファーを64 KBから130 KBの間の値に設定するしないsend()を期待どおりに機能させるむしろ送信バッファーを_0_はそれをブロックします(これはとにかく気づいたことですが、この動作に関するドキュメントはありません)。

つまり、私の質問は次のとおりです。Windowsでのsend()(およびその他のソケット操作)の動作に関するドキュメントはどこにありますか?

21
user4344762

この問題について調査した後。これが正解だと私が信じているものです:

send()を呼び出すと、次の2つのことが起こります。

  • _SO_SNDBUF_以下の保留中のデータがある場合、send()はすぐに戻ります(5 KBを送信しているか、500 MBを送信しているかは関係ありません)。

  • _SO_SNDBUF_以上の保留中のデータがある場合、保留中のデータを_SO_SNDBUF_以下に復元するのに十分なデータが送信されるまで、send()はブロックします。

この動作はWindowsソケットにのみ適用され、POSIXソケットには適用されないことに注意してください。 POSIXソケットは、固定サイズの送信バッファーを1つだけ使用すると思います(間違っている場合は修正してください)。


次に、主な質問である「Windowsのソケット送信バッファーのサイズは?」に戻ります。十分なメモリがある場合は、必要に応じて1 GBを超える可能性があります(ただし、上限はわかりません)。

15
Tom

この動作を再現できます。リソースモニターを使用すると、send()が発生したときに、Windowsが実際に1GBのバッファースペースを割り当てていることが簡単にわかります。

興味深い機能は、最初の送信の直後に2番目の送信を行う場合、その呼び出しはbothの送信が完了するまで返らないということです。最初の送信からのバッファスペースは、その送信が完了すると解放されますが、2番目のsend()は、すべてのデータが転送されるまでブロックし続けます。

最初の送信が完了したときにsend()への2番目の呼び出しがすでにブロックされていたため、動作の違いが疑われます。 send()への3番目の呼び出しは、最初の呼び出しと同じように、すぐに戻ります(1GBのバッファースペースが割り当てられます)。

したがって、質問に対する答え(「送信バッファーの大きさは?」)は「Windowsが適切と考える限りの大きさ」であると結論付けます。要するに、システムメモリの枯渇を避けるために、ブロックする送信を数百メガバイト以下に制限する必要があるでしょう。

Setsockopt()の呼び出しが正しくありません。 4番目の引数は整数へのポインターであり、ポインターに変換された整数ではありません。これが修正されると、バッファサイズをゼロに設定すると、send()が常にブロックされることがわかります。

要約すると、観察された動作は、send()が即座に提供されることです:

  • 提供されたすべてのデータをバッファリングするのに十分なメモリがある
  • 進行中の送信はありません
  • バッファサイズがゼロに設定されていません

それ以外の場合は、データが送信されると戻ります。

KB214397 これの一部について説明します-ハンスに感謝します!特に、バッファサイズをゼロに設定するとWinsockバッファリングが無効になると説明されており、「必要に応じて、WinsockはSO_SNDBUFバッファサイズより大幅に多くバッファリングできる」とコメントしています。

(記述された完了通知は、「以前にバッファリングされた送信」をどのように解釈するかによって、実際の動作と完全には一致しません。しかし、それは近いです。)

不注意でシステムメモリが使い果たされるリスクを除いて、これは問題になりません。相手側のコードがすべてのデータをまだ受信しているかどうかを本当に知る必要がある場合、それを行う唯一の信頼できる方法は、それを通知することです。

5
Harry Johnston

ブロッキングソケットでは、send()関数は、供給されたデータ全体が送信バッファーに配置されるまで戻りません。

それは保証されません。利用可能なバッファスペースはあるが、データ全体に十分なスペースがない場合、ソケットは可能なデータをすべて受け入れ(通常は受け入れる)、残りは無視します。 send()の戻り値は、実際に受け入れられたバイト数を示します。残りのデータを送信するには、再度send()を呼び出す必要があります。

では、送信バッファーのサイズはどのくらいですか?

調べるには、_SO_SNDBUF_オプションを指定して getsockopt() を使用します。

setsockopt() と_SO_SNDBUF_オプションを使用して、独自のバッファサイズを指定します。ただし、ソケットは指定した値に最大キャップを課す場合があります。実際に割り当てられているサイズを確認するには、getsockopt()を使用します。

0
Remy Lebeau