2016-09-01 38 views
3

我有一个奇怪的问题,选择在Linux上的套接字采取意想不到的长。选择在Linux上慢速插槽

  • 服务器接收数据的时候,接收套接字缓冲区大小为65536
  • 客户端发送数据的时候,发送套接字缓冲区大小是4096

一般来说,数据转移真的很快。但是:客户端中用于测试写入是否会阻塞的选择需要很长时间(发送数据时没有调用select before:0.5s,在实际发送数据之前发送数据与调用select相同:5s)。问题是特定于缓冲区大小。如果我增加客户端中的发送缓冲区来让我们说4 * 4096,问题就会消失。

现在我想知道为什么选择需要这么长的特定缓冲区大小。代码示例如下:http://pastebin.com/PqisLnLU

相同的代码可以在Windows上运行,甚至可以在没有这些奇怪行为的Windows Windows子系统上运行。

谢谢!

+0

“在客户端选择一个测试写入是否会阻塞的时间非常长(没有选择:0.5s,选择5s)” - 如何在没有选择的情况下进行选择...? – davmac

+0

谢谢,修正了这个问题。 – RobertK

回答

1

您将看到Nagle's algorithm的影响,该影响用于以延迟为代价提高TCP吞吐量。

写入相对较小,并且在不久将来写入更多数据的情况下被延迟,这些数据可能会被捆绑在一个IP数据包中。当您在发送之前使用select时,您不会发送更多(因为发送缓冲区仍然已满),因此在发送数据包(并清空缓冲区)之前存在大量延迟。相反,如果您不使用select缓冲区已​​满,则会立即通过网络堆栈进行分流,因为您的send更多。 (我猜你是在同一台机器上运行你的客户端和服务器程序,所以它们之间的“网络”连接实际上是回送接口,它具有相当高的MTU;否则,Nagle的算法可能会这里不成问题)。

当您充分增加缓冲区大小时,在填充缓冲区期间的某个点上会达到合适的IP数据包大小,并且数据会立即通过网络(并在收到确认时从发送缓冲区清除) - 所以没有延迟。

尝试禁用Nagle算法(客户端):

#include <netinet/tcp.h> 

... 

value = 1; 
if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(int))) 
{ 
    printf("\n Error : SetSockOpt TCP_NODELAY Failed \n"); 
} 

您将看到使用select的变化是那么一样快,因为没有select操作的变种。

+0

是的,这解决了这个问题,谢谢指出!但为什么在Windows上选择这么快(我想这很难回答,因为我们都没有Windows的来源)?在这两个系统上TCP_NODELAY是0. – RobertK

+0

@RobertK对,我不知道;这是一个不同的TCP/IP实现。一旦发送缓冲区已满,Windows可能会推出一个数据包,这是非常合理的行为。 – davmac

+0

@RobertK我应该补充一点,linux实际上设置的缓冲区大小是请求大小的两倍。如果你将缓冲区大小设置为2048(然后加倍),你会得到一个有趣的行为,即使用“select”或不使用总是(同样)慢。在这种情况下,我相信这是因为发送缓冲区只能在从对端收到确认数据包时才被清除;你迫使每个数据包都有一个往返延迟。 – davmac

1

我想知道,为什么选择这么久与特定的缓冲区大小

因为客户端的发送缓冲区太小。 4096字节小于我知道的任何操作系统的默认值。 Linux上的默认值比10年前大约为52k。除非发送者的套接字发送缓冲区大小与接收者的套接字接收缓冲区相差无几,否则您不可能“填满管道”。

我也会质疑你为什么在客户端使用非阻塞I/O和select()。除非你连接到多台服务器,否则没有任何意义。只需使用专用线程并让其阻塞即可。

+0

这不回答这个问题(这是*为什么*它需要这么长时间?) – immibis

+0

“你不可能'填充管道',除非你的发送者的套接字发送缓冲区与接收者的套接字接收缓冲区相差不大,我不知道什么样的误解会导致你暗示这是事实。 – davmac