TCP三次握手&四次挥手
参考:一篇文章带你熟悉 TCP/IP 协议 (opens new window)
https://juejin.cn/post/6844903510509633550?searchId=20231120234435449A3DCDE9A7242E5EA3#heading-13
# 三次握手
指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立;
socket编程中,这个过程由客户端执行connect来触发;
流程图:
第一次握手:客户端标志位SYN置1,随机初始化一个序列号client_isn=j,将数据包发送给服务端,客户端进入SYN_SENT状态,等待服务端确认;
第二次握手:服务端接收数据包后由标志位SYN=1得知客户端请求建立连接,服务端标志位SYN和ACK置1,确认应答号置为j+1,也随机初始化一个序列号server_isn=k,并将该数据包发送给客户端以确认连接请求,服务端进入SYN_RCVD状态;
第三次握手:客户端确认后,检查确认应答号是否为j+1,ACK是否为1,是则将确认应答号置为k+1,并将数据包发送给服务端,服务端检查正确后,都进入ESTABLISHED状态;
# 为什么不是两次、四次?
为什么不是两次?
阻止重复历史连接的初始化;
客户端连续多次发送SYN建立连接报文,在网络拥堵情况下:旧的SYN报文比新的SYN报文先到,客户端验证的服务端的确认应答号不正确就回RST报文终止连接,后续新到的SYN报文就可以正常完成三次握手;
如果两次握手,第一次握手就建立连接,导致服务端建立了一个历史连接,浪费服务端资源;
# 为什么初始化的序列号要求不一样?
防止历史报文被下一个相同四元组的连接接受;(上一个连接的数据包的序列号正好在服务端的接受窗口内)
# 第二次握手丢失了发生什么?
第二次握手:SYN+ACK报文;
包含了ACK报文, 如果丢失,客户端会认为第一次握手的SYN报文丢失了,客户端就会触发超时重传;
且因为丢失了SYN报文,服务端收不到客户端发送的ACK报文,也会触发超时重传机制,重传SYN-ACK报文;
# SYN攻击
攻击者短时间内伪造不同的IP地址的SYN报文, 服务端每收到一个SYN报文,就会进入SYN_RCVD状态,但服务端不知道客户端的IP,就会将服务端准备发送的ACK+SYN报文打满服务端的半连接队列;
解决:
增大半连接队列;
减少SYN+ACK重传次数;
# 四次挥手
指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开;
在socket编程中,这一过程由客户端或服务端任一方执行close来触发;
流程图:
第一次挥手:客户端发送一个FIN报文,关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1状态;
服务端还可以继续发送数据;
第二次挥手:服务端收到FIN报文后,先发送ACK应答报文,进入CLOSE_WAIT状态;
客户端进入FIN_WAIT_2状态,继续等待服务端FIN报文;
第三次挥手:服务端确定数据发送完成,向客户端发送FIN报文,服务端进入LAST_ACK状态;
第四次挥手:客户端收到FIN报文后,回一个ACK应答报文,进入TIME_WAIT状态;
服务端收到ACK报文后,进入CLOSE状态;
客户端经过2MSL后,自动进入CLOSE状态;
为什么需要经过2MSL?
客户端发送最后一个ACK报文,如果发送失败,服务端未收到可以重传,如果客户端等待了2MSL后未收到服务端未收到的回复,则证明服务端已正常关闭;
MSL:报文最大生存时间;
为什么设置2MSL?
发送方的数据包被接收方处理后又向对方发送ACK,一来一回需要等待2倍的时间;
# 出现大量TIME_WAIT状态原因?
说明服务端主动断开了很多TCP连接;
什么场景服务端会主动断开连接?
没有使用长连接;
长连接超时;
一直使用长连接不会占用资源?
web服务软件一般都会提供一个参数,指定HTTP长连接超时时间,比如nginx提供的keepalive_timeout参数;
长连接请求数达到上限;
# 保活机制
连接建立后,如果客户端宕机,服务端的TCP连接处于ESTABLISH状态,占用资源;
解决:
保活机制,每隔一个时间间隔,发送一个探测报文,如果连续几个探测报文没有得到响应,则认为当前TCP连接死亡;