Windows下在非阻塞TCP套接字上使用SO_SNDBUF的奇怪行为
我正在尝试降低非阻塞 TCP 套接字上的发送缓冲区大小,以便可以正确显示上传进度条,但我看到了一些奇怪的行为。
我正在创建一个非阻塞 TCP 套接字,将 SO_SNDBUF 设置为 1024,验证设置是否正确,然后连接(在调用连接之前和之后尝试过此操作,没有区别)。
问题是,当我的应用程序实际出现并调用 send (发送大约 2MB)而不是返回发送了大约 1024 个字节时,发送调用显然接受所有数据并返回 2 MB 的发送值(正是我传入的值) )。一切都运行正常(这是一个 HTTP PUT,我收到响应等),但我最终在进度栏中显示的是上传处于 100% 大约 30 秒,然后响应传入。
我已经验证,如果我在获得响应之前停止上传未完成,因此它不像上传得非常快然后服务器停止了......有什么想法吗? Windows 会查看此设置吗?
I'm trying to lower the send buffer size on my non-blocking TCP socket so that I can properly display an upload progress bar but I'm seeing some strange behavior.
I am creating a non-blocking TCP socketed, setting SO_SNDBUF to 1024, verifying that is is set properly, then connecting (tried this before and after call to connect with no difference).
The problem is, when my app actually comes around and calls send (sending about 2MB) rather than returning that around 1024 bytes were sent, the send call apparently accepts all the data and returns a sent value of 2 MB (exactly what I passed in). Everything operates properly (this is an HTTP PUT and i get a response, etc) but what I end up displaying in my progress bar is the upload sitting at 100% for about 30 seconds then the response coming in.
I have verified that if I stop before getting the response the upload does not complete so it's not like it just uploaded really fast and then the server stalled... Any ideas? Does windows even look at this setting?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Windows 确实会查看此设置,但该设置并未按您的预期工作。
当您设置这些缓冲区的大小时,您实际上是在与您通信的实际 NIC 上设置缓冲区的大小,从而确定发出的数据包的大小。
关于 Windows,您需要了解的是,您的调用代码和实际 NIC 之间有一个缓冲区,我不确定您是否可以控制该缓冲区的大小。如果当您在套接字上调用 Send 操作时,您正在转储该套接字中的数据,并且 Windows 内核将使用缓冲区中的数据在 NIC 上执行小规模的逐步发送,会发生什么情况。
这意味着代码实际上会报告 2MB 已“发送”,但这仅意味着您的 2MB 数据已成功写入内部缓冲区,并不意味着/保证数据已经发送。
我一直致力于视频流和 TCP 通信方面的类似项目,这些信息可以在 MSDN 论坛和 technet 上找到,但需要对其实际工作原理进行一些非常详细的搜索。
Windows does look at this setting, but the setting is not working as you expect it to be.
When you're setting the size of those buffers, you're actually setting the size of the buffers on the actuall NIC you're communicating with, thus determining the size of the packets that are going out.
What you need to know about Windows, is that there is a buffer between your calling code and the actuall NIC, and I'm not sure that you can control the size of that. What happens if when you call the Send operation on your socket, you're dumping the data in that socket, and the Kernel of Windows will perform small step by step sends on the NIC using the data in the buffer.
This means that the code will actually report 2MB beeing 'sent', but this just means that your 2MB of data has been successfully written in the internal buffer, and it does not mean/guarantee that the data has already been sent.
I've been working on similar projects with video streaming and tcp communications, and this information is somewhere available on the MSDN Forums and technet, but it requires some really detailed searching on how it all actually works.
我在 Windows 上使用 Java 非阻塞通道观察到同样的情况。
根据 http://support.microsoft.com/kb/214397
这是有道理的;发送是由本地计算机上的程序发起的,该程序被认为是合作的而不是敌对的。如果内核有足够的内存,则没有必要拒绝发送数据;无论如何,必须有人缓冲它。 (接收缓冲区用于远程程序,这可能是敌对的)
内核对发送数据的缓冲确实有限制。我正在创建一个服务器套接字,内核每次发送最多接受 128K;不像你的例子中的 2MB 用于客户端套接字。
同样根据同一篇文章,内核仅发送缓冲区 2;下一个非阻塞发送应该立即返回报告写入的 0 字节。因此,如果我们每次只发送少量数据,程序将受到接收端的限制,并且您的进度指示器会很好地工作。
I observed the same thing on Windows, using Java non-blocking channel.
According to http://support.microsoft.com/kb/214397
This makes sense; the send is initiated by a program on local machine, which is presumed to be cooperative and not hostile. If kernel has enough memory, there's no point to reject the send data; some one must buffer it anyway. (Receive buffer is for the remote program, which may be hostile)
Kernel does have limits on this buffering of send data. I'm making a server socket, and kernel accepts at most 128K per send; not like 2MB in your example which is for a client socket.
Also according to the same article, kernel only buffer 2 sends; next non-blocking send should return immediately reporting 0 bytes written. So if we only send small amount of data each time, the program will be throttled by the receiving end, and your progress indicator would work nicely.
该设置不会影响 NIC 上的任何内容;受影响的是内核缓冲区。发送和接收的默认值为 8k。
您所看到的行为的原因是:发送缓冲区大小不是您一次可以发送的数量的限制,而是“标称”缓冲区大小。它实际上仅在缓冲区中仍有数据等待发送时影响后续发送。
例如:
将发送缓冲区设置为101字节
发送10字节,将被缓冲
再发送 10 个字节,将被缓冲
...继续,直到缓冲区中有 100 个字节
再发送 10 个字节
此时WinSock使用一些逻辑来确定是否接受新的10字节(并使缓冲区为110字节)或块。我不记得具体的行为,但它在 MSDN 上。
再发送 10 个字节
最后一个字节肯定会阻塞,直到有可用的缓冲区空间。
因此,本质上,发送缓冲区是相当大的,并且:
抱歉,内容含糊且缺少链接;我有点着急,但碰巧想起了我前段时间写的一个网络产品中的这些细节。
The setting does not affect anything on the NIC; it is the Kernel buffer that is affected. It defaults to 8k for both Send and Receive.
The reason for the behavior you are seeing is this: the send buffer size is NOT the limit of the amount you can sent at one time, it is the "nominal" buffer size. It really only affects subsequent sends when there is still data in the buffer waiting to be sent.
For example:
Set the send buffer to 101 bytes
Send 10 bytes, it will be buffered
Send 10 more bytes, it will be buffered
...continue until the buffer has 100 bytes in it
Send 10 more bytes
At this point WinSock uses some logic to determine whether to accept the new 10 bytes (and make the buffer 110 bytes) or block. I don't recall the behavior exactly but it is on MSDN.
Send 10 more bytes
This last one will definately block until some buffer space is available.
So, in essence, the send buffer is sizeable and:
Sorry for the vagueness and lack of links; I'm in a bit of a hurry but happened to remember these details from a network product I wrote a while back.