ISN = M + F(localhost, localport, remotehost, remoteport).
M是一个计时器,这个计时器每隔4μs加1。因此 ISN 会在大约 4.55 小时循环一次。而一个tcp段在网络中的最长寿命(Maximum Segment Lifetime )默认是2分钟,所以我们可以认为 ISN 会是唯一的。
F是一个Hash函数,根据源IP、目的IP、源端口、目的端口生成一个随机数值。
(图片来源:http://www.cnxct.com/something-about-phpfpm-s-backlog/)
1、client执行系统调用connect()发送syn(初始序列号为seq=x,随机生成)至server以请求建立一个连接,此时client的状态变为SYN_SENT,并等待server的SYN+ACK。如果在一段时间后没有收到,则重新发送,默认次数是5(tcp_syn_retries)。大约是180s。重试次数完成后还没收到,则放弃此连接。
2、server收到syn后,检查SYN_RCVD半连接队列是否达到阈值(tcp_max_tcp_backlog),如果达到阈值,则丢弃此syn,不予响应。如果没有达到阈值,则将这个连接信息存入这个半连接队列,只要进入队列,server状态就会由listen()变为SYN-RCVD,同时向client发送ack+syn(seq=y,ack=x+1)。并等待client回复一个ack。如果一段时间后没有收到ack,则会重新发送syn+ack。发送次数默认是5(tcp_synack_retries)。重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s, 总共31s, 称为指数退避
,第5次发出后还要等32s才知道第5次也超时了,因此如果在1s + 2s + 4s+ 8s+ 16s + 32s = 63s后还没有收到cleint的ack回复,server会断开这个连接。
3、client接收到server发过来的syn+ack,进入ESTABLISHED状态(单方面认为)。同时向server发送ACK。server接收后,检查accept queue全连接队列是否达到阈值(取决于min(backlog, net.core.somaxconn) ,backlog是在socket创建的时候传入的,net.core.somaxconn是系统级别的一个端口能监听的最大连接数)。如果没满则进入队列,server由SYN_RECV变为accept状态,server端执行accept()系统调用,进入ESTABLISHED状态。如果满了,则按照tcp_abort_on_overflow的指示进行。如果值为0,那么server会丢掉client 发过来的ack(server端认为此时连接还没建立起来),此时客户端会显示read timeout。如果是1,server发送一个reset包给client,表示废掉这个握手过程和这个连接(本来在server端这个连接就还没建立起来)。客户端会显示connection reset by peer。
客户端状态变化路径:SYN_SENT ->ESTABLISHED
服务端状态变化路径:LISTEN->SYN_RECV ->ESTABLISHED
1、 client执行系统调用close(),向server发送FIN+seq,由ESTABLISHED进入FIN_WAIT_1状态
client--->server:我已经没有数据要发送了,你还有数据发送吗?
2、 server收到FIN后,向client发送ACK表示确认,由ESTABLISHED进入CLOSE_WAIT状态。client收到server的ACK后,进入FIN_WAIT_2状态。
server-->client:我知道你已经没有数据要发送了,但我还要再确认一下我是不是还有数据要给你。
3、server将未完成的数据传完后,server向client发送FIN,进入LAST_ACK状态。
server-->client:好了,这下我也没有东西要给你了,你可以终结连接了。此时服务器不确认客户端是否收到信息,继续保持连接。
4、client收到server的FIN后,向server发送ACK,同时进入TIME_WAIT状态,启动TIME_WAIT定时器,超时时间设为2MSL。
服务器端收到ACK,执行系统调用close(),进入CLOSED状态。客户端在2MSL时间内没收到对端的任何响应,TIME_WAIT超时,进入CLOSED状态。
在客户端的TIME_WAIT状态持续2MSL的时间后,如果客户端没有收到服务器发来的任何消息,则客户端会终结连接。如果最后一步客户端发给服务器的ack丢失,服务器会重发fin给客户端,然后客户端再重发最后的ack给服务器。注意如果客户端此时不处于time_wait的话,客户端会发送RST给server,server会将此包解释为一个错误(在java中会抛出connection reset的SocketException)
client-->server: 我知道了,你也终结连接吧
客户端状态:ESTABLISHED——FIN_WAIT_1——FIN_WAIT_2——TIME_WAIT——CLOSED
服务端状态:ESTABLISHED——CLOSE_WAIT——LAST_ACK——CLOSED