TCP 如何保证消息的可靠性和顺序性
题目
TCP 如何保证消息的可靠性和顺序性
TCP 保证可靠性主要依靠下面 7 种机制:
一、校验和
- 发送方将整个报文段分为多个
16
位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算,如最终结果为检验字段所有位是全 1 则正确,否则存在错误;- 如果收到段的检验和有差错,
TCP
将丢弃这个报文段和不确认收到此报文段;
二、序列号
TCP
会对每个包都进行编号,序列号的作用是:
- 保证可靠性(当接收到的数据中少了某个序号的数据时,能马上知道);
- 保证数据的按序到达;
- 提高效率,可实现多次发送,一次确认;
- 去除重复数据;
数据传输过程中的确认应答处理、重发控制以及重复控制等功能都可以通过序列号来实现;
三、确认应答机制(ACK)
TCP
通过确认应答机制实现可靠的数据传输。在TCP
的首部中有一个标志位 —— ACK,此标志位表示确认号是否有效;- 接收方对于按序到达的数据会进行确认,当标志位
ACK = 1
时,首部的确认字段有效。进行确认时,确认字段值表示这个值之前的数据都已经按序到达了;- 而发送方如果收到了已发送的数据的确认报文,则继续传输下一部分数据;而如果等待了一定时间还没有收到确认报文就会启动重传机制;
正常情况下的应答机制:
四、超时重传机制
当报文发出后在一定的时间内未收到接收方的确认,发送方就会进行重传(通常是在发出报文段后设定一个闹钟,到点了还没有收到应答则进行重传),其基本过程如下:
当然,未收到确认不一定就是发送的数据包丢了,还可能是确认的 ACK
丢了:
当接收方接收到重复的数据时就将其丢掉,重新发送 ACK
。而要识别出重复的数据,就要用到前面提到的序列号了,利用序列号很容易就可以做到去重的效果;
重传时间的确定
- 报文段发出到收到应答中间有一个报文段的往返时间
RTT
,显然超时重传时间RTO
会略大于这个RTT
;TCP
会根据网络情况动态的计算RTT
,即RTO
是不断变化的。在Linux
中,超时以500ms
为单位进行控制,每次判定超时重发的超时时间都是500ms
的整数倍;- 其规律为:如果重发一次仍得不到应答,就等待
2 * 500ms
后再进行重传,如果仍然得不到应答就等待4 * 500ms
后重传,依次类推,以指数形式递增;- 重传次数累计到一定次数后,
TCP
认为网络或对端主机出现异常,就会强行关闭连接;
超时重传时间 RTO 小于往返时间 RTT
超时重传时间 RTO 远大于往返时间 RTT
超时重传时间的确定
计算超时重传时间
五、连接管理机制
连接管理机制即 TCP 建立连接时的三次握手和断开连接时的四次挥手
TCP 报文段的首部格式
5.1、三次握手
建立过程为:
- 第一次握手:建立连接时,客户端发送
SYN
包(SYN = j)
到服务器,并进入SYN_SEND
状态,等待服务器确认; - 第二次握手:服务器收到
SYN
包,必须确认客户的SYN(ACK = j + 1)
,自己也发送一个SYN
包(SYN = k)
,即SYN + ACK
包,此时服务器进入SYN_RECV
状态; - 第三次握手:客户端收到服务器的
SYN + ACK
包,向服务器发送确认包ACK(ACK = k + 1)
,此包发送完毕,客户端和服务器进入ESTABLISHED
状态,完成三次握手;
通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。 三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的。 但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?
- 第一次握手:
Server
确认了对方发送正常,自己接收正常; - 第二次握手:
Client
确认了:自己发送、接收正常,对方发送、接收正常;Server
确认了:对方发送正常,自己接收正常; - 第三次握手:
Client
确认了:自己发送、接收正常,对方发送、接收正常;Server
确认了:自己发送、接收正常,对方发送、接收正常;
如果是两次握手,则已失效的连接请求报文段突然又传送到了 TCP 服务器,因而导致错误
5.2、四次挥手
由于 TCP
连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工), 正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,保证通信双方都把话说完了才挂电话
那 TCP
的四次挥手,是为了保证通信双方都关闭了连接,具体过程如下:
- 第一次挥手:客户端 A 发送一个 FIN,然后进入 FIN_WAIT_1 状态;这表示客户端 A 没有数据要发送给服务器 B 了;
- 第二次挥手:服务器 B 收到这个 FIN,发回一个 ACK,确认序号为收到的序号加 1,客户端 A 进入 FIN_WAIT_2 状态;服务器 B 告诉客户端 A,我"同意"你的关闭请求;
- 第三次挥手:服务器 B 发送一个 FIN 给客户端 A,请求关闭连接,同时服务器 B 进入 LAST_ACK 状态;
- 第四次挥手:客户端 A 发回 ACK 报文确认,并将确认序号设置为收到序号加 1,然后客户端 A 进入 TIME_WAIT 状态,服务器 B 收到客户端 A 的报文段以后,就关闭连接; 此时,客户端 A 等待 2MSL 后依然没有收到回复,则证明服务器 B 已正常关闭,那好,客户端 A 也可以关闭连接了;
为什么连接的时候是三次握手,关闭的时候却是四次握手
当
Server
端收到Client
端的SYN
连接请求报文后,可以直接发送SYN + ACK
报文。其中ACK
报文是用来应答的,SYN
报文是用来同步的。 但是关闭连接时,当Server
端收到FIN
报文时,很可能并不会立即关闭SOCKET
,所以只能先回复一个ACK
报文, 告诉Client
端,“你发的FIN
报文我收到了”。只有等到我Server
端所有的报文都发送完了,我才能发送FIN
报文,因此不能一起发送。故需要四次挥手;
为什么 TIME_WAIT 状态还需要等 2MSL 后才能返回到 CLOSED 状态?
TIME_WAIT
状态就是用来重发可能丢失的ACK
报文。在客户端发送出最后的ACK
回复,但该ACK
可能丢失。服务端如果没有收到ACK
, 将不断重复发送FIN
片段。所以客户端不能立即关闭,它必须确认服务端接收到了该ACK
;- 客户端会在发送出
ACK
之后进入到TIME_WAIT
状态。**客户端**会设置一个计时器,等待2MSL
的时间。如果在该时间内再次收到FIN
,那么客户端会重发ACK
并再次等待2MSL
;- 所谓的
2MSL
是两倍的MSL(Maximum Segment Lifetime)
。MSL
指一个片段在网络中最大的存活时间,2MSL
就是一个发送和一个回复所需的最大时间;- 如果直到
2MSL
,客户端都没有再次收到FIN
,那么客户端推断ACK
已经被成功接收,则结束TCP
连接;
六、流量控制
接收端处理数据的速度是有限的,如果发送方发送数据的速度过快,导致接收端的缓冲区满,而发送方继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应;
TCP
支持根据接收端的处理能力,来决定发送端的发送速度,这个机制叫做流量控制;- 在
TCP
报文段首部中有一个 16 位窗口,当接收端接收到发送方的数据后,在应答报文ACK
中就将自身缓冲区的剩余大小,放入 16 窗口中。这个大小随数据传输情况而变,窗口越大,网络吞吐量越高,而一旦接收方发现自身的缓冲区快满了,就将窗口设置为更小的值通知发送方 - 如果缓冲区满,就将窗口置为 0,发送方收到后就不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端;
注意:窗口大小不受 16 位窗口大小限制,在 TCP 首部 40 字节选项中还包含一个窗口扩大因子 M,实际窗口大小是窗口字段的值左移 M 位;
七、拥塞控制
- 流量控制解决了两台主机之间因传送速率而可能引起的丢包问题,保证了
TCP
数据传送的可靠性。然而如果网络非常拥堵,此时再发送数据就会加重网络负担,发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题; - TCP 引入慢启动机制,先发出少量数据,就像探路一样,先摸清当前的网络拥堵状态后,再决定按照多大的速度传送数据;
拥塞窗口(cwnd)
- 发送开始时定义拥塞窗口大小为 1;每次收到一个
ACK
应答,拥塞窗口加 1;而在每次发送数据时,发送窗口取拥塞窗口与发送端接收窗口最小者; - 拥塞窗口的维护原则:只要网络没有出现拥塞,拥塞窗口就再增大一些,但只要网络出现拥塞,拥塞窗口就会减少一些
- 判断出现网络拥塞的依据:没有按时收到应当到达的确认报文(即发生超时重传)
7.1、拥塞控制算法1:慢启动
维护一个慢开始门限 ssthresh 状态变量
- 当 cwnd < ssthresh 时,使用慢开始算法;
- 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法;
- 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法;
在启动初期以指数增长方式增长;设置一个慢启动的阈值,当以指数增长达到阈值时就停止指数增长,按照线性增长方式增加;
7.2、拥塞控制算法2:拥塞避免
线性增长达到网络拥塞时(重传计数器超时)立即"乘法减小",拥塞窗口置回 1,进行新一轮的“慢启动”,同时新一轮的阈值变为原来的一半;
7.3、拥塞控制算法3:快重传
7.4、拥塞控制算法4:快恢复
总结:
转载请标明出处,原文地址:https://blog.csdn.net/weixin_41835916 如果觉得本文对您有帮助,请点击赞支持一下,您的支持是我写作最大的动力,谢谢。