一起重温计网咯 | TCP与UDP协议
如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!博客目录 | 先点这里
一起重新温习TCP和UDP协议咯
- 传输层协议
- TCP/UDP
- 端口号
- 套接字
- UDP协议
- UDP的特点和目的
- UDP首部
- TCP协议
- TCP的特点和目的
- TCP首部
- TCP控制位
- TCP其他内容
- TCP的三次握手
- TCP的四次挥手、
- 超时重发控制和高速重发控制
- TCP的滑动窗口
- 拥塞控制和慢启动
- 常见疑问
- 为什么需要三次握手才能建立起连接?
- TCP首次握手的SYN超时问题
- TCP建立连接后,客户端出现了故障会怎么样?
- TCP挥手阶段为什么要有TIME_WAIT状态?
- 服务器出现大量CLOSE_WAIT状态的原因
传输层
TCP/UDP
UDP:
- 面向非连接
- 不维护连接状态,支持同时向多个客户端传输相同的内容,类似广播
- 数据包报头只有8个字节,相比TCP包头的20字节而言,额外开销更小
- 吞吐量只受限于数据生成速率,传输速率以及机器性能
- 尽最大努力交付,不保证可靠交付,不需要维持复杂的链接状态表
- 面向报文,不对应用程序提交的报文信息进行拆分或者合并,没有拥塞控制
TCP:
- 面向可靠连接
- 为了保证可靠交付,实现了传输时的各种控制,如数据破坏,丢包,分片顺序紊乱,拥塞控制等
- TCP首部信息更多,如校验和,序列号,确认应答,重发控制,连接管理,窗口控制诸多信息
区别:
- 面向连接 VS 无连接
- 可靠性。TCP是可靠连接,可以保证双方通讯的接收是无误的。而UDP是不可靠的,客户端发送完消息后,是不管服务端有没有收到的。
- 有序性。TCP是通过序列号来保证消息包的顺序的,到达不一定有序,但最终会排序,而UDP是不存在顺序的
- 速度。TCP的速度比较慢,因为要保证可靠性,要建立连接,要做很多其他的事情,所以UDP相对而言,速度就快很多了
端口号
《图解TCP/IP》
- 数据链路层和IP层的地址,分别指的是MAC地址和IP地址。前者用来识别同一链路中的不同计算机,后者用来识别TCP/IP网络中互联的主机和路由器。在传输层也有类似地址的概念,传输层的地址就是端口号。
- 端口号用来识别同一台计算机中进行通讯的不同的应用程序。因此,也称程序地址
通俗点讲,MAC地址是识别局域网不同主机的手段,IP地址是识别在整个互联网中暴露在公网上的主机的手段,而端口号就是区别同一主机不同应用程序的手段
套接字
TCP/IP协议模型中,不同主机或程序之间的通讯是依赖IP地址
,端口号
,协议号
来确认发送端和接收端的,就像发邮件一样,IP地址
,端口号
,协议号
三者才能确认一个邮箱地址,缺一不可。
Socket是什么呢?
- Socket是一种简化通讯的抽象概念,将通讯方式抽象成一个模板,是一座应用层和传输层沟通的桥梁。我们通过这座桥梁,就可以抛开复杂的概念,在实现的角度上,更容易的做到端对端的交流和通讯。所以Socket就相当是邮件系统的一个邮箱地址,记录着
IP地址
,端口号
,协议号
三者的信息,通过Socket建立的连接,我们就称为Socket连接。
其他知识补充
- Socket原意是“插座”。通过将这
IP地址
,端口号
,协议号
三者的信息结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同主机或应用程序的通信,实现数据传输的并发服务。- 要通过互联网进行通信,至少需要一对套接字,一个运行于客户机端,称之为ClientSocket,另一个运行于服务器端,称之为serverSocket。
- 根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
UDP协议
UDP的特点和目的
特点:
- UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务。并且它是将应用程序发来的数据在收到的那一刻,立即按照原样发送到网络上的一种机制
- UDP是一种不可靠的通信协议,在网络拥堵的情况下,UDP不会进行流量控制;丢包了,UDP也不会负责重发,数据包到达的顺序乱掉了,UDP也不会进行纠正行为,但是对视频/语音通信而言,丢包不重要,如果向TCP一样要对丢包数据进行重传,那么视频/语音质量就会非常的差劲,根本不知道对方在说什么
应用:
- 包总量较少的通信(DNS, SNMP等)
- 视频,音频等多媒体通信(即时通信)
- 限定于LAN等特定网络中的应用通信
- 广播通信(广播,多播)
UDP首部
以下是UDP首部信息的基本组成
- 源端口号
表示发送端端口号,字段长16位,是可选项,有时可能不会设置源端口号。没有源端口号时,该字段的值默认为0,可用于不需要返回的通信中。 - 目的端口号
表示接收端端口号,字段长度为16位,是必选项,不然无法得知要通信的进程是谁 - 包长度
该字段保存了UDP首部的长度和数据的长度之和,内容是数字,单位是字节(1字节8位) - 校验和
校验和是为了提供可靠的UDP首部和数据而设计,具体谷歌/百度
TCP协议
TCP的特点和目的
TCP是一种利用IP面向连接的可靠传输层通信协议,可以说是对“传输”,“发送”,“通信”进行控制的协议,它充分地实现了数据传输时各种控制功能,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
- TCP是面向连接的通信协议,只有在确认通信对端存在的情况下,建立了连接后才能发数据
- TCP是可靠的通信协议,为了通过IP数据报实现可靠性传输,需要考虑很多的事情,例如数据的破坏,丢包,重复以及分片顺序混乱等问题。而TCP就是通过校验和,序列号,确认应答,重发控制,连接管理以及窗口控制等机制实现可靠性传输的
注意:
- TCP的数据长度并未写入TCP首部。实际通讯中求得TCP包的长度的计算公式是:IP首部的数据包长度 - IP首部长度-TCP首部长度
TCP首部
以下是TCP首部信息的基本组成
- 源端口号
表示发送端的端口号,字段长16位 - 目的端口号
表示接收端的端口号,字段长度为16 - 序列号
字段长度为32位,序列号是指发送数据的位置。每发送一次数据,就累加一次该数据字节数的大小。序列号不会从0或1开始,而是在建立连接时由计算机生成的随机数作为其初始值,通过SYN包传给接收端主机。然后再将每转发过去的字节数累加到初始值上表示数据的位置。此外在建立连接和断开连接时发送的SYN包和FIN包虽然并不携带数据,但是也会作为一个字节整加对应的序列号 - 确认应答号
字段长度为32位,是指下一次应该收到的数据的序列号。实际上,它是指已收到确认应答号前一位为止的数据。发送端收到这个确认应答号以后可以认为在这个序号以前的收据都已经被正确接收 - 数据偏移
字段长度为4位,单位为4字节(32位)。表示TCP所传输的数据部分应该从TCP包的哪个位开始计算,所以可以把它看做是TCP首部的长度。 - 保留
字段长度为4位,一般设置为0,该字段主要是为了以后扩展时使用 - 控制位
字段长度为8位,每一位从左到右分别是CWR,ECE,URG,ACK,PSH,RST,SYN,FIN。这些控制标志也叫控制位,具体含义可以谷歌、百度 - 窗口大小
字段长度为16位,用于通知从相同TCP首部的确认应答号所指位置开始能够接收的数据大小。TCP不允许发送超过此处所示大小的数据。不过,如果窗口为0,则表示可以发送窗口探测,以了解最新的窗口大小。但这个数据必须是1个字节。窗口大小是有接收端主机决定 - 校验和
与UDP校验和类似,具体谷歌/百度
紧急指针 - 字段长度16位,只有在URC控制位为1的时候有效。该字段的数值表示本报文段紧急数据的指针。
- 选项
选项字段可以设置一些可选参数,用于提供TCP的传输性能。因为根据数据偏移(首部长度)进行控制,所以其长度最大为40字节,字段大小最好是32位的倍数
TCP控制位
控制位长度为8
位,所以每一位代表一个控制位状态,每个控制位只有0
和1
两种状态
控制位 | 描述 |
---|---|
CWR | CWR标志与后面的ECE标志都用于IP首部的ECN字段。ECE标志位为1 时,则通知对方已将拥塞窗口缩小 |
ECE | 表示ECN-Echo, 置为1 会通知通信对方,从对方到这边的网络有拥塞。在收到数据包的IP首部中的ECN为1 时,将TCP首部的ECE设置为1 |
URG | 该位为1 时,表示包中有需要紧急处理的数据 |
ACK | 该位为1 时,确认应答的字段变成有效。TCP规定除了最初建立连接时的SYN包之外该位必须设置为1 |
PSH | 该位为1 时, 表示需要将受到的数据立即传给上层应用协议。PSH为0 时,则不需要立即传而是先进行缓存 |
RST | 该位为1 时,表示TCP连接中出现异常必须抢强制断开连接 |
SYN | 该位为1 时,表示希望建立连接,并在其序列号的字段进行序列号初始值的设定 |
FIN | 该位为1 时,表示今后不会再有数据发送,并希望断开连接。当通信结束希望断开时,通信双方的主机之间就可以互相交换FIN位置为1的段。每个主机又对对方的FIN进行确认应答以后就可以断开连接。不过,主机收到FIN设置为1的TCP段后不必马上回复一个FIN包,而是可以等到缓冲区中所有的数据都因已成功发送而自动删除之后再回应 |
TCP协议其他内容
TCP的三次握手
通俗版本:
- 第一次握手
客户端A向服务端B发起询问,"你听的到我吗?" - 第二次握手
服务端B听到了客户端A的呼叫,回应道,"我听的到你说话,你听得到我说话吗?" - 第三次握手
客户端A听到了服务端B的回应,回复道,“我也听得到你说话,我们可以建立连接吧”
正式版本:
三次握手是为了建立连接,保证可靠的通信,三次握手的流程如下:
-
第一次握手
建立连接时,客户端A
发送SYN
包(seq = x
)到服务器B
,并进入SYN_SEND
状态,等待服务端B
确认 -
第二次握手
服务端B
收到客户端A
发来的SYN
包,所以要对其进行确认,反馈ACK
确认应答(ack = x +1
) , 同时自己也要发送一个SYN
包(seq = y
)给客户端A
, 所以总共就是两个状态包,SYN + ACK
, 然后服务端B
随即进入SYN_RECV
状态 -
第三次握手
客户端A
收到服务端B
的SYN +ACK
包,得知服务端B
已经收到了自己上次发送的消息,此时要告诉服务端B
,我也收到了你的反馈,所以又向服务端B
发送确认ACK
应答包(ack = y + 1
),此包发送完毕,客户端和服务端进入ESTABLEISHED
状态,完成三次握手,建立起连接
TCP的四次挥手
通俗版本:
- 第一次挥手
客户端A向服务端B呼叫,"我已经没有更多的消息要告诉你了,准备要关闭连接啦!" - 第二次挥手
服务端B听到了客户端A的呼叫,回应道,"行,我知道了,我还有一些信息要转告给你,全部发完再通知你" - 第三次挥手
服务端B主动通知客户端A,"我要说的也已经说完了,已经做好关闭连接的准备啦,你听到了吗?" - 第三次挥手
客户端A听到了服务端B的回应,回复道,“okok, 那我们关闭连接吧”
正式版本:
TCP采用四次挥手来释放连接,关闭连接的请求可以由客户端触发,也可以由服务端触发,我们这里主要说明客户端触发,道理都是一样的
-
第一次挥手
客户端不再有数据要发送,所以客户端A
发送一个FIN
包(seq = x
),来关闭客户端到服务端的数据传输,客户端A
进入FIN_WAIT_1
状态 -
第二次挥手
服务端B
接收到FIN
包后,反馈一个应答确认ACK
包(ack = x + 1
)给客户端A
,同时也发送自己的序列号(seq = y
)。同时服务端B
进入CLOSE_WAIT
状态,期间服务端还可以发送数据给客户端 -
第三次挥手
服务端B
一段时间后,再发送一个FIN
包,表示关闭服务端到客户端的数据传输,服务端也不再有数据要返回了,seq = w
(w = y + data.length
(期间服务端发送的数据大小)) ; 同时也再发送ACK
确认(ack = x +1
), 其实就是对客户端对服务端第一次挥手的再次回应。 服务端从CLOSE_WAIT
状态变成LAST-ACK
状态 -
第四次挥手
客户端A
收到服务端B
返回的FIN后,客户端进入了TIME_WAIT状态,并发送一个ACK
(ack = x + 1
)确认应答给服务端,表示客户端等待进入最后关闭阶段了; 等服务端收到后,则直接进入关闭连接状态
超时重发控制和高速重发控制
先来了解两个概念,RTT和RTO:
RTT
发送一个数据包到收到对应的ACK, 所花费的时间RTO
重传时间间隔
TCP协议中有两种关于数据超时,需要重发的控制,分别是
-
超时重发
-
高速重发
超时重发控制
- 超时重发控制指的是,发送端发送消息后超过了限定时间,仍未为收到接收端的确认应答,发送端将对数据进行重发的一种方案。虽然我们都知道超时就需要重发,但是这个超时时间是怎么确定的呢?
- 为了保证无论出于何种网络环境下,都要提供高新能的通讯质量,这个超时时间不能太长,也不能太短,一定要适中,所以TCP连接每次发包时,都会计算往返时间RTT以及其偏差。TCP连接会将RTT和偏差相加的值作为重发的超时时间。
- 如果数据在超时重发之后,依然收不到应答,那么将会再次发送,但是该超时时间也会发生变化,以2的指数倍时间延长。但超时时间不会被无限延长,数据重发也不会是无限次数。一般在达到一定的重发次数后,依然无法收到回应, TCP连接就会认为对端主机发生异常,并强制关闭连接
高速重发控制
- 高速重发机制就是并不是利用超时时间来确定重发的。而是一种由发送端在TCP窗口大小中连续收到三次同一序列号答复的确认应答时,快速做出对数据进行重发的机制。 既不需要等到超时时间的判断,只需要在超时时间内,有三个相同的确认应答回复要同一段数据,我们就认为该数据没有被接收端接收到,而对其进行立即重发
- 高速重发的好处就是不需要等到超时时间才重发。因为在此时间内,我就有其他办法知道数据丢失了,而对其立即重发。节省了时间,也提高了通讯的速度。所以相对超时重发机制,就取名叫高速重发机制
MSS和窗口大小
(一) MSS,最大消息长度
什么是MSS,最大消息长度?
- TCP连接是以段为单位发送的数据的,既一次发送一段的数据。囊额这个段就是该TCP连接的
"最大消息长度(Maximum Segment Size)"
, 也即MSS 。最理想的情况下,MSS正好是不会IP分片处理的大小 - TCP在传输大量数据的时候,并不会将整个大数据一次性发送,而是以MSS的长度对数据进行切割分段,然后分批发送的。
怎么确定MSS的大小呢?
- MSS是在发送端和接收端进行TCP三次握手的时候,经过两端主机计算得出的。既两端主机在建立TCP连接的时候,会在TCP首部写入MSS选项,互相告知对方,自己的硬件接口能够适应的MSS大小,然后会在两者之间选其小,既选择一个最小的值最为双方都能接受的MSS值
(二) TCP连接窗口大小
什么是TCP连接的窗口大小?
- TCP首部中有一个窗口大小的选项,它是由通讯双方的接收端决定的,既接收端的缓冲区最大可以接收这个大小的数据。既接收方缓存区所能存储的数据大小限度就是该发送发和接收方TCP连接的窗口大小
- 但是窗口大小和MSS是不同的,MSS只是一个段,而窗口大小指的是,发送端不需等待确认应答就可以持续发送的数据最大值。 既通常情况下,发送端发送一个段,就需要等待应答,才能发送下一个段。但有了窗口大小,比如窗口大小是5个MSS,那么我们就可以并发时的顺序发送5个MSS段数据,而不需要等待这5个段的确认应答。
- 窗口大小机制是基于使用大量的缓冲区,实现的多段的并发发送和对多个段的同时应答。为什么是缓冲区呢,因为你多段的数据是有序列号顺序的,你一次性发送了多段,我接收方只是暂时都无序接收了, 但是最终还是依赖你这一批的段都发送成功,我也接收成功,然后我接收方根据序列号自行拼接段顺序。而存储一批批乱序的接收方空间就是缓存区
怎么确认TCP连接的窗口大小?
- 窗口大小的值是由通讯双方的接收端主机决定的,既接收端最大可以接收这个大小的数据。在TCP首部中,会有一个专门的字段来存储窗口大小的值。接收端可以将自己可以接收的缓冲区大小写到这个窗口大小的字段中,通知给发送端。一般这个值越大,说明网络的吞吐量越高。
为什么需要窗口大小?
TCP是以段为单位发送数据的,既每发一个段,就需要进行一次确认应答。这样的传输方式有两个缺点:
- 包的往返时间,既RTT越长,该TCP通讯的质量就越低。
- 一旦有一个包丢失了,就需要等待其到达超时时间,然后再进行重发。如果因为网络波动,经常超时重发,这就会大大的增加了通讯的时间成本
为了解决以上的问题,TCP就引入了窗口的概念。即确认应答不再以每个分段的形式进行,而是以更大的单位进行确认,既一个窗口大小。
TCP滑动窗口机制
什么是窗口滑动机制?
- 我们知道了窗口大小是一个缓冲区,是可以容忍接收的多个段的最大数据大小。虽然说是并行的,但是这个并行接收也是有先后顺序的,既有的先应答成功,有的后应答成功。一旦有应答成功,那个这个窗口大小是会产生滑动的。比如窗口大小是5MSS, 一旦成功接收并应答一个MSS的数据,那么窗口就要向右滑动一个MSS,既顺延下去,保证同一时间总是有5个MSS的段在发送和接收
- 不过窗口的滑动也是有所依赖的,既并不是一个段成功应答,我就立即滑动一个段。而是需要依赖结果顺序的,比如窗口大小为5个段大小,此时
1,2,4,5
段接收并已确认响应,但是由于3
段没有得到响应,所以我们按顺序滑动,窗口大小只能滑动2个MSS, 范围是(3,8]
。只有3段得到响应,原5个段才可以被滑过,进入(5,10]
- 也因为窗口大小是在时时更新的,在不断变化和滑动的,所以通讯双方需要不断的告知自己目前能接受的窗口大小,还剩余多少空间可以接收对方的数据。比如说接收方的缓存区几乎要满了,接收方会更新窗口大小,并通知给发送端,告知发送端,我目前最多还能接受这个大小的数据,你可别发多了。
- 同时如果接收端的响应,接收端没有收到的话。发送端就会发送一个叫窗口探测的特殊请求,专门用于打探接收端目前能接受的窗口大小。以避免发送端发多了数据,导致接收端无法处理这么多的数据,造成数据在缓冲区溢出。
滑动窗口机制带来的好处
- 允许多段数据同时发送,而无需等待应答,加快网络通信的效率,提高通信的质量
- 对流量进行控制限流,既同一时间最多,你就可以发送这么多段的数据
- 具有拥塞控制,支持对网络流量进行拥塞的控制,避免造成网络堵塞
拥塞控制和慢启动
有了TCP的窗口大小,收发主机之间即使不再以一个数据段为单位来发送确认应答。也能够放连续发送大量数据包。但是,如果在通讯刚开始的阶段就发送大量数据,也是可能会产生其他问题的。
- 一般来说,计算机网络是处于同一个共享环境,既一台机子或一个网络环境,存在着大量的TCP连接或是其他协议的通讯方式。因此很有可能会因为其他主机之间的通讯而使得网络造成阻塞。在网络出现阻塞的时候,如果我们的发送方突然发送了一个较大的数据,那么就极有可能加重了网络阻塞的程度,甚至导致网络的瘫痪。
所以TCP为了解决类似问题的发生,在通讯的一开始就会通过一个叫 “慢启动”
的算法机制,对发送端的发送数据量进行控制,同时为了在发送端调节锁需要发送的数据量,又会引入一个 “拥塞窗口”
的概念。
- 在双方通讯之初,为了避免一开始就发送大量的数据,于是会有一个慢启动的机制,先将发送端的窗口大小设置为1个MSS大小,之后每收到一个应答,窗口大小就以2的指数倍增加,直到达到最大大小限制。比如一开始只能发送一个MSS,接收应答后,之后我就可以发2个MSS, 然后4个MSS, 8个MSS
慢启动和拥塞窗口等概念一起组成了TCP连接的拥塞控制机制,有了这些机制,就可以有效的减少通讯开始时连续发包导致的网络阻塞现象
常见疑问
为什么需要三次握手才能建立起连接?
在问为什么之前,我们可以去先了解一个小故事“两军问题”
首先,我们得理解TCP的三次握手的本质,三次握手本质是两个步骤,即能成功建立连接的本质是双方都完成了一个呼叫和确认回应的过程
- 客户端告诉服务端,我要建立连接,并且要得到服务端的确认回应
- 服务端告诉客户端,我可以建立连接,并且要的要客户端的确认回应
TCP 为什么是三次握手,而不是两次或四次?
- TCP作为一种可靠传输控制协议,其核心思想是:即要保证数据可靠传输,又要提高传输效率,而用三次恰恰可以满足以上两个方面的需求!
- TCP 为什么是三次握手,而不是两次或四次? - @作者:车小胖
其实建立连接不一定要三次握手,也可以两次握手,四次握手,甚至更多,只是三次握手刚好满足双方的一呼一答,又能最大程度的提高建立连接的效率,所以三次握手是比较好的一个选择。
两次握手,可能存在通知不到位的情况,三次以上的握手,无论再握多少次,从两军问题中,我们可以知道,增加握手次数也无法做到完全可靠的通信,又略显多余,
同理为什么要四次挥手的原理也是一样,分别对客户端和服务端都是一个来回,客户端需要一个通知和确认,服务端也需要一个通知和确认,在保证可靠传输和性能的前提下,四次挥手恰好可以满足以上的需求。
TCP首次握手的SYN超时问题
服务端收到客户端的SYN , 回复SYN-ACK的时候,可能因为客户端掉线了,所以服务端一直没有收到客户端的ACK确认。对于这个问题,Linux系统中,默认服务端会不断的重试直至超时,重试间隔从1秒开始到翻倍,默认等待63秒才断开连接
所以这就会造成一种针对SYN Flood
的风险隐患
- 黑客攻击者可以通过客户端不断的发送SYN包,但是在第二次握手的时候,将客户端下线,不断耗尽处理服务端的SYN包的资源,造成正常的连接无法使用
- Linux环境下,如果SYN队列满了, 操作系统会通过tcp_syncookies参数会发一个不同的SYN包,“SYN Cookie”包,这种包,攻击者的客户端肯定是不会回应的。但是正常的客户端的则会回应SYN Cookie包,直接与服务端建立连接
还有另一种类似的情况,如果第三次握手期间,客户端回复了ACK,但是服务端掉线了,一直没有接收到ACK,这又会是一个什么样的情况呢?
TCP建立连接后,客户端出现了故障会怎么样?
保活机制:
- 在一段保活时间内(
Keep-Alive Time
),连接要是处于非活动状态,开启保活功能的一方将向对方发送保活探测报文,如果发送端没有收到对应响应报文,则继续发送,直到尝试次数达到保活探测数仍未收到响应,则判断对方主机确认不可达,则中断连接
TCP挥手阶段为什么要有TIME_WAIT状态?
主动关闭连接端为什么要有TIME_WAIT
阶段呢?而不是立即关闭请求?首先了解什么是 MSL
? 以及什么是 2MSL 重新计时
MSL
MSL
的英文名称是Maximum Segment Lifetime,既 “报文最大生存时间”,它的意思是报文在网络上存在的最长时间,超过这个时间报文将被丢弃。- 不同操作系统的 MSL 默认时间不同
- Windows:120s
- Linux: 60s
- Unix: 30s
2MSL 重新计时
- TIME_WAIT 阶段需要经过 2MSL 才会进入 CLOSED 阶段,关闭连接
- 客户端段在收到服务端的 Last FIN 后,就会进入 TIME_WAIT 阶段,同时向服务端返回 FIN_ACK, 如果服务端没有接收到最后的 ACK, 就会导致服务端重发 FIN, 客户端如何又再次收到 FIN,则会重新进入 TIME_WAIT 阶段, ** 2MSL 会重新计时 **
那么为什么要有 TIME_WAIT 阶段呢?
TIME_WAIT 存在的作用只有两个目的:
- 优雅的关闭被动端的连接,即让被动端有足够的时间接收到 FIN-ACK 而主动关闭,而不是因为出现异常,没收到 ACK 而选择不断的发送 FIN 包,因为到达重发最大次数而被强制关闭
- 通过等待 2MSL 的 TIME_WAIT 时间来避免前后两个使用相同四元组的连接中的前一个连接的报文干扰到后一个连接
(一) 确保被动关闭端有足够的时间来收到ACK
- 我们知道了MSL是报文最大的生存时间,一到某个报文被发出后,超过了MSL时间,就会被认为失效报文,不再被处理。
- 所以当被动关闭端进入last-ack阶段的时候,主动端又接收到了被动端的FIN请求,那么主动端就会回应一个ACK给被动端,当被动端接收到这个ACK,就会进入Closed关闭连接阶段。但问题是,如果被动端因为网络问题,没有收到主动端的ACK怎么办?那么被动端就会超时重发一个FIN包。
- 如果此时主动端没有TIME_WAIT机制,发回ACK包后就直接关闭了连接,而被动端没有接收到ACK而重发FIN包,就会导致主动端永远接收不到重发的FIN包,无法回应,从而导致被动的一直在不停重发FIN包,直到重试次数达到最大值才停止,进入Closed阶段,这样就会耗费大量的时间,导致被动端无法新建立一个同一个socket的连接。
- 如果此时主动端有TIME_WAIT机制的话,被动端的重发FIN包,主动端就可以等待MSL时间收到FIN包,重新发送ACK包,并等待被动端接收,等待接收时间又是一个MSL, 说白了TIME_WAIT的2MSL, 就是消息的一来一回的最大生存周期,保证主动端能在一个报文生命周期收到FIN包,同时又给了一个报文生命周期的时间给被动端接收ACK包
- 当然如果TIME_WAIT时间结束后,被动端依然接收不到ACK包,而主动端已经关闭,那么被动端只能无奈,重试直最大次数,然后再关闭连接,所以有TIME_WAIT机制就可以保守的保证连接关闭的最短时间是4分钟后
(二) 确保该连接的消息超过MSL而失效,不会影响到下一个连接
- 因为TCP协议流的特性,对待同一个源IP,源端口,目的IP,目的端口,协议的Socket不同连接,TCP是无法识别出他们的不同的,既对待同一个Socket的不同连接,他们不会认为是不同的多个连接,只会认为是一个连接。所以如果连接关闭后,立马又建立了连接,那么两个连接的消息可能是会被串在一起,混淆了的。
- 而主动端的2MSL,就可以保证消息在上一个连接的消息不会存活到下一个连接。既上一个连接会等待当前连接发送的消息都失效后,才关闭连接,不会让当前消息存活在当前连接之外。
- 比如说连接A发送了一个消息,因为网络问题,消息一直没有到达,在该消息发送到目的主机的过程中,连接A已经关闭释放,并建立了新的连接B,因为协议,IP,端口都是相同的,所以消息会发送到连接B的目的主机上,但是连接B不会处理该消息。因为它自从发送出来, 已经超过了MSL时间,已经被视为过期。如果没有TIME_WAIT, 那么连接关闭后,在MSL时间内又重新建立连接,就会导致上个连接的消息在下个连接中被认为是未过期的消息而被处理
为什么是 2MSL 时间呢?一个 MSL 不可以吗?甚至更短?
我们首先假设网络存在波动,每次的请求要么成功要么失败,成功的请求假设都需要在网络经过 MSL 才到达目的地
- 首先因为客户端的第一个 ACK 在网络中的最大生命周期是 MSL, 所以至少需要预留 MSL 的时间给第一个 ACK 去到达服务端,这里证明需要至少需要
MSL
时间 - 其次假设服务端一直没有收到 ACK 就会触发超时重发 FIN 包,重发肯定有最大次数限制,我们最后一次重发 FIN 包的时间点是 x, 那么我们至少需要 x + MSL 的时间来保证最后一个重传 FIN 包有足够的时间到达客户端,以重新触发 TIME_WAIT 阶段, 这里证明至少需要
x + MSL
时间- 因为 x 是服务端所允许的最后一次重传 FIN 时间,所以 X 应该大于 MSL 甚至 2MSL
由上面的结论,我们可以得到 TIME_WAIT 应该至少需要 [MSL , x + MSL]
的时间, 且 MSL < 2MSL < X + MSL
并非 2MSL ? 所以到底是什么要求了 2MSL?
- 因为至少需要 MSL 的时间才能让第一个 ACK 有足够的时间到达服务端,假设在第一个 ACK 到达服务端之前,即 MSL 临界时间点,服务端重传了一个 FIN 包,**那么我们就还需要 MSL 的时间去等待该报从网络中消失,避免影响到同四元组的下一个连接 **
所以总结,因为我们需要让第一个 ACK 有足够的时间到达服务端,所以 TIME_WAIT 至少需要一个 MSL 时间,同时因为 FIN 包可能在 ACK 经过 MSL 时间要到达服务端的前一刻又被重发了,所以还需要等待多 MSL 时间去等待该包在网络中消失,所以综合至少需要 2MSL 的时间来保证该连接能够让服务端优雅的关闭,同时又可以避免上一个连接的报文影响到下一个连接
服务器出现大量CLOSE_WAIT状态的原因
- 待续…
参考资料
- 《图解TCP/IP》
- 为什么TCP4次挥手时等待为2MSL?- @知乎
- 如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!