理解 listen 的第二个参数

Linux内核协议栈为一个tcp连接管理使用两个队列,分别是:
半连接队列:用来保存SYN_SENT和SYN_RECV两个状态的连接。也就是三次握手还没有完成,连接还没建立的连接。
全连接队列:用来保存保存ESTABLISHED状态的连接。三次握手已经完成的连接。

全连接队列存放三次握手成功的连接。如果当服务器不调用accept函数,没有将全连接队列的请求拿出来,当该队列满的时候,客户端的连接就无法再过来,而是存放在半连接队列中,所以当全连接满时,服务器会处于SYN_RECV状态。

注意:全队列的长度是listen的第二个参数+1

举个例子:全连接队列就像餐馆里的座位数目,处于全连接队列里的连接就像是正在用餐的客人,而一旦被accept,就相当于用餐结束,客人离开餐馆,座位数目+1。半连接队列像是在餐馆外面等待的人,当餐馆有座位时(全连接队列没满)就让排队的人进入餐馆坐着(把半连接队列里的连接放进全连接,当然他们的三次握手也随之完成,状态进入ESTABLISHED),座位数-1。
之所以要这样设计,是为了尽可能多地服务更多的客人,如果不设置半连接队列,座位满了,客人就直接走了,如果客人刚走就有空座位了,岂不是得不偿失?所以TCP为了尽可能多得利用资源,设计了半连接和全连接队列。当然排队的队列也不能太长,因为与其增加排队的长度,不如多加几张桌子(多创建几个进程/线程进行accept)。


使用代码验证

使用之前写的TCP代码,将listen的第二个参数改成1,意味着全连接队列最多有2个连接:
在这里插入图片描述

然后启动服务器,用四个客户端进行连接,发现有四个连接处于ESTABLISHED状态:
在这里插入图片描述

这是因为我们调用了accept,如果我们把accept代码注释掉,让服务器只listen不accept:
在这里插入图片描述

然后再用四个客户端连接服务器:
在这里插入图片描述

可以看到全连接队列有两个连接,半连接队列也有两个,如果此时第五个客户端也连接服务器,半连接队列就会+1:
在这里插入图片描述


从原码角度来看

在这里插入图片描述

为什么全队列的长度是listen的第二个参数+1

从上面的图可以看出来,内核在判定全连接队列为满时采用的是>而不是>=,我们listen的第二个参数为sk_max_ack_backlog表示全连接队列最大长度,只有当前全连接队列长度sk_ack_backlogsk_max_ack_backlog大时,才判定全连接队列已满。

在这里插入图片描述

Logo

更多推荐