根据我的理解,每个套接字都与两个缓冲区相关联,一个发送缓冲区和一个接收缓冲区,所以当我调用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?
这是关于测试的一些信息:
修改:我还尝试连接到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下工作的文档?
答案 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()
找出实际分配的大小。