简体   繁体   English

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

[英]What is the size of a socket send buffer in Windows?

Based on my understanding, each socket is associated with two buffers, a send buffer and a receive buffer, so when I call the send() function, what happens is that the data to send will be placed into the send buffer, and it is the responsibility of Windows now to send the content of this send buffer to the other end. 根据我的理解,每个套接字都与两个缓冲区,一个发送缓冲区和一个接收缓冲区相关联,所以当我调用send()函数时,发生的事情是要发送的数据将放入发送缓冲区,它是Windows现在负责将此发送缓冲区的内容发送到另一端。

In a blocking socket, the send() function does not return until the entire data supplied to it has been placed into the send buffer. 在阻塞套接字中, send()函数在提供给它的整个数据放入发送缓冲区之前不会返回。

So what is the size of the send buffer? 那么发送缓冲区的大小是多少?

I performed the following test (sending 1 GB worth of data): 我执行了以下测试(发送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;
}

Output: 输出:

send() has returned
Return value: 1073741824
WSAGetLastError(): 0

send() has returned immediately, does this means that the send buffer has a size of at least 1 GB? send()已立即返回,这是否意味着发送缓冲区的大小至少为1 GB?

This is some information about the test: 这是关于测试的一些信息:

  • I am using a TCP blocking socket. 我正在使用TCP阻塞套接字。
  • I have connected to a LAN machine. 我已连接到局域网机器。
  • Client Windows version: Windows 7 Ultimate 64-bit. 客户端Windows版本:Windows 7旗舰版64位。
  • Server Windows version: Windows XP SP2 32-bit (installed on Virtual Box). 服务器Windows版本:Windows XP SP2 32位(安装在Virtual Box上)。

Edit: I have also attempted to connect to Google (173.194.116.18:80) and I got the same results. 编辑:我也尝试连接到谷歌(173.194.116.18:80),我得到了相同的结果。

Edit 2: I have discovered something strange, setting the send buffer to a value between 64 KB and 130 KB will make send() work as expected! 编辑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);

Edit 3: It turned out (thanks to Harry Johnston) that I have used setsockopt() in an incorrect way, this is how it is used: 编辑3:事实证明(感谢Harry Johnston)我以不正确的方式使用了setsockopt() ,这是它的使用方式:

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

Setting the send buffer to a value between 64 KB and 130 KB does not make send() work as expected, but rather setting the send buffer to 0 makes it block (this is what I noticed anyway, I don't have any documentation for this behavior). 将发送缓冲区设置为64 KB和130 KB之间的值不会使send()按预期工作, 而是将发送缓冲区设置为0使其阻塞(这是我注意到的,无论如何,我没有任何文档这个行为)。

So my question now is: where can I find a documentation on how send() (and maybe other socket operations) work under Windows? 所以现在我的问题是:我在哪里可以找到有关send() (以及其他套接字操作)如何在Windows下工作的文档?

After investigating on this subject. 在调查了这个问题之后。 This is what I believe to be the correct answer: 这就是我认为正确的答案:

When calling send() , there are two things that could happen: 调用send() ,可能会发生两件事:

  • If there are pending data which are below SO_SNDBUF , then send() would return immediately (and it does not matter whether you are sending 5 KB or you are sending 500 MB). 如果有待处理的数据低于SO_SNDBUF ,则send()将立即返回(无论您是发送5 KB还是发送500 MB都无关紧要)。

  • If there are pending data which are above or equal SO_SNDBUF , then send() would block until enough data has been sent to restore the pending data to below SO_SNDBUF . 如果有待处理的数据高于或等于SO_SNDBUF ,则send()将阻塞,直到发送了足够的数据将待处理数据恢复到SO_SNDBUF以下。

Note that this behavior is only applicable to Windows sockets, and not to POSIX sockets. 请注意,此行为仅适用于Windows套接字,而不适用于POSIX套接字。 I think that POSIX sockets only use one fixed sized send buffer (correct me if I'm wrong). 我认为POSIX套接字只使用一个固定大小的发送缓冲区(如果我错了,请纠正我)。


Now back to your main question "What is the size of a socket send buffer in Windows?". 现在回到主要问题“Windows中套接字发送缓冲区的大小是多少?”。 I guess if you have enough memory it could grow beyond 1 GB if necessary (not sure what is the maximum limit though). 我想如果你有足够的内存,如果有必要可以超过1 GB(不知道最大限制是多少)。

I can reproduce this behaviour, and using Resource Monitor it is easy to see that Windows does indeed allocate 1GB of buffer space when the send() occurs. 我可以重现这种行为,并且使用资源监视器很容易看到Windows在发生send()时确实分配了1GB的缓冲区空间。

An interesting feature is that if you do a second send immediately after the first one, that call does not return until both sends have completed. 一个有趣的功能是,如果您在第一个发送后立即执行第二次发送,则在两个发送完成之前,该呼叫不会返回。 The buffer space from the first send is released once that send has completed, but the second send() continues to block until all the data has been transferred. 发送完成后,第一次发送的缓冲区空间将被释放,但第二次send()将继续阻塞,直到所有数据都已传输完毕。

I suspect the difference in behaviour is because the second call to send() was already blocking when the first send completed. 我怀疑行为的不同是因为第一次发送完成后第二次调用send()已经阻塞了。 The third call to send() returns immediately (and 1GB of buffer space is allocated) just as the first one did, and so on, alternating. send()的第三次调用立即返回(并且分配了1GB的缓冲区空间),就像第一次调用一样,依此类推,交替进行。

So I conclude that the answer to the question ("how large are the send buffers?") is "as large as Windows sees fit". 所以我得出结论,问题的答案(“发送缓冲区有多大?”)是“与Windows认为合适的大”。 The upshot is that, in order to avoid exhausting the system memory, you should probably restrict blocking sends to no more than a few hundred megabytes. 结果是,为了避免耗尽系统内存,你应该将阻塞发送限制在不超过几百兆字节。

Your call to setsockopt() is incorrect; 你对setsockopt()的调用是不正确的; the fourth argument is supposed to be a pointer to an integer, not an integer converted to a pointer. 第四个参数应该是一个指向整数的指针,而不是一个转换为指针的整数。 Once this is corrected, it turns out that setting the buffer size to zero causes send() to always block. 一旦纠正,事实证明将缓冲区大小设置为零会导致send()始终阻塞。

To summarize, the observed behaviour is that send() will return immediately provided: 总而言之,观察到的行为是send()将立即返回提供:

  • there is enough memory to buffer all the provided data 有足够的内存来缓冲所有提供的数据
  • there is not a send already in progress 没有发送已经在进行中
  • the buffer size is not set to zero 缓冲区大小未设置为零

Otherwise, it will return once the data has been sent. 否则,它将在数据发送后返回。

KB214397 describes some of this - thanks Hans! KB214397描述了一些 - 感谢汉斯! In particular it describes that setting the buffer size to zero disables Winsock buffering, and comments that "If necessary, Winsock can buffer significantly more than the SO_SNDBUF buffer size." 特别是它描述了将缓冲区大小设置为零会禁用Winsock缓冲,并且注释“如果需要,Winsock可以缓冲超过SO_SNDBUF缓冲区大小”。

(The completion notification described does not quite match up to the observed behaviour, depending I guess on how you interpret "previously buffered send". But it's close.) (描述的完成通知与观察到的行为不完全匹配,这取决于我猜你如何解释“先前缓冲的发送”。但它已经接近了。)

Note that apart from the risk of inadvertently exhausting the system memory, none of this should matter. 请注意,除了无意中耗尽系统内存的风险之外,这一切都不重要。 If you really need to know whether the code at the other end has received all your data yet, the only reliable way to do that is to get it to tell you. 如果你真的需要知道另一端的代码是否已经收到了你的所有数据,那么唯一可行的方法就是让它告诉你。

In a blocking socket, the send() function does not return until the entire data supplied to it has been placed into the send buffer. 在阻塞套接字中,send()函数在提供给它的整个数据放入发送缓冲区之前不会返回。

That is not guaranteed. 这不能保证。 If there is available buffer space, but not enough space for the entire data, the socket can (and usually will) accept whatever data it can and ignore the rest. 如果有可用的缓冲区空间,但没有足够的空间容纳整个数据,那么套接字可以(并且通常会)接受它可以接收的任何数据而忽略其余的数据。 The return value of send() tells you how many bytes were actually accepted. send()的返回值告诉您实际接受了多少字节。 You have to call send() again to send the remaining data. 您必须再次调用send()来发送剩余数据。

So what is the size of the send buffer? 那么发送缓冲区的大小是多少?

Use getsockopt() with the SO_SNDBUF option to find out. 使用带有SO_SNDBUF选项的getsockopt()来查找。

Use setsockopt() with the SO_SNDBUF option to specify your own buffer size. 使用带有SO_SNDBUF选项的setsockopt()来指定您自己的缓冲区大小。 However, the socket may impose a max cap on the value you specify. 但是,套接字可能会对您指定的值施加最大上限。 Use getsockopt() to find out what size was actually assigned. 使用getsockopt()来找出实际分配的大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM