三次握手、四次挥手
一图总结
基本概念
消息类型 | 描述 |
---|---|
ACK | acknowledge 确认标志,为1表示确认号有效,为0表示报文中不包含确认信息,忽略确认号字段。帮助对方确认收到的SYN消息。 |
SYN | synchronize 请求同步标志,是建立tcp/ip建立连接的握手信号,用于建立连接过程。在连接请求中。 SYN=1 和 ACK=1。用来初始化和建立连接 |
FIN | Finally 结束标志,用于来断开连接。 |
seq | sequance 序列号。 |
ack | acknowledge 确认号。 |
三次握手
三次握手指建立一个TCP连接时,需要客户端和服务端之间共发送三个包。
它的目的是同步双方的序列号和确认号,交换TCP窗口大小信息。
在socket编程中,客户端执行connect()时,将触发三次握手。
刚开始客户端处于closed状态,服务器处于listen状态
-
第一次握手 客户端向服务器发送一个SYN报文,并指明客户端的的序列号seq。
此时客户端处于SYN_SEND
(同步发送)状态
首部的同步位SYN=1 ,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。 -
第二次握手 服务端收到SYN报文后,会发送自己的SYN/ACK的确认报文作为应答,即SYN=1,ACK=1,并且附带确认号ack=客户端的序号+1,和自己的序列号seq=y。 此时服务器端处于
SYN_RCEV
(同步接收)状态 在确认报文段中SYN=1 ACK=1,确认号ack=x+1,序号seq=y -
第三次握手 客户端收到SYN报文后,会发送一个带ACK的确认包,即ACK=1,ack=服务器的序号+1,附带自己的序列号seq=x+1。 确认报文段ACK=1,确认号ack=y+1,序号seq=x+1,ACK报文段可以携带数据,不携带数据则不消耗序号。 发送完毕后,客户端和服务器进入
ESTABLISHED
(已建立裂解)状态
为什么不是两次握手
原因:无法确定客户端的接收能力。
四次挥手
四次握手是指TCP断开连接,需要客户端和服务端共发送四个包。
客户端和服务端都可以主动发起挥手动作。
刚开始双方都处于ESTABLISHED
(已建立)状态。
- 第一次挥手
客户端发送FIN报文(FIN=1,seq=u),并停止再发送数据,主动关闭TCP连接.
结束报文中FIN=1,seq=u 此时客户端进入FIN_WAIT1
(终止等待1)状态,等待服务端的确认。 - 第二次挥手
服务器收到FIN之后,会发送ACK报文,表明自己已经收到客户端的报文。把客户端的序号值+1作为ACK报文的序列号值,附带服务端自己的序号值seq=v
确认报文中ACK=1,ack=u+1,seq=v
此时服务端处于 等待关闭
CLOSE_WAIT
状态。客户端收到后,进入FIN_WAIT2
(终止等待2)状态 - 第三次挥手 服务器端准备好关闭连接时,和客户端的第一次挥手一样,发送FIN/ACK报文,表示确认结束。 结束报文中FIN=1,ACK=1,seq=w,ack=u+1
- 第四次挥手 客户端收到 FIN 之后,对此发出ACK报文段。 此时客户端处于 TIME_WAIT(时间等待) 状态。 此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。而服务器只要收到了客户端发出的确认,立即进入 CLOSED 状态。所以服务器结束 TCP 连接的时间要比客户端早一些。 确认报文中ACK=1,seq=u+1,ack=w+1
等待2MSL的意义
如果不等待会怎样?
如果不等待,客户端直接跑路,当服务端还有很多数据包要给客户端发,且还在路上的时候,若客户端的端口此时刚好被新的应用占用,那么就接收到了无用数据包,造成数据包混乱。所以,最保险的做法是等服务器发来的数据包都死翘翘再启动新的应用。
那,照这样说一个 MSL 不就不够了吗,为什么要等待 2 MSL?
1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端 1 个 MSL 确保对端没有收到 ACK 重传的 FIN 报文可以到达 这就是等待 2MSL 的意义。
为什么是四次挥手而不是三次?
因为服务端在接收到FIN, 往往不会立即返回FIN, 必须等到服务端所有的报文都发送完毕了,才能发FIN。因此先发一个ACK表示已经收到客户端的FIN,延迟一段时间才发FIN。这就造成了四次挥手。
如果是三次挥手会有什么问题?
等于说服务端将ACK和FIN的发送合并为一次挥手,这个时候长时间的延迟可能会导致客户端误以为FIN没有到达客户端,从而让客户端不断的重发FIN。
详解:
https://zhuanlan.zhihu.com/p/86426969