在windows下的非阻塞TCP套接字上使用SO_SNDBUF的奇怪行为

在windows下的非阻塞TCP套接字上使用SO_SNDBUF的奇怪行为,tcp,buffer,send,nonblocking,Tcp,Buffer,Send,Nonblocking,我试图降低非阻塞TCP套接字上的发送缓冲区大小,以便可以正确显示上载进度条,但我看到一些奇怪的行为 我正在创建一个非阻塞TCP套接字,将SO_SNDBUF设置为1024,验证设置是否正确,然后连接(在调用之前和之后尝试此操作以连接,没有任何区别) 问题是,当我的应用程序实际运行并调用send(发送大约2MB)而不是返回发送的大约1024字节时,send调用显然会接受所有数据并返回2MB的发送值(正是我传入的值)。一切都正常运行(这是一个HTTP PUT,我得到了一个响应,等等),但我最终在我的进

我试图降低非阻塞TCP套接字上的发送缓冲区大小,以便可以正确显示上载进度条,但我看到一些奇怪的行为

我正在创建一个非阻塞TCP套接字,将SO_SNDBUF设置为1024,验证设置是否正确,然后连接(在调用之前和之后尝试此操作以连接,没有任何区别)

问题是,当我的应用程序实际运行并调用send(发送大约2MB)而不是返回发送的大约1024字节时,send调用显然会接受所有数据并返回2MB的发送值(正是我传入的值)。一切都正常运行(这是一个HTTP PUT,我得到了一个响应,等等),但我最终在我的进度条中显示的是上传在100%的状态下大约30秒,然后响应出现


我已经验证过,如果我在收到响应之前停止,上传不会完成,所以它不像是上传得很快,然后服务器就停止了。。。有什么想法吗?windows是否查看了此设置?

windows确实查看了此设置,但该设置未按预期工作。 在设置这些缓冲区的大小时,实际上是在设置与之通信的实际NIC上的缓冲区大小,从而确定传出的数据包的大小

关于Windows,您需要知道的是,在您的调用代码和实际NIC之间有一个缓冲区,我不确定您是否可以控制该缓冲区的大小。如果在套接字上调用Send操作时,您正在该套接字中转储数据,Windows内核将使用缓冲区中的数据在NIC上执行逐步发送,会发生什么情况

这意味着代码实际上会报告2MB being'sent',但这仅仅意味着2MB数据已经成功写入内部缓冲区,并不意味着/保证数据已经发送


我一直在从事视频流和tcp通信方面的类似项目,这些信息可以在MSDN论坛和technet上找到,但它需要对其实际工作原理进行一些非常详细的搜索。

我在Windows上观察到同样的情况,使用Java非阻塞通道

如有必要,Winsock的缓冲区可以大大超过SO_SNDBUF缓冲区大小

这是有道理的;发送是由本地机器上的程序发起的,该程序被认为是合作的,而不是敌对的。如果内核有足够的内存,就没有必要拒绝发送数据;总得有人来缓冲一下。(接收缓冲区用于远程程序,可能是恶意的)

内核对发送数据的缓冲有限制。我正在制作一个服务器套接字,内核每次发送最多接受128K;与您的示例中的2MB不同,2MB用于客户端套接字


同样根据同一篇文章,内核只发送缓冲区2;下一次非阻塞发送应立即返回报告已写入0字节。因此,如果我们每次只发送少量数据,程序将被接收端限制,您的进度指示器将正常工作。

该设置不会影响NIC上的任何内容;受影响的是内核缓冲区。发送和接收的默认值均为8k

您看到这种行为的原因是:发送缓冲区大小不是一次可以发送的数量的限制,而是“标称”缓冲区大小。它实际上只会在缓冲区中仍有数据等待发送时影响后续发送

例如:

  • 将发送缓冲区设置为101字节

  • 发送10个字节,它将被缓冲

  • 再发送10个字节,它将被缓冲

  • …继续,直到缓冲区中有100个字节

  • 再发送10个字节

    此时,WinSock使用一些逻辑来确定是接受新的10字节(并将缓冲区设为110字节)还是块。我不记得具体的行为,但它在MSDN上

  • 再发送10个字节

  • 最后一个肯定会阻塞,直到有缓冲区空间可用

    因此,本质上,发送缓冲区是相当大的,并且:

    • 当缓冲区为空时,WinSock将始终接受几乎任何大小的发送
    • 如果缓冲区有数据且写入将溢出,则有一些逻辑来确定是否接受/拒绝
    • 如果缓冲区已满或溢出,它将不接受新的发送

    抱歉的模糊和缺乏链接;我有点赶时间,但碰巧还记得我不久前写的一个网络产品中的这些细节。

    所以_SNDBUF是发送缓冲区的最大大小。。。试图用1KB的缓冲区同时发送2MB对我来说有点奇怪。您可能应该将发送缓冲区增加到2MB以上,或者使发送小于1kb。我认为您错误地认为,设置SO_SNDBUF会将2MB“削减”到1KB的段中,然后逐个发送,您需要自己完成。我的印象是,它应该限制随时可以排队的数据量,对吗。基本上,我只需要一个指示器,指示实际传输了多少数据(与排队相对)。缓冲区的大小与我无关,我只是想选择一些足够小的,可以看到一些粒度的东西。有更好的方法吗?HTTP没有任何内置的mid upload ACK afaik。是的,就是这样。如果您只能排队1kB,则无法发送2MB。为什么它假装发送了2MB是另一个问题。请记住,无论您做什么,TCP都可能在您背后很好地组合或拆分您的数据包(请参阅Nagle)。你应该自己分割你的发送,我想在你的情况下,10到50KB的大小就可以了。要查看上传过程,请使用Wireshark或类似Fiddler2的代理。