Windows中套接字发送缓冲区的大小是多少?

时间:2015-02-28 19:03:12

标签: c++ windows sockets winapi tcp

根据我的理解,每个套接字都与两个缓冲区相关联,一个发送缓冲区和一个接收缓冲区,所以当我调用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阻塞套接字。
  • 我已连接到局域网机器。
  • 客户端Windows版本:Windows 7旗舰版64位。
  • 服务器Windows版本:Windows XP SP2 32位(安装在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:事实证明(感谢Harry Johnston)我以不正确的方式使用了setsockopt(),这就是它的使用方式:

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

将发送缓冲区设置为64 KB到130 KB之间的值不会使send()按预期工作,而是将发送缓冲区设置为{ {1}}阻止(这是我注意到的,我没有关于此行为的任何文档)。

所以现在我的问题是:我在哪里可以找到有关0(以及其他套接字操作)如何在Windows下工作的文档?

3 个答案:

答案 0 :(得分:15)

在调查此主题后。这就是我认为正确的答案:

调用send()时,可能会发生两件事:

  • 如果有待定数据低于SO_SNDBUF,那么send()将立即返回(无论您是发送5 KB还是发送500 MB都无关紧要。)< / p>

  • 如果有待处理的数据高于或等于SO_SNDBUF,那么send()将阻止,直到有足够的数据发送以将待处理数据恢复到SO_SNDBUF以下。< / p>

请注意,此行为仅适用于Windows套接字,而不适用于POSIX套接字。我认为POSIX套接字只使用一个固定大小的发送缓冲区(如果我错了,请纠正我)。


现在回到你的主要问题&#34; Windows中套接字发送缓冲区的大小是多少?&#34;。我想如果你有足够的内存,如果有必要可以超过1 GB(不知道最大限制是多少)。

答案 1 :(得分:5)

我可以重现这种行为,并且使用资源监视器很容易看到Windows在发生send()时确实分配了1GB的缓冲区空间。

一个有趣的功能是,如果您在第一个发送后立即执行第二次发送,则在两个发送完成之前,该呼叫不会返回。发送完成后,第一次发送的缓冲区空间将被释放,但第二次send()将继续阻塞,直到所有数据都已传输完毕。

我怀疑行为的不同是因为第一次发送完成后第二次调用send()已经阻塞了。 send()的第三次调用立即返回(并且分配了1GB的缓冲区空间),就像第一次调用一样,依此类推,交替进行。

所以我得出结论,问题的答案(“发送缓冲区有多大?”)是“与Windows认为合适的大”。结果是,为了避免耗尽系统内存,你应该将阻塞发送限制在不超过几百兆字节。

您对setsockopt()的调用不正确;第四个参数应该是一个指向整数的指针,而不是一个转换为指针的整数。一旦纠正,事实证明将缓冲区大小设置为零会导致send()始终阻塞。

总结一下,观察到的行为是send()将立即返回:

  • 有足够的内存来缓冲所有提供的数据
  • 目前尚未发送
  • 缓冲区大小未设置为零

否则,一旦数据发送,它将返回。

KB214397描述了一些 - 感谢汉斯!特别是它描述了将缓冲区大小设置为零会禁用Winsock缓冲,并且注释“如果需要,Winsock可以缓冲超过SO_SNDBUF缓冲区大小。”

(所描述的完成通知与观察到的行为并不完全匹配,具体取决于您如何解释“先前缓冲的发送”。但它已经接近了。)

请注意,除了无意中耗尽系统内存的风险之外,这一切都不重要。如果您真的需要知道另一端的代码是否已收到您的所有数据,那么唯一可行的方法就是告诉您。

答案 2 :(得分:0)

  

在阻塞套接字中,send()函数在提供给它的整个数据放入发送缓冲区之前不会返回。

这不能保证。如果有可用的缓冲区空间,但没有足够的空间容纳整个数据,那么套接字可以(并且通常会)接受它可以接收的任何数据而忽略其余的数据。 send()的返回值告诉您实际接受了多少字节。您必须再次致电send()以发送剩余数据。

  

那么发送缓冲区的大小是多少?

使用getsockopt()SO_SNDBUF选项查找。

使用setsockopt()SO_SNDBUF选项指定您自己的缓冲区大小。但是,套接字可能会对您指定的值施加最大上限。使用getsockopt()找出实际分配的大小。

相关问题