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

21 c++ sockets windows 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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

send() has returned
Return value: 1073741824
WSAGetLastError(): 0
Run Code Online (Sandbox Code Playgroud)

send() 已立即返回,这是否意味着发送缓冲区的大小至少为1 GB?

这是关于测试的一些信息:

  • 我正在使用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);
Run Code Online (Sandbox Code Playgroud)

编辑3:事实证明(感谢Harry Johnston)我使用setsockopt()的方式不正确,这就是它的使用方法:

setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&send_buffer, send_buffer_sizeof);
Run Code Online (Sandbox Code Playgroud)

发送缓冲区设置为64 KB和130 KB之间的值不会使send()按预期方式工作,而是设置发送缓冲区,以0使它块(这是我注意到了,无论如何,我没有对这种行为的任何文件).

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

Tom*_*Tom 15

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

打电话时send(),有两件事情可能发生:

  • 如果没有等待在下面的数据SO_SNDBUF,然后send()会立即返回(和它没有关系是否正在发送5 KB或您要发送500 MB).

  • 如果存在大于或等于的待处理数据SO_SNDBUF,send()则将阻塞,直到已发送足够的数据以将待处理数据恢复到下面SO_SNDBUF.

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


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


Har*_*ton 5

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

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

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

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

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

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

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

否则,它将在数据发送后返回.

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

(描述的完成通知与观察到的行为不完全匹配,这取决于我猜你如何解释"先前缓冲的发送".但它已经接近了.)

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