一篇文章彻底搞懂 TCP 三次握手四次挥手
Posted 无恙_z
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一篇文章彻底搞懂 TCP 三次握手四次挥手相关的知识,希望对你有一定的参考价值。
首先看TCP的报文格式:
三次握手
- 客户端要给服务端发送一个建立连接的请求(请求里有一个标志位
SYN
=1,并且会初始化一个32位的随机序列号seqA
) - 这时服务器就知道客户端要建立一个连接,然后就会回一个东西(有标志位
SYN
=1,随机生成一个32位的序列号sqeB
,ACK
=1,消息确认序号ack
包,ack
=seq
+1(这个操作是为了告诉服务端,下次发的报文需要从ack
开始)) - 客户端在收到服务端的消息后,需要确认下
ack
是否等于seqA
+1,是的话就立马给服务端发一个确认报文(ACK
=1,ack
=ackB
+1)
如果只建立两次链接可以吗?
当然不行。因为客户端给服务端发送得报文可能会因为网络延迟,导致报文过期,但网络正常后服务端依旧会收到这个报文。这时它就会给客户端响应连接,由于现在客户端并没有发出建立连接的请求,因此不会理睬服务端的确认,也不会向服务端发送数据。但服务端却以为新的运输连接已经建立,并一直等待客户端发来数据。这样,服务端的很多资源就白白浪费掉了。
其次,两次握手无法保证Client正确接收第二次握手的报文(Server无法确认Client是否收到),也无法保证Client和Server之间成功互换初始序列号。
第三次握手中,如果客户端的ACK未送达服务器,会怎样?
Server端
:
由于Server没有收到ACK确认,因此会重发之前的SYN+ACK(默认重发五次,之后自动关闭连接进入CLOSED状态),Client收到后会重新传ACK给Server。
Client端
,两种情况:
- 在Server进行超时重发的过程中,如果Client向服务器发送数据,数据头部的ACK是为1的,所以服务器收到数据之后会读取 ACK number,进入 establish 状态。
- 在Server进入CLOSED状态之后,如果Client向服务器发送数据,服务器会以RST包应答。
如果已经建立了连接,但客户端出现了故障怎么办?
服务器每收到一次客户端的请求后都会创建一个计时器,倒计时通常是设置为2小时。若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
四次挥手
即客户端想要断开连接
- 客户端给服务端发送一个报文(
FIN
=1,seqA
序列号) - 服务端收到后就会发送一个确认报文(
ACK
=1,ack
=seqA
+1),此时客户端已经没有要发送的数据了,但仍可以接受服务器发来的数据(即处于半关传输阶段)。 - 服务端会接着再发一个消息(
FIN
=1,seqB
序列号) - 客户端要收到了服务端的消息后,进入TIME_WAIT状态,并发一个消息确认包(
ACK
=1,ack
=seqB
+1)。服务器收到后,确认ack
包后,变为CLOSED状态,不再向客户端发送数据。客户端等待2*MSL(报文段最长寿命)时间后,也进入CLOSED状态。完成四次挥手。
它为什么要这么挥手?第二步跟第三步为什么不能合并在一起?
因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复ACK,表示接收到了断开连接的请求。等到数据发完之后再发FIN,断开服务器到客户端的数据传送。
如果第二次挥手时服务器的ACK没有送达客户端,会怎样?
客户端没有收到ACK确认,会重新发送FIN请求。
第四次挥手,客户端为什么要等待2MSL?(客户端TIME_WAIT状态的意义?)
第四次挥手时,客户端发送给服务器的ACK有可能丢失,TIME_WAIT状态就是用来重发可能丢失的ACK报文。如果Server没有收到ACK,就会重发FIN,如果Client在2*MSL的时间内收到了FIN,就会重新发送ACK并再次等待2MSL,防止Server没有收到ACK而不断重发FIN。
并且如果client直接closed,然后又向server发起了一个新连接,我们不能保证这个新连接和刚关闭的连接的端口号是不同的。假设新连接和已经关闭的老端口号是一样的,如果前一次滞留的某些数据仍然在网络中,这些延迟数据会在新连接建立后到达Server,所以socket就认为那个延迟的数据是属于新连接的,数据包就会发生混淆。所以client要在TIME_WAIT状态等待2倍的MSL,这样保证本次连接的所有数据都从网络中消失。
以上是关于一篇文章彻底搞懂 TCP 三次握手四次挥手的主要内容,如果未能解决你的问题,请参考以下文章