在实现TCP通信的时候,在调用socket函数创建套接字以后,需要调用listen接口函数来将套接字设为监听状态,listen函数的第一个参数是文件描述符fd,第二个参数则是可以额外建立的连接数。
目录
一、验证第二个参数
我们的验证很简单,我们先将第二个参数设为1,即额外连接数是1,因此实际上允许建立的连接数为2(额外连接数+1)。下面沿用之前写好的TCP通信代码。
先启动服务端,然后多开三个窗口作为三个客户端。此时我们再开一个窗口输入 netstat -ntp 来查看连接状态。我们会发现,有一个连接处于SYN_RECV的状态,因为上层限制了连接建立成功的个数是 2 个,所以只会存在两个连接是ESTABLISHED状态。
二、全连接队列 和 半连接队列
实际上,连接状态即便是变为了ESTABLISHED,也不代表就能立马进行通信。同意连接是传输层的决策,但是上层此时可能忙于处理其他业务,无暇顾及,此时就会把状态为ESTABLISHED的连接放入“全连接队列”等待,等到上层调用accept的时候,就会从全连接队列中 取走连接并返回文件描述符。(全连接队列中的连接个数 = listen函数的第二个参数+1)
由于上层限制了每次可以建立成功连接的个数,因此溢出的连接只能停留在 SYN_RECV 状态,这些连接会进入 半连接队列 等待,等全连接队列中空出来一个位置,半连接队列中的某个连接就会进入全连接队列。(半连接队列中的连接个数是算法形成的,不同的OS可能不同)。
由此可以知道,
- 全连接队列(accpetd队列)(用来保存处于established状态,但是应用层没有调用accept取走的请求)
- 半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求)
三、为什么需要全连接队列?
以一家火锅店为例,这家店生意很火,每天座无虚席。
店家不在店外摆凳子,想进店的顾客都只能站在外面等,于是顾客不断流失。突然有一天,店里没客人了,巧的是这个时候店外也没有顾客想要进来,这种状况持续了很久。
自此店家开始在店外摆凳子,提供免费饮料,顾客很乐意在外面等。当店内有客人离席的时候,就通过叫号的方式招呼顾客进来。
店外队列 —— 全连接队列 店内 —— 上层服务
需要全连接队列的原因是,当有连接被释放的时候,就可以让新的连接进入上层,保证了上层的资源始终是被100%利用的。
四、为什么全连接队列不能太长?
全连接队列的长度是上层,也就是用户设置的,即listen接口函数的第二个参数。但是不能太长,维护队列需要成本,排在队尾的连接需要等待的时间可能也比较长,原本对方就可能比较急,等的时间太长会失去连接的意义。