1 三次握手
三次握手发生在客户端和服务器要建立TCP连接时,主要作用就是让客户端和服务器都知道自己和对方的发送、接收能力均正常,建立可靠连接
- 刚开始客户端处于Closed状态,服务器处于Listen状态
- 第一次握手:客户端向服务端发送一个SYN报文,初始化序列号ISN)。SYN=1的报文段不能携带数据,但要消耗掉一个序号。之后客户端处于SYN_SENT状态。此时头部报文中
SYN=1,seq=x
- 第二次握手:服务端接收到客户端的SYN报文之后,也会向客户端发送一个SYN报文作为应答,并指定客户端的
ISN+1
为自己的ACK值,再初始化ISN。之后服务端处于SYN_RCVD状态。此时应答报文中SYN=1,ACK=1,ack=x+1,seq=y
- 第三次握手:客户端收到服务端的应答报文后此时客户端变为 ESTABLISHED
状态,并再次向服务端发送一个SYN报文,也将服务端的ISN+1作为自己的ACK值,初始化ISN。此时SYN报文中
ACK=1,ack=y+1,seq=x+1(初始化为x,则第二个报文段需+1),因为
SYN!=1`,所以可以携带数据 - 至此,TCP三次握手完成
流程图如下:
总结:
- 第一次握手服务端知道了客户端的发送能力和自己的接受能力正常;
- 第二次握手客户端知道了自己的发送能力、接收能力正常,服务器的发送能力、接收能力正常;
- 第三次握手服务端知道了客户端的接受能力和自己的发送能力正常
2 为什么需要三次握手
- 二次握手会导致安全性,四次握手没必要
- TCP三次握手目的是让客户端和服务端都彼此直到自己和对方的接受、发送能力正常。如果使用2次握手,则只要服务端接收到客户端的建立连接请求,两端的TCP连接就建立好了。
- 如果客户端发给服务端的请求报文在传输过程中滞留,很长时间后才到达服务端,这个报文对于客户端来说已经是一个失效的报文了,但是服务端接收到了这个报文后就会误以为客户端想建立连接,所以就会向客户端发送确认报文,等到客户端发送数据。但是客户端并不会识别服务端发来的确认报文,也不会向服务端发送数据,所以就会让服务端一直等待,白白浪费资源。
3 半连接队列
- 服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列
- 全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象
4 四次挥手
TCP四次挥手用于客户端想要与服务端断开连接的时候
- 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。即发出连接释放报文段(
FIN=1,seq=u
),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认 - 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,此时服务端处于 CLOSE_WAIT 状态,TCP处于半关闭状态,客户端到服务端的连接释放。服务端发送的确认报文段(
ACK=1,ack=u+1,seq=v
)。当客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段 - 第三次挥手:当服务端也想断开连接时,给客户端发送 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK的状态。连接释放报文段(
FIN=1,ACK=1,ack=u+1,seq=w
) - 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,确认报文段(
ACK=1,seq=u+1,ack=w+1
)。此时客户端处于 TIME_WAIT 状态,等待2MSL后客户端进入CLOSE状态。等待服务端接收确认报文段后,关闭TCP连接,服务端进入CLOSED状态
流程图如下:
5 为什么需要四次挥手
因为当服务端收到客户端的FIN
报文后,需要发送ACK+FIN
报文。此时只是客户端数据发送完毕,需要关闭,但是服务端的数据可能没有发送完毕,所以就先只发送一个ACK应答报文,让客户端知道自己以及收到了它的FIN
请求,当自己发送完了数据后再发送FIN
报文
6 为什么需要等待2MSL
- 为了保证客户端发送的最后的ACK报文顺利到达服务端,双方都正常进入CLOSE状态。
- 如果客户端不等待2MSL就直接关闭,如果发送的ACK报文丢失,但是服务端一直没有收到ACK应答,就会一直重传FIN报文,但是此时客户端已经关闭,服务端就永远收不到ACK,就一直无法关闭
- MSL是一个报文最大的存活时间,客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段