TCP各个状态主要存在于三次握手和四次挥手的过程vim
一、TCP创建链接时的三次握手:服务器
服务端应用监听端口处于LISTEN状态,等待创建链接。cookie
第一次握手:客户端发送SYN=一个随机数,而后进入SYN_SENT状态。网络
第二次握手:服务端收到SYN后,向客户端回应ACK=随机数+1,同时发送SYN=k,而后进入SYN_RCVD状态。ssh
第三次握手:客户端收到ACK后进行验证,并回应SYN,返回ACK=k+1,而后进入ESTABLISHED状态,服务端收到该ACK后进行验证,而后也进入ESTABLISHED状态。socket
服务端和客户端都进入ESTABLISHED状态后,表示链接创建完成,能够传输数据。tcp
tcpdump抓包三次握手过程:优化
14:42:00.167658 IP 1.2.3.4.35646 > 192.168.1.223.ssh: Flags [S], seq 1012495606, win 29200, options [mss 1460,sackOK,TS val 3535567886 ecr 0,nop,wscale 7], length 0 14:42:00.167700 IP 192.168.1.223.ssh > 1.2.3.4.35646: Flags [S.], seq 1306417285, ack 1012495607, win 28960, options [mss 1460,sackOK,TS val 3639018129 ecr 3535567886,nop,wscale 7], length 0 14:42:00.169637 IP 1.2.3.4.35646 > 192.168.1.223.ssh: Flags [.], ack 1, win 229, options [nop,nop,TS val 3535567889 ecr 3639018129], length 0
二、TCP断开链接时的四次挥手:spa
TCP链接中的任一端均可以主动断开链接,应用中通常是客户端主动断开。code
第一次挥手:客户端向服务器端发送FIN,表示要断开链接(客户端再也不发送数据了),而后进入FIN_WAIT_1状态。
第二次挥手:服务端收到FIN后,回应ACK,而后进入CLOSE_WAIT状态。此时,服务端只能发送数据,不能接收数据。等服务端将缓冲区的数据发送完成后,向客户端发送FIN,而后进入LAST_ACK状态。
第三次挥手:客户端收到FIN的ACK后,进入FIN_WAIT_2状态,此时客户端没法再发送数据,只能接收数据。客户端收到服务端发来的FIN(表示服务端已将缓冲区的数据发送完成),向服务端回应ACK(确认客户端已经收到该FIN),而后客户端进入TIME_WAIT状态。
第四次挥手:服务端收到ACK后,也进入TIME_WAIT状态,客户端进入TIME_WAIT状态后,在等待等待2*MSL时间,进入CLOSED状态,链接关闭。
tcpdump抓包四次挥手过程:
14:42:19.284169 IP 1.2.3.4.ssh > 192.168.1.223.35646: Flags [F.], seq 41, ack 7, win 227, options [nop,nop,TS val 3639037245 ecr 3535587003], length 0 14:42:19.285929 IP 192.168.1.223.35646 > 1.2.3.4.ssh: Flags [.], ack 41, win 229, options [nop,nop,TS val 3535587005 ecr 3639037245], length 0 14:42:19.285986 IP 192.168.1.223.35646 > 1.2.3.4.ssh: Flags [F.], seq 7, ack 42, win 229, options [nop,nop,TS val 3535587005 ecr 3639037245], length 0 14:42:19.285998 IP 1.2.3.4.ssh >192.168.1.223.35646: Flags [.], ack 8, win 227, options [nop,nop,TS val 3639037247 ecr 3535587005], length 0
为啥创建链接的时候是三次握手,而关闭链接的时候是四次挥手呢???
答:服务端在收到客户端请求链接链接(SYN)的时候,能够将SYN和ACK一块儿发送(放到一个报文中发送)。
服务端在收到客户端发来的FIN请求关闭链接的时候,仅表示客户端已没有数据发送了,可是服务端可能还有没发送完的数据,因此在回应ACK以后,还要等到将全部的数据发送完毕后,再向客户端回应一个FIN,确承认以关闭链接。
关于MSL时间:
MSL是Maximum Segment Lifetime英文的缩写,中文能够译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。由于tcp报文(segment)是ip数据报(datagram)的数据部分,具体称谓请参见《数据在网络各层中的称呼》一文,而ip头中有一个TTL域,TTL是time to live的缩写,中文能够译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个ip数据报能够通过的最大路由数,每通过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。RFC 793中规定MSL为2分钟,实际应用中经常使用的是30秒,1分钟和2分钟等。 2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后能够再发一个ACK应答包。在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当链接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中能够经过设置SO_REUSEADDR选项达到没必要等待2MSL时间结束再使用此端口。 TTL与MSL是有关系的但不是简单的相等的关系,MSL要大于等于TTL。
查看系统中当前全部链接状态:
netstat -n|awk '/^tcp/{++S[$NF]}END{for (key in S) print key,S[key]}' TIME_WAIT 4976 CLOSE_WAIT 46 SYN_SENT 3 FIN_WAIT2 1 ESTABLISHED 405
发现TIME_WAIT状态仍是比较多的,能够经过调整内核参数优化:
#vim /etc/sysctl.conf net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少许SYN攻击,默认为0,表示关闭;net.ipv4.tcp_tw_reuse = 1 表示开启重用。容许将TIME_WAIT sockets从新用于新的TCP链接,默认为0,表示关闭;net.ipv4.tcp_tw_recycle = 1 表示开启TCP链接中TIME_WAIT sockets的快速回收,默认为0,表示关闭;net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间;