原文:http://blog.chinaunix.net/uid-29075379-id-3858844.html

int listen(int fd, int backlog);


有几个概念需要在开头澄清一下

TCP socket分两种,监听socket和传输socket两种

监听socket:负责处理网络上来的连接请求(客户端的syn包到达便是连接请求来了,如果不知道syn包,请参看一下TCP三次握手);

传输socket:负责在网络上的两个端点之间传输TCP数据。


未决socket:pending socket,就是某客户端的syn包到达,内核为这个syn包对应的tcp请求生成一个socket,但是此时三次握手并没有完成,这样的socket就是pending socket,是未决连接,没有经过三次握手认证的tcp连接。

已建立连接的socket:established socket,tcp服务器利用三次握手完成对客户端的简单认证之后,未决socket就变成已连接socket,后续可以用这个socket传输数据。

内核为每个tcp服务器维护两个socket队列:未决socket队列和已建立连接的socket队列

图 1


现在进入主题:)

在TCP服务器端创建socket完毕,调用listen函数的时候,系统下层发生了以下动作:

1. 将刚才创建的(fd所标示的)socket转换为此tcp服务器的监听socket,让此socket进入监听请求模式,此socket的tcp状态由CLOSE转至LISTEN.

2.内核为此监听socket所对应的tcp服务器建立一个未决socket队列和一个已建立连接socket队列

backlog这个参数用来决定未决socket队列的长度,有个映射关系,0表示长度可以无限大。


现在来串一串整个过程

监听socket收到某客户端的syn包,第一次握手完成;

然后内核为此syn请求生成一个pending socket,例如图1中的socket5,标记状态为SYN_RECV,并且将socket5加入相应的pending socket队列,并且服务器发出ack和syn,第二次握手完成。

后续针对此socket5有两种可能

1可能

过会儿客户端响应了服务器的syn(第三个ack到达),第三次握手结束。内核触发accept函数执行,将socket5状态标记为ESTABLISHED,并且将此socket5由pending socket queue移至establishedsocket queue,如图2

图2


2可能

客户端的最后一个ack并未来到,过很久,图1中的socket5超时了,被移除,如图3

图3


到这儿,listen的作用应该清楚了


另外针对此listen,有两个极限情况导致的拒绝服务情况需要考虑

backlog设置过小,pending socket队列已满,此时客户端调用connect发送syn分节给服务器端请求连接,服务端会忽略此syn包,客户端收不到syn的ack,会触发syn超时,这个超时时间比较长,重发syn,导致客户端长时间连接不上。

backlog设置为0或者过大,只要收到syn包,就会在pending socket队列中增加节点,这个容易导致物理内存耗尽

Syn flood就是***pending socket队列的


linux内核可以全局设置此pending socket队列大小

/proc/sys/net/ipv4/tcp_max_syn_backlog