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

基于我的理解,每个套接字都与两个缓冲区,一个发送缓冲区和一个接收缓冲区相关联,所以当我调用send()函数时,发生的事情是要发送的数据将被放入发送缓冲区,现在Windows的责任是把这个发送缓冲区的内容发送到另一端。

在一个阻塞套接字中, send()函数不会返回,直到提供给它的全部数据被放入发送缓冲区。

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

我进行了以下testing(发送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()立即返回,这是否意味着发送缓冲区的大小至less为1 GB?

这是关于testing的一些信息:

  • 我正在使用TCP阻塞套接字。
  • 我已连接到局域网机器。
  • 客户端Windows版本:Windows 7旗舰版64位。
  • 服务器Windows版本:Windows XP SP2 32位(安装在Virtual Box上)。

编辑:我也试图连接到谷歌(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:事实certificate(感谢哈里·约翰斯顿),我用了一个不正确的方式setsockopt() ,这是如何使用它:

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

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

所以我现在的问题是:我在哪里可以find一个关于send() (以及其他套接字操作)如何在Windows下工作的文档?

在调查了这个问题之后。 这是我认为是正确的答案:

当调用send() ,有两件事情可能发生:

  • 如果挂起的数据低于SO_SNDBUF ,那么send()会立即返回(不管你发送5 KB还是发送500 MB都没有关系)。

  • 如果存在大于或等于SO_SNDBUF未决数据,则send()会阻塞,直到发送足够的数据将未决数据恢复到SO_SNDBUF以下。

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


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

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

一个有趣的特点是,如果你在第一次发送之后立即进行第二次发送,那么这个呼叫在两个发送完成之前不会返回。 第一次发送的缓冲空间在发送完成后释放,但是第二次发送()继续阻塞,直到所有的数据都被传送完毕。

我怀疑行为上的差异是因为第二次调用send()在第一次发送完成时已经被阻塞了。 第三次调用send()立即返回(和1GB的缓冲区空间分配),就像第一次调用一样,等等,交替。

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

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

总而言之,观察到的行为是send()将立即返回:

  • 有足够的内存来缓冲所有提供的数据
  • 没有发送已经在进行中
  • 缓冲区大小不会设置为零

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

KB214397介绍了一些 – 感谢汉斯! 特别是它描述了将缓冲区大小设置为零将禁用Winsock缓冲,并且评论说:“如有必要,Winsock可以缓冲大于SO_SNDBUF缓冲区的大小。

(所描述的完成通知与所观察到的行为并不完全匹配,这取决于我如何解释“之前缓冲的发送”,但是接近)。

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

在一个阻塞套接字中,send()函数不会返回,直到提供给它的全部数据被放入发送缓冲区。

这是不能保证的。 如果有可用的缓冲空间,但是没有足够的空间来存放整个数据,套接字可以(通常会)接受任何可能的数据,而忽略其余的数据。 send()的返回值告诉你实际接受了多少个字节。 你必须再次调用send()来发送剩余的数据。

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

使用getsockopt()SO_SNDBUF选项来查找。

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