TCP 初识(一)
什么是TCP?
TCP是面向连接的
,可靠的
,基于字节流
的传输层
通信协议。
面向连接
:一定是一对一
才能连接,不能像UDP协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的。可靠的
:无论网络链路中出现怎样的链路变化,TCP都可以保证一个报文一定能够到达接收端。字节流
:用户消息通过TCP协议传输时,消息可能会被操作系统分组
成多个的TCP报文,如果接收方程序如果不知道消息的边界
,是无法读出一个有效的用户消息的,并且TCP报文时有序的
,当前一个
TCP报文没有收到的时候,即使它先收到后面的TCP报文,那么也不能扔给应用层去处理,同时对重复
的TCP报文会自动丢弃。
网络分层(OSI模型与TCP/IP模型对比)
OSI模型的上面三层(应用层,表示层,回话层
)与TCP/IP模型的应用层
是面向用户应用的
,OSI模型的下面4层(传输层,网络层,数据链路层,物理层
)与TCP/IP模型的传输层,网络层,网络接口层是面向数据通信的
。
为什么需要TCP协议?TCP工作在那一层?
因为IP层是不可靠的
,它不保证网络包的交付,不保证网络包的按序交付,也不保证网络包中的数据的完整性。
如果需要保障网络数据包的可靠性,那么即需要由上层(传输层
)的TCP协议来负责。因为TCP是一个工作在传输层的可靠数据传输服务,它能确保接收端接收的网络包是无损坏,无间隔,非冗余和按序的
。
什么是TCP连接?
TCP连接就是,用于保证可靠性和流量控制维护的某些状态信息,这信息的组合,包含Socket,序列号和窗口大小称为连接
。
建立一个TCP连接时需要客户端与服务端达成上面三个信息的共识。
- Socket:由IP地址和端口号组成。
- 序列号:用来解决乱序的问题。
- 窗口大小:用来流量控制
TCP头格式有哪些?
序列号
:在建立连接时由计算机生成的随机数作为起始值,通过SYN包传给接收端主机,每发送一次数据,就累加
一次该数据字节数
的大小,用来解决网络包乱序的问题
。
确认应答号
:指下一次期望
收到的数据序列号,发送端收到这个确认应答以后可以认为在这个序号之前的数据都已经被正常接收,用来解决丢包的问题
。
控制位
:
* ACK
:该位为1时,确认应答
的字段变为有效,TCP规定除了最初建立连接时的SYN包之外该位必须设置为1.
* RST
: 该位为1时,表示TCP连接中出现异常必须强制断开连接。
* SYN
:该位为1时,表示希望建立连接后,并在其序列号
的字段进行序列号初始值的设定。
* FIN
: 该位为1时,表示今后不会再有数据发送,希望断开连接,当通信结束希望断开连接时,通信双方的主机之间就可以相互交换FIN为1的TCP段。
如何确认一个唯一的TCP连接?
TCP四元组可以唯一的确定一个连接。四元组的元素包括如下:
- 源地址
- 源端口
- 目的地址
- 目的端口
源地址和目的地址的字段(32位)是在IP头部中,作用是通过IP协议发送报文给对方主机。
源端口和目的端口字段(16位)是在TCP头部中,作用是告诉TCP协议应该把报文发给哪个进程。
UDP和TCP有什么区别吗?
UDP不提供复杂的控制机制,利用IP提供面向无连接通信服务
。
UDP协议头部只有8个字节(64位),UDP的头部格式如下:
- 目标和源端口:主要是告诉UDP协议应该把报文发给哪个进程。
- 包长度:该字段保存了UDP首部的长度跟数据的长度之和。
- 校验和:校验和是为了提供可靠的UDP首部和数据而设计,防止收到在网络传输中受损的UDP包。
TCP和UDP区别
-
连接
TCP连接时面向连接的传输协议,传输数据前先要建立连接。
UPD是不需要连接,即刻传输数据。 -
服务对象
TCP是一对一的两点服务,即一条连接只有两个端点。
UDP支持一对一,一对多,多对多的交互通信 -
可靠性
TCP是可靠交付数据的,数据可以无差别,不丢失,不重复,按序到达。
UDP是尽最大努力交付不保证可靠的交付数据。 -
拥塞控制,流量控制
TCP有拥塞控制和流量控制机制,保证数据传输的安全性
UDP则没有,即使网络非常拥堵了,也不影响UDP的发送速率。 -
首部开销
TCP首部长度较长,会有一定的开销,首部在没有使用选项字段时是20个字节,如果使用了选项字段会变长。
UDP首部只有8个字节,并且是固定不变的,开销较小。 -
传输方式
TCP是流失传输,没有边界,但保证顺序和可靠。
UDP是一个包一个包的发送,是有边界的,但可能会丢失包和乱序。 -
分片不同
TCP的数据大小如果大于MSS大小,则会在传输层进行分片,目标主机收到后,也同样在传输组装TCP数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
UDP的数据大小如果大于MTU大小,则会在IP进行分片,目标主机收到后,在IP层组装完数据,接着再传给传输层。
TCP三次握手
TCP是面向连接的协议,所以使用TCP前必须建立连接,而建立连接时通过三次握手来进行的。
-
一开始,客户端和服务端都处于CLOSE状态,先是服务端主动监听某个端口,处于LISTEN状态
-
客户端会随机初始化序号(client_isn),将此序号置于TCP首部的
序列号
字段中,同时把SYN标志位置为1,表示SYN报文,接着把第一个SYN报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于SYN-SENT状态。 -
服务端收到客户端SYN报文后,首先服务端也随机初始化自己的序号(server_isn),将此序列号填入TCP首部的
序列号
字段中,其次把TCP首部的确认应答号
填入client_isn + 1,接着把SYN和ACK标志位为1,表示向客户端发起连接和对客户端请求连接做出响应,最后把报文发送给客户端,该报文不包含应用数据,之后应用层数据,之后服务端处于SYN-RCVD状态。 -
客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文TCP首部ACK标志位 为1,其次
确认应答号
字段填入server_isn +1,最后把报文发送给服务端,这次报文可以携带客户端到服务端的数据,之后客户端处于ESTABLISHED状态。
可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的
。
一旦完成三次握手,双方都处于ESTABLISHED状态,此时连接就已经建立完成,客户端和服务端就可以相互发送数据了。
如果第一次握手丢失了,会怎么样?
当客户端想和服务端建立TCP连接的时候,首先第一个发的就是SYN报文,然后进入到SYN_SENT状态。
在这之后,如果客户端迟迟收不到服务端SYN-ACK(第二次握手),就会触发超时重传
机制,重传SYN报文,而且重传的SYN报文的序列号都是一样的
。
在Linux里,客户端的SYN报文最大重传次数
是由tcp_syn_retries内核参数控制
,这个参数是可以自定义的,默认是5.
通常,第一次超时重传是在1秒后,第二次超时重传是在2秒后,第三次超时重传是在4秒后,第四次超时重传是在8秒后,第五次是在超时重传16秒后,每次超时时间是上一次的2倍
。
如果第二次握手丢失了
当服务端收到客户端的第一次握手后,就会回SYN-ACK报文给客户端,这个就是第二次握手,此时服务端会进入SYN_RCVD状态。
第二次握手的SYN-ACK报文其实有两个目的:
- 第二次握手里的ACK,是对第一次握手的确认报文;
- 第二次握手里的SYN,是对服务端发起建立TCP连接的报文?
所以如果第二次握手丢失了,就会发生比较有意思的事情。
因为第二次握手报文里包含对客户端的第一次握手的ACK确认报文,所以,如果客户端迟迟没有收到第二次握手,那么客户端就觉得可能自己的SYN报文(第一次)丢失了,于是客户端就会触发超时重传机制,重传SYN报文
。
然后,因为第二次握手中包含服务端的SYN报文,所以当客户端客户端收到后,需要给服务端发送ACK确认报文(第三次握手),服务端才会认为该SYN报文被客户端收到了。
如果第二次握手丢失了,服务端就收不到第三次握手,于是服务端这边会触发超时重传机制,重传SYN-ACK报文
。
因此,当第二次握手丢失了,客户端和服务端都会重传
客户端会重传SYN报文,也就是第一次握手,最大重传次数由tcp_syn_retries内核参数决定
。服务端会重传SYN-ACK报文,也就是第二次握手,最大重传次数由TCP_synack_retries内核参数决定
。
具体过程:
-
如果tcp_syn_retrise 为1,当客户端超时重传1次,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到服务端的第二次握手(SYN-ACK报文),那么客户端就会断开连接
-
如果 tcp_synack_retries为2,当服务端超时重传2次SYN-ACK报文后,已经达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到客户端的第三次握手(ACK报文),那么服务端就会断开连接。
如果第三次握手丢失了
客户端收到服务端的SYN-ACK报文后,就会给服务端回一个ACK报文,也就是第三次握手此时客户端状态进入到ESTABLISH状态。
因为这个第三次握手的ACK是对第二次握手的SYN的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传SYN-ACK报文,直到收到第三次握手,或者达到最大重传次数。
注意,ACK报文时不会重传的,当ACK丢失了,就由对方重传对应的报文。
如果tcp_synack_retries为2,当服务端超时重传2次SYN-ACK报文后,已经达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到客户端的第三次握手(ACK报文),那么服务端就会断开连接。
TCP四次挥手
- 客户端打算关闭连接,此时会发送一个TCP首部FIN标志位被置为1的报文,也即FIN报文,之后客户端进入FIN_WAIT_1状态。
- 服务端收到该报文后,就向客户端发送ACK应答报文,接着服务端进入CLOSE_WAIT状态。
- 客户端收到服务端ACK应答报文后,之后进入FIN_WAIT_2状态。
- 等待服务端处理完数据后,也向客户端发送FIN报文,之后进入LAST_ACK状态。
- 客户端收到服务端的FIN报文后,回个ACK应答报文,之后进入TIME_WAIT状态
- 服务端收到了ACK应答报文后,就进入了CLOSE状态,至此服务端已经完成连接的关闭。
- 客户端在经过2MSL一段时间后,自动进入CLOSE状态,至此客户端也完成连接的关闭。
每个方向都需要一个FIN和ACK,因此通常称为 四次挥手。
需要注意的是: 主动关闭连接的,才有TIME_WAIT状态
。
为什么挥手需要四次?
- 关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。
- 服务端收到客户端的FIN报文时,先回一个ACK应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN报文给客户端来表示同意现在关闭连接。
服务端通常需要等待完成数据的发送和处理,所以服务端的ACK和FIN一般都会分开发送,因此是需要四次挥手。
但是在特定情况下,四次挥手是可以变成三次挥手的
。
如果第一次挥手丢失了
当客户端(主动关闭方)调用close函数后,就会向服务端发送FIN报文,试图与服务端断开连接,此时客户端的连接进入到FIN_WAIT状态。
正常情况下,如果能及时收到服务端(被动关闭方)的ACK,则会很快变为FIN_WAIT2状态。
如果第一次挥手丢失了,那么客户端迟迟收不到被动方的ACK的话,也就会触发超时重传机制,重传FIN报文,重传次数由 tcp_orphan_retries参数控制。
当客户端重传FIN报文的次数超过tcp_orphan_retries后,就不会发送FIN报文,则会在等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到第二次挥手,那么直接进入到close状态。
如果tcp_orphan_retries为3,当客户端超时重传3次FIN报文后,已经达到了最大重传次数,于是再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到服务端的第二次挥手(ACK报文),那么客户端就会断开连接。
如果第二次挥手丢失了
当服务端收到客户端的第一次挥手后,就会先回一个ACK确认报文,此时服务端的连接进入到CLOSE_WAIT状态。
ACK报文不会重传的
,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传FIN报文,直到收到服务端的第二次挥手,或者达到最大的重传次数。
-
当客户端超时重传2次FIN报文后,由于tcp_orphan_retries为2,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到服务端的第二次挥手(ACK报文),那么客户端就会断开连接。
-
这里提一下,当客户端收到第二次挥手,也就是收到服务端发送的ACK报文后,客户端就会处于FIN_WAIT_2状态,在这个状态需要等服务器发送第三次挥手,也就是服务端的FIN报文。
-
对于CLOSE函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT_2状态不可以持续太久,而tcp_fin_timeout控制了这个状态下连接的持续时长,默认值是60秒。
-
意味着对于调用close关闭的连接,如果在60秒后还没有收到FIN报文,客户端(主动关闭方)的连接就会直接关闭。
如果第三次挥手丢失了
当服务端(被动关闭方)收到客户端(主动关闭方)的FIN报文后,内核会自动回复ACK,同时连接处于CLOSE_WAIT状态,顾名思义,它表示等待应用进程调用close函数关闭连接。
此时,内核是没有权利替代进程关闭连接,必须由进程主动调用close函数来触发服务端发送FIN报文。
服务端处于CLOSE_WAIT状态时,调用了close函数,内核就会发出FIN报文,同时连接进入LAST_ACK状态,等待客户端返回ACK来确认连接关闭。
如果迟迟收不到这个ACK,服务端就会重发FIN报文,重发次数仍然由tcp_orphan_retries参数控制,这与客户端发FIN报文重传次数控制方式是一样的。
假设 tcp_orphan_retries = 3,当第三次挥手时,发生的过程如下:
-
当服务端重传第三次挥手报文的次数达到3次后,由于tcp_orphan_retries 为3,达到了重传最大次数,于是再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到客户端的第四次挥手(ACK报文),那么服务端就会断开连接。
-
客户端因为是通过CLOSE函数关闭的,处于FIN_WAIT_2状态是有时长限制,如果tcp_fin_timeout时间内还是没能收到服务端的第三次挥手(FIN报文),那么客户端就会断开连接。
第四次挥手丢失了,会发生什么
当客户端收到服务端的第三次挥手的FIN报文后,就会回ACK报文,也就是第四次挥手,此时客户端连接进入TIME_WAIT状态。
在Linux系统,TIME_WAIT状态会持续2MSL后进入关闭状态
如果第四次挥手的ACK报文没有达到服务端,服务端就会重发FIN报文,重发次数仍然由 tcp_orphan_retries参数控制。
假设 tcp_orphan_retries 为2,当第四次挥手一直丢失时,发生的过程如下:
-
当服务端重传第三次挥手次数达到 2 时,由于 tcp_orphan_retries 为2,达到了最大重传次数,于是再等待一段时间(时间为上一次超时时间的2倍),如果还是没能收到客户端的第四次(ACK 报文),那么服务端就会断开连接。
-
客户端在收到 第三次挥手后,就会进入TIME_WAIT状态,开启时长为2MSL的定时器,如果途中再次收到第三次挥手(FIN报文后)就会重置定时器,当当等待2MSL时长后,客户端就会断开连接。
为什么是三次握手?不是两次,四次?
常回答的是:“因为三次握手才能保证双方具有接收和发送的能力”。
在前面我们知道了什么是TCP连接
:
* 用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket,序列号和窗口大小称为连接。
所以,重要的是为什么三次握手可以初始化Socket,序列号和窗口大小并建立TCP连接。
接下来,以三个方面分析三次握手的原因:
三次握手才可以阻止重复历史连接的初始化(主要原因)
- 三次握手才可以同步双方的初始序列号
- 三次握手才可以避免资源浪费
原因一:避免历史连接
简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。
我们考虑一个场景,客户端先发送了SYN(seq =90)报文,然后客户端宕机了,而且这个SYN报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向服务端建立连接,发送了SYN(seq=100),(注意! 不是重传SYN,重传的SYN的序列号是一样的
)。
看看三次握手是如何阻止历史连接的:
客户端连续发送多次SYN(都是同一个四元组)建立连接的报文,在网络拥堵
情况下:
- 一个【旧的SYN报文】比【最新的SYN】报文早到达了服务端,此时服务端就会回一个SYN+ACK 报文给客户端,这次报文中的确认号是91(90+1)
- 客户端收到后,发现自己期望收到的确认号应该是100+1,而不是90+1,于是就回RST报文
- 服务端收到RST报文后,就会释放连接。
- 后续最新的SYN抵达了服务端后,客户端与服务端就可以正常的完成三次握手了。
上述中【旧的SYN报文】称为历史连接,TCP使用三次握手建立连接的最主要原因就是防止【历史连接】初始化了连接
。
考虑一下,如果服务端在收到RST报文之前,先收到【新SYN报文】,也就是服务端收到客户端的顺序是 : 【旧SYN报文】--》【新SYN报文】,此时会发生什么?
当服务端第一次收到SYN报文,也就是收到[旧SYN报文]时,就回复SYN + ACK报文给客户端,此报文的确认号是 91(90+1)。
然后这时再收到【新SYN报文】时,就会回 Challenge ACK 报文给客户端,这个ACK报文并不是确认收到【新SYN报文】的,而是上一次的ACK确认号
,也就是91(90 +1),所以客户端收到此ACK报文时,发现自己期望收到确认号应该是101,而不是91,于是就回RST报文。
如果是两次握手连接,就无法阻止历史连接
。那为什么TCP两次握手为什么无法阻止历史连接呢?
主要是在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立一个历史连接,造成资源浪费
。
在两次握手的情况下,服务端在收到SYN报文后,就进入ESTABLISHED状态,意味着这时可以给对方发送数据,但是客户端此时还没有进入ESTABLISHED状态,假设这次是历史连接,客户端判断到此次连接为历史连接,那么就会回RST报文来断开连接,而服务端在第一次握手就进入ESTABLISHED状态,所以它可以发送数据,但是它不知道这个是历史连接,它只有在收到RST报文后,才会断开连接。
可以看到,如果采用两次握手建立TCP连接的场景下,服务端在向客户端发送数据前,并没有阻止掉历史连接,导致服务端建立了一个 历史连接,有白白发送了数据,妥妥地浪费服务端的资源。
要解决这种形象,最好就是在服务端发送数据前,也就是建立连接前,要阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次挥手
。
所以,TCP使用三次握手建立连接的最主要原因是防止【历史连接】初始化了连接
。
原因二:同步双方初始化序列号
TCP协议的通信双方,都必须维护一个【序列号】,序列号是可靠传输的一个关键因素,它的作用:
-
接收方可以去除重复的数据
-
接收方可以根据数据包的序列号按序接收
-
可以标识发送出去的数据中,哪些是已经被对方收到(通过ACK报文中的序列号知道);
序列号在TCP连接中占据着非常重要的作用,所以当客户端发送携带[初始序列号]的SYN报文的时候,需要服务端回一个ACK应答报文,表示客户端的SYN报文已经被服务端成功接收,那当服务端发送【初始序列号】给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠同步
。
四次握手其实也能够可靠的同步双方的初始化序列号,但由于第二步和第三步可以优化成一步,所以就成了三次握手。
而两次握手只保证了一方的初始序列号被对方成功接收,没办法保证双方初始序列号都能被确认接收。
原因三:避免资源浪费
如果只有[两次握手],当客户端发送的SYN报文在网络中阻塞,客户端没有接收到ACK报文,就会重新发送SYN,由于没有第三次握手,服务端不清楚客户端是否收到回复的ACK报文,所以服务端每收到一个SYN就只能先主动建立一个连接,这会造成什么情况呢?
如果客户端发送的SYN报文在网络中阻塞了,重复发送了多次SYN报文,那么服务端在收到请求后就会多个冗余的无效的链接,造成不必要的资源浪费
.
即两次握手会造成消息滞留情况下,服务端重复接受无用的连接请求SYN报文,而造成重复分配资源。
TCP建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号
,序列号能保证数据包不重复,不丢弃和按序传输。
不适用两次握手 和 四次握手的原因:
- 两次握手:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
- 四次握手:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信的次数。
为什么TIME_WAIT等待的时间是2MSL?
MSL 是 Maximum Segment Lifetime,报文最大生存时间
,它是任何报文在网络上存在的最大时间,超过这个时间报文将被丢弃。因为TCP报文基于IP协议的,而IP头中有一个TTL字段,是IP数据包可以经过的最大路由数,每经过一个处理他的路由器此值就减 1 , 当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。
MSL 与 TTL 的区别:MSL的单位是时间,而TTL是经过路由跳数,所以MSL应该大于等于TTL消耗为0的时间
,以确保报文已被自然消亡。
TTL的值一般是64,Linux将MSL设置为30秒,意味着Linux认为数据报经过64个路由器的时间不会超过30,如果超过了,就认为报文已经消失在网络了
。
TIME_WAIT 等待2倍的MSL,比较合理的解释是:网络可能存在发送方的数据包,当这些方式方的数据包被接收方处理后又向对方发送响应,所以一来一回需要等待2倍的时间
。
比如,如果被动方没有收到断开连接的最后的ACK报文,就会触发超时重发FIN报文,另一方收到FIN后,会重发ACK给被动方,一来一去正好2个MSL。
可以看到2MSL
,这其实是相当于至少允许报文丢失一次
,比如,若ACK在一个MSL内丢失,这样被动重发的FIN会在第2个MSL内到达,TIME_WAIT状态的连接可以应对。
2MSL的时间是从客户端接收到FIN后发送ACK开始计时的
,如果在TIME_WAIT时间内,因为客户端的ACK没有传输到服务器,客户端有收到了服务端重发的FIN报文,那么2MSL时间将重新计时
。
在Linux系统里2MSL默认是60秒,那么一个MSL也就是30秒。Linux系统停留在TIME_WAIT的时间为固定的60秒
。
为什么需要TIME_WAIT状态?
主动发起关闭连接的一方,才会有TIME_WAIT状态。
需要TIME_WAIT,主要是两个原因:
- 防止历史连接中的数据,被后面相同四元组的连接错误的接收;
- 保证[被动关闭连接]的一方,能被正确的关闭;
原因一:防止历史连接中的数据,被后面相同四元祖的连接错误的接收
为了能更好的理解这个原因,我们先来连接序列号(SEQ)和初始化序列号(ISN)
-
序列号,是一个TCP一个头部字段,标识了,TCP发送端到TCP接收端的数据流的一个字节,因为TCP是面向字节流的可靠性协议,为了保证消息的顺序性和可靠性,TCP为每个传输方向上的每个字节都赋予一个编号,以便传输成功后确认,丢失后重传已经在接收端保证不会乱序,
序列号是一个32位的无符号数,因此在到达4G之后再循环到0
。 -
初始化序列号,在TCP建立连接的时候,客户端和服务端都会各自生成一个初始序列号,它基于时钟生成的一个随机数,来保证每个都拥有不同的初始化序列号,
初始化序列号可视为一个32位的技数器,该计数器的数值每4微秒加1,循环一次需要4.55小时
。
序列号和初始化序列号并不是无限递增,会发送回绕为初始值的情况,这意味着无法根据序列号来判断新老数据。
假设 TIME_WAIT 没有等待时间或者时间过短,被延迟的数据包抵达后会发生什么呢?
-
服务端在关闭连接之前发送的SEQ=301报文,被网络丢失了
。 -
接着,服务端以相同的四元组重新打开新链接,前面被延迟的SEQ = 301,这时抵达了客户端,而且该数据报文的序列号刚好在客户端接收窗口内,因此客户端会正常接收这个数据报文,但是这个数据报文时上一个连接残留下来的,这样就产生数据错乱等严重的问题
。
为了防止历史中的数据,被后面相同四元组的连接错误的接收,因此TCP设计了TIME_WAIT状态,状态会持续2MSL时长,这个时间足以让两个方向上的数据包都被丢弃,使得原来的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的
。
原因二: 保证【被动关闭连接】的一方,能被正确的关闭
TIME-WAIT作用是等待的时间以确保最后的ACK能让被动关闭方接收,从而帮助其正常关闭
。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?