TCP建立连接与终止

首先先看下TCP通过三次握手建立连接和四次握手断开连接的流程图

TCP正常连接建立和终止所对应状态图

为了分析以上流程图, 通过wireshark抓包分析TCP连接web服务器并发送几个字符到服务器,服务器接收数据后返回非正常请求并主动断开连接的过程,抓包如下:

wireshark抓包分析

通过抓包分析可以清晰的看到TCP建立连接的三次握手阶段,正常数据收发阶段以及四次断开阶段,可以清晰的与状态图中对应起来。以下再放一张TCP的状态变迁图方便对TCP各个状态的理解:

TCP状态变迁图

TCP连接队列与linux内核源码

TCP建立连接就是通过TCP三次握手完成的,但是对应于TCP连接的建立服务端需要应用程序调用listen函数等待客户端发起SYN建立连接,这其中有一个TCP全连接队列(该队列表示已经建立TCP链接,队列大小由listen和net.core.somaxconn参数确定,同时内核参数tcp_max_syn_backlog表示处于SYNC_RECV的半连接状态队列可与此协同讨论),该队列中的连接表示已经建立TCP连接(即三次握手已经完成),但是还没有被应用层接受(accept表示阻塞从established状态的全连接队列中取出取出一个连接)。若队列已经满了,那么新的客户端发起TCP连接的SYN请求时,服务端将会忽略该请求或者发送RST给客户端(根据内核参数tcp_abort_on_overflow确定)。
以上有两个队列,半连接队列 (SYN队列)、全连接队列 (Accepted队列)。

半连接队列:当服务器每收到客户端的一个SYN,就会将该客户端放入未完成连接队列,而服务器套接口处于 SYN_RCVD 状态。
全连接队列:当客户端和服务器彻底完成三次握手过程,TCP连接将从半连接队列升级成全连接队列,并从半连接队列中清空该TCP连接,此时套接口处于ESTABLISHED 状态。

半连接队列的最大值由tcp_max_syn_backlog确定,全链接队列的最大值由listen参数和内核somaxconn确定,但是最终两个队列的队列大小需是内核经过计算后确定。

TCP半连接队列和全连接队列

以下是内核源码对应于应用层listen()函数的调用栈:

linux 3.16.81 内核源码TCP队列申请

通过以上分析可以看出listen() 中的入参 backlog 不仅影响到了全连接队列大小,还影响到了半连接队列。 通过以上调用栈可以确定TCP连接过程的全连接队列大小和半连接队列大小确定公式如下:

/*
backlog为listen函数调用时传入的参数。
net.core.somaxconn和net.ipv4.sysctl_max_syn_backlog对应内核参数
*/
accept全连接队列长度:
entries = min(backlog/*from listen()*/, net.core.somaxconn)

//半链接SNY队列长度,(内核函数reqsk_queue_alloc中)
entries = min(entries, net.ipv4.sysctl_max_syn_backlog)
entries = max(entries, 8) 
entries = roundup_pow_of_two(entries  + 1) 
//其中roundup_pow_of_two表示最大的2的指数幂,比如roundup_pow_of_two(9)=16 roundup_pow_of_two(7)=8)

附件:

TCP半连接队列最大值参数说明
TCP全连接队列最大值参数说明