上面说的listen是配置在nginx配置文件中的listen,实际底层执行时调用的就是底层的一个listen函数,listen函数是网络编程一个基本的函数,backlog就是它的参数。和TCP协议息息相关。
#创建一个套接口并监听申请的连接.
#include <sys/socket.h>
int listen( int sockfd, int backlog);
sockfd:用于标识一个已捆绑未连接套接口的描述字。
backlog:等待连接队列的最大长度。
listen函数仅由TCP服务器比如我们的nginx调用,如上面调用listen后会使用socket函数创建一个套接口时,它被假设为一个主动套装口,也就是说,它是一个将调用connet发起连接的客户套接口。listen函数把一个未连接的套接口转换成一个被动套接口,指示内核应接受指向该套接口的连接请求。即我们直观地说启用一个80端口,来监听80请求。根据TCP状态转换图,调用listen导致套接口从CLOSED状态转换到LISTEN状态。这个函数的第二个参数backlog规定了内核应该为这个套接口排队的最大连接个数。因此这里内核会为每一个给定的监听套接口维护两个队列:
1、SYN_RCVD 未完成连接队列(incomplete connection queue)即握手过程中的队列,每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接口处于SYN_RCVD状态。这些套接字处于SYN_RCVD状态的长度由/proc/sys/net/ipv4/tcp_max_syn_backlog设置
2、ESTABLISHED 已完成连接队列(completed connection queue)即已成功握手的队列,每个已完成TCP三路握手过程的客户对应其中一项。这些套接口处于ESTABLISHED状态。
当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后响应以三路握手的第二个分节:服务器的SYN响应,其中稍带对客户SYN的ACK(即SYN+ACK)。这一项一直保留在未完成连接队列中,直到三路握手的第三个分节(客户对服务器SYN的ACK)到达或者该项超时为止(曾经源自Berkeley的实现为这些未完成连接的项设置的超时值为75秒)。如果三路握手正常完成,该项就从未完成连接队列移到已完成连接队列的队尾。当进程调用accept时,已完成连接队列中的队头项将返回给进程,或者如果该队列为空,那么进程将被投入睡眠,直到TCP在该队列中放入一项才唤醒它。
上面的描述即是TCP握手的几个阶段:1收到客户端的syn请求 ->2将这个请求放入syn_table中去->3服务器端回复syn-ack->4收到客户端的ack->5放入已经握手成功的队列accept queue中
而这里listen函数里的backlog影响的就是是握手成功的队列的大小(即上面的accept queue队列的大小)。当客户链接请求大于这个个数(缓冲池满),其它的未进入链接缓冲池的客户端在tcp层上tcp模块会自动重新链接,直到超时。
注:有说在linux内核2.2版本以前,backlog是已完成队列和未完成连接队列之和(此项不知道)
nginx在调用底层listen函数时,传递的backlog参数即是在nginx配置文件中listen端口后面给的参数。而nginx定义的NGX_LISTEN_BACKLOG默认为511,系统中的默认值如下图:
可以使用命令查看当前的配置值大小: cat /proc/sys/net/ipv4/tcp_max_syn_backlog ,即是上图中后面这行的配置值,这个值比nginx的默认配置值大很多,所以nginx的默认配置值会让系统未发挥最大效力,因此以有必要调整这个值。这就是我们看到的在nginx配置文件中linsten 80 后面加上的这个backlog参数,且配置的参数不能超过内核里面设定的数值(超过了也许不生效,不确定是否会出异常)。