TCP的三次握手和四次挥手

813 阅读6分钟

TCP 三次握手

总流程图

Client->Server: SYN=1
Server->Client: SYN=1,ACK=1
Client->Server: ACK=1

第一次握手:Client -> Server

Client->Server: SYN=1,seq = x
  • 本次握手,主要是客户端向服务器发送建立连接请求,此时客户端无法确定服务端是否收到连接建立请求的报文,因此 TCP 连接还没有建立成功。
  • 在这个步骤之前,客户端处于 CLOSED 状态,当 Server 准备好接收客户端连接请求时,Server 变成 LISTEN 状态
  • 当 Client 发送完第一次握手的报文 SYN=1 时,自身进入 SYN-SEND 状态
  • 第一次握手会消耗客户端的一个序列号 seq

第二次握手:Server->Client

Client->Server: SYN=1;seq=x
Server->Client: SYN=1,ACK=1;ack=x+1,seq=y
  • 在第二次握手过程中,服务器端收到了客户端发来的连接请求报文,此时可以保证的是客户端的数据能够正常发往服务端,但是客户端依旧不知道服务端是否收到建立连接请求的报文,因此服务端应该继续给客户端一个响应,告知客户端,表示同意接收建立 TCP 连接
  • 在服务端同意了客户端的连接请求之后,会进入 SYN-RCVD 状态
  • 服务端发往客户端的时候也要消耗一个自身的序列号

第三次握手:Client->Server

Client->Server: SYN=1;seq=x
Server->Client: SYN=1,ACK=1;ack=x+1,seq=y
Client->Server: ACK=1;ack=y+1;seq=x+1
  • 在客户端收到服务端返回的同意建立连接的报文之后,客户端会发送一个确认收到的报文给服务端,告诉服务端,客户端收到同意建立连接的报文,发送完毕之后,客户端正式进入 ESTABLISHED 状态,表示客户端已经建立好了连接
  • 服务端在接收到客户端发送的 ACK 报文之后,也会进入 ESTABLISHED 状态,连接正式建立完毕,可以开始通信了

为什么要第三次握手?

  • 主要是为了防止已经失效的连接请求报文有发送给服务器,从而产生错误。如果在一开始客户端发送的连接请求报文因为网络原因而滞留了,未能即使到达服务端,那么因为长时间未收到服务端返回的响应报文,客户端会进行重发,而重发的报文又被服务端接收到了并且通过二次握手建立了连接,同时完成了信息传输后关闭了连接,结果滞留在网络中的第一次建立连接请求终于到达了服务端,此时服务端会以为客户端又要建立连接,因此又会向客户端响应,同时因为只有两次握手,双方又进入建立连接的状态,但是双方之间又没有任何数据通信,从而浪费资源。如果有三次握手,当服务端进行响应之后,没有收到客户端返回的确认报文,那么服务端就会知道客户端并没有和自己建立连接,那么便会释放这次连接。

四次挥手

总流程图

Note left of Client: 客户端关闭TCP连接
Client -> Server: FIN=1;seq=u
Server -> Client: ACK=1;ack=u+1,seq=v
Server --> Client: 数据传输
Note right of Server: 服务端挥手关闭TCP连接
Server -> Client: FIN=1;seq=w,ack=u+1
Client -> Server: ACK=1;seq=u+1;ack=w+1

第一次挥手

Note left of Client: 客户端关闭TCP连接
Client -> Server: FIN=1;seq=u
  • 在四次挥手之前,双方都在进行正常通信,都处于ESTABLISHED状态。当双方通信结束时,客户端要断开连接,此时客户端会发送第一次会挥手报文给服务端,同时自身进入FIN-WAIT-1状态

第二次挥手

Note left of Client: 客户端关闭TCP连接
Client -> Server: FIN=1;seq=u
Server -> Client: ACK=1;ack=u+1,seq=v
  • 服务端在收到客户端发来的断开连接报文之后,便知道了客户端要关闭当前TCP连接,因此会对该断开连接做出响应,告诉客户端,服务端知道客户端要关闭连接了。
  • 服务端在响应了客户端的FIN包之后,自身并不会立即断开连接,而是进入CLOSE-WAIT状态。此时Client到Server的通信已经断开了,Client是无法向Server发送数据了,但是服务端是可以继续向客户端发送数据的
  • 客户端在收到服务端发送过来的确认报文之后,进而进入FIN-WAIT-2状态

第三次挥手

Note left of Client: 客户端关闭TCP连接
Client -> Server: FIN=1;seq=u
Server -> Client: ACK=1;ack=u+1,seq=v
Server --> Client: 数据传输
Note right of Server: 服务端挥手关闭TCP连接
Server -> Client: FIN=1;seq=w,ack=u+1
  • 在第二次挥手之后,服务器会继续发送一些还未传输完毕的数据给客户端。等所有数据都传输完毕,本次TCP连接要关闭时,服务端也应该断开自己到客户端的连接。因此会主动向客户端发送第三次挥手报文。
  • 发送完第三次挥手报文之后,自身又进入LAST-ACK状态。

第四次挥手

Note left of Client: 客户端关闭TCP连接
Client -> Server: FIN=1;seq=u
Server -> Client: ACK=1;ack=u+1,seq=v
Server --> Client: 数据传输
Note right of Server: 服务端挥手关闭TCP连接
Server -> Client: FIN=1;seq=w,ack=u+1
Client -> Server: ACK=1;seq=u+1;ack=w+1
  • 客户端收到服务端发来的断开连接的报文,意味着服务端发往客户端的数据通道也要关闭,此时客户端便会返回一个ACK报文给服务端,之后自身进入TIME-WAIT状态,等待2MSL后自动进入CLOSED状态,TCP连接关闭。

为什么要等待2MSL后再进入CLOSED状态

  • 确保最后一个ACK报文被服务端接收到,因为如果服务端没有接收到客户端返回的ACK,那么服务端会认为自己发送的断开连接请求的报文(第三次挥手)丢失了,那么服务端会进行重发,而如果客户端再2MSL之内都没有收到服务端重发的包,就意味着服务端已经收到第四次挥手发送的ACK了,那么双方的连接便正常断开了
  • 在2MSL时间内,可以使得双方这次连接中的所有数据包都消失,不会影响其他连接。如果不等待,万一客户端直接断开连接后,又立马新建的TCP连接,且端口之类的连接信息都是一致的,那么就会因为没有等待2MSL,网络滞留的数据包便会在本次连接中发送给服务端,而发送过去的数据对本次连接而言是垃圾数据