Skip to content

tcp三次握手和四次挥手

tcp是基于连接的可靠传输方式,主要依赖于三次握手、传输确认和四次挥手。

三次握手

三次握手是建立连接的过程。

  • 客户端发起SYN包。该包的SYN标识为1,且包含一个序列号SEQ,假定值为x,数据发送出去后,客户端就处于SYNC_SEND状态。
  • 服务端接收到后,回复一包SYN+ACK包。该包包含原客户端发送的SYN包的SEQ值x+1,以及自身的序列号SEQ值y。告诉客户端,我收到了SEQ为x的请求,准备接收连接了。服务端回复数据包后,自身就处于SYN_RCV状态。为了提高性能,服务端使用一个SYN队列 存储接收的请求。
  • 客户端接收到服务端回复的数据包后,返回一个ACK报文,包含服务端报文携带的SEQ值y+1。告诉服务端,y+1报文已经收到,且该返回的ACK报文可以携带数据。客户端返回ACK报文后,自身状态就转变为了TCP_ESTABLISHED
  • 服务端接收到客户端的ACK报文后,建立连接,服务端连接状态也变为TCP_ESTABLISHED。此时服务端会将连接请求从SYN队列中取出,放入ACCEPT队列,而应用程序进程则从accpet队列中获取已经建立完成的连接。

为什么是三次,不是两次或者四次

因为网络是不可靠的。tcp协议就是要在不可靠的信道下建立可靠的连接。

如果是2次握手,那么意味着服务端只接收了一次SYN报文就建立连接了。如果客户端没有收到服务端回复的ACK报文,那么就会重复的发送SYN报文,结果就是建立了大量不必要的连接,造成了资源浪费。而第三次握手,即客户端回复的ACK报文,则解决了这个问题,客户端重新发起连接请求后,前面的连接请求就作废了。服务端收不到前面请求的ACK包,自然不会建立连接。

四次握手当然也是可以的,但是如果不能提供更多的功能,那么四次握手增加了一次报文通信,也是造成了资源浪费。

传输确认

网络传输存在丢包和乱序问题。TCP协议是怎么解决的呢?

报文的发送端将要发送的数据放在数据缓冲区,并对数据进行编号。每个报文截取其中的一部分内容进行发送。发送报文中包含:序列号、长度和发送内容。

报文的接收方在收到报文后,需要回复确认报文ACK。确认报文包括:序列号+长度,也就是下一包数据需要发送的起始序列号。

如发送的第一个数据包,序列号为0,长度为100,发送内容为数据缓冲区的前100个字节。第二个数据包序列号为100,长度为100,发送内容为数据缓冲区的101到200个字节。

接收端收到数据包后,返回的ACK就为100,也就是第二个数据包的序列号。

通过接收端返回的ACK,发送端就可以知道哪些报文已经被接收端成功接收。

发送端可以一次性发送多个连续的数据包,接收端只要回复一次ACK即可。接收端根据序列号和长度,在接收到数据包后,重构出完整的数据报文。如果其中丢失了某些数据包,接收端可以通过ACK来要求发送端重新传输。

由于TCP连接是全双工的,不区分服务端和客户端,所以对于数据传输的两端来说均采用上述机制。

四次挥手

处于连接状态的客户端和服务端都可以发起关闭连接请求。

此处以客户端主动发起关闭请求为例

  • 客户端发送一个FIN数据包,表示要关闭连接,自身进入FIN-WAIT-1状态。这是第一次挥手。
  • 服务端收到FIN包,回复一个ACK包,自身进入CLOSE-WAIT状态。这是第二次挥手。客户端收到ACK包后,进入FIN-WAIT-2状态,此时服务端还可以发送数据,客户端还可以接收数据。
  • 待服务端发送完数据后,发送一个FIN包,自身进入LAST-AcK 状态。这是第三次挥手。
  • 客户端收到数据后,回复ACK包,进入TIME-WAIT状态,等待到超时时间过去后,关闭连接。服务端收到ACK后,立刻关闭连接。

为什么客户端需要TIME-WAIT 状态?

这是为了确保服务端接收到ACK报文。一但ACK包丢失,那么服务端将一直在等待确认状态(LAST-ACK)。在等待超时时间的过程中,如果服务端没有收到ACK包,那么就会重发FIN包,客户端会重新回复ACK包,并更新超时时间。

为什么挥手要4次?

因为通信的双方都需要在停止发送报文后,发送一个FIN包,并等待对方回复一个ACK包。

Published in基础知识

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *