listen函数里面backlog的意义以及各种情况

先看了这篇:

http://www.cppblog.com/thisisbin/archive/2010/02/07/107444.html

里面说了会维护两个队列,established 和 syn_rcvd的。而back_log指定的长度是两个队列之和(乘以一个系数)

当客户端的第一个SYN到达的时候,TCP会在未完成队列中增加一个新的记录然后回复给客户端三路握手中的第二个分节(服务端的SYN和针对客户端的ACK),这条记录会在未完成队列中一直存在,直到三路握手中的最后一个分节到达,或者直到超时(Berkeley时间将这个超时定义为75秒)

如果当客户端SYN到达的时候队列已满,TCP将会忽略后续到达的SYN,但是不会给客户端发送RST信息,因为此时允许客户端重传SYN分节,如果返回错误信息,那么客户端将无法分清到底是服务端对应端口上没有相应应用程序还是服务端对应端口上队列已满这两种情况

http://blog.csdn.net/wangst4321/article/details/8733212

这篇文章给了一些系数的值:

不同backlog对应的队列长度

listen函数里面backlog的意义以及各种情况

系统为正等待连接请求的TCP服务程序,维护一个固定长度的连接队列。这个队列中的连接是已经被TCP接收(已经完成三次握手),但是还没有被应用程序接收(调用accept函数)

listen函数里面backlog的意义以及各种情况

Linux的情况

而第一篇文章的评论里面说了,上面这种情况是BSD的情况,Linux里面不是这样的。Linux里面是,这个backlog 就是established的数量,而syn_rcvd数量是另外通过

/proc/sys/net/ipv4/tcp_max_syn_backlog 设置的。具体可以看:

http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html

The interesting question is now how such an implementation behaves if the accept queue is full and a connection needs to be moved from the SYN queue to the accept queue, i.e. when the ACK packet of the 3-way handshake is received.

最关键的情况,是当established队列放不下,怎么办?

文中分析源代码,发现结论是,忽略。意思是当最后一个ack到来时候,本来应该把连接移动到established队列,但是满了,那么就忽略不移动;那么这个时候的情况对应于ack丢失,需要把三次握手里面的第二步(syn+ack)重发,之后又会有ack到来(注意会有超时次数)。而超过次数之后,server端就close了,client再发的话,会收到RST.

还有一个非常有意思的问题:三次握手的客户端,在发出最后一个ack的时候,状态已经是established了。那这个时候,它其实就开始发送数据了。

结果是:If it sends data (without waiting for data from the server first), then that data will be retransmitted as well. Fortunately TCP slow-start should limit the number of segments sent during this phase.

也对应了第二篇文章里面观察到的现象,就是发送的数据也一直得不到确认,而是需要不断重发。(Link

如下为第二篇文章的部分内容:

这里应该注意到客户端认为的连接状态是成功建立的,因为从客户端端口到服务器端口的连接都是处于ESTABLISHED的。这将导致客户端程序执行connect时是成功返回的,并继续下一步的动作,向服务器发送数据。

由于数据的发送是交给系统底层完成的,当客户端执行send时,数据将传送给系统底层。如果系统底层可以接收所有数据,则客户程序认为发送成功并返回。这点通过客户端执行结果,可以得到验证。所有客户端都是立即完成,并输出以下信息:
connect successfully.
sent byte: 1024
实际的情况是什么样的呢?通过命令“tcpdump -i lo”观察数据的传送。从以下数据可以看出,服务器程序实际上并没有与客户端程序建立连接,而且数据传输也没有真正完成。

总结

客户端connect成功返回时,并不表示与服务端的连接已经真正建立。Send发送数据成功返回也不表示服务器端已经成功接收了。编程时应该注意到这两点。

第二篇文章的内容(完)

回到第三篇文章:

On the other hand, if the client first waits for data from the server and the server never reduces the backlog, then the end result is that on the client side, the connection is in state ESTABLISHED, while on the server side, the connection is considered CLOSED. This means that we end up with a half-open connection!

另一个问题是,是否所有的SYN都会加到SYN的队列。Linux的做法是不会,因为ESTABLISHED队列满了,会导致SYN队列也满了,还不进来。

LInux将两个队列的阈值分开的好处,文中描述为如下:

The implementation in Linux effectively separates these two concerns:

the application is only responsible for tuning the backlog such that it can callaccept fast enough to avoid filling the accept queue);

a system administrator can then tune/proc/sys/net/ipv4/tcp_max_syn_backlog based on traffic characteristics.

(完)

上一篇:解Bug之路-记一次中间件导致的慢SQL排查过程


下一篇:ASP.NET Web API 中的异常处理(转载)