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

时间:2011-03-15 14:30:39

标签: tcp buffer send nonblocking

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

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

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

我已经验证过,如果我在收到回复之前就停止上传没有完成,那么它就不会像上传那么快,然后服务器停滞......任何想法? Windows甚至会看这个设置吗?

3 个答案:

答案 0 :(得分:3)

Windows会查看此设置,但设置无法正常工作。 当您设置这些缓冲区的大小时,实际上是在设置正在与之通信的实际NIC上的缓冲区大小,从而确定要发送的数据包的大小。

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

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

我一直致力于类似的视频流和tcp通信项目,这些信息可以在MSDN论坛和technet上找到,但它需要一些非常详细的搜索,以确定它是如何实际工作的。

答案 1 :(得分:2)

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

根据http://support.microsoft.com/kb/214397

  

如果需要,Winsock可以缓冲大大超过SO_SNDBUF缓冲区大小。

这是有道理的;发送由本地机器上的程序启动,该程序被认为是合作的而不是敌对的。如果内核有足够的内存,则拒绝发送数据是没有意义的;有人必须缓冲它。 (接收缓冲区用于远程程序,可能是敌对的)

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

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

答案 2 :(得分:2)

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

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

例如:

  1. 将发送缓冲区设置为101字节
  2. 发送10个字节,它将被缓冲
  3. 再发送10个字节,它将被缓冲
  4. ...继续,直到缓冲区中有100个字节
  5. 再发送10个字节

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

  6. 再发送10个字节

  7. 最后一个肯定会阻塞,直到有一些缓冲区可用。

    因此,实质上,发送缓冲区很大,并且:

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

    对于模糊和缺乏联系感到抱歉;我有点急,但碰巧记得我前一段时间写过的网络产品中的这些细节。