TCP 如何保证消息的可靠性和顺序性
TCP 保证可靠性主要依靠下面 7 种机制:
- 发送方将整个报文段分为多个
16位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算,如最终结果为检验字段所有位是全 1 则正确,否则存在错误;- 如果收到段的检验和有差错,
TCP将丢弃这个报文段和不确认收到此报文段;
TCP 会对每个包都进行编号,序列号的作用是:
数据传输过程中的确认应答处理、重发控制以及重复控制等功能都可以通过序列号来实现;
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 报文段的首部格式









建立过程为:
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 服务器,因而导致错误
由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工), 正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,保证通信双方都把话说完了才挂电话

那 TCP 的四次挥手,是为了保证通信双方都关闭了连接,具体过程如下:

为什么连接的时候是三次握手,关闭的时候却是四次握手
当
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 窗口中。这个大小随数据传输情况而变,窗口越大,网络吞吐量越高,而一旦接收方发现自身的缓冲区快满了,就将窗口设置为更小的值通知发送方
注意:窗口大小不受 16 位窗口大小限制,在 TCP 首部 40 字节选项中还包含一个窗口扩大因子 M,实际窗口大小是窗口字段的值左移 M 位;



TCP 数据传送的可靠性。然而如果网络非常拥堵,此时再发送数据就会加重网络负担,发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题;拥塞窗口(cwnd)
ACK 应答,拥塞窗口加 1;而在每次发送数据时,发送窗口取拥塞窗口与发送端接收窗口最小者;维护一个慢开始门限 ssthresh 状态变量
在启动初期以指数增长方式增长;设置一个慢启动的阈值,当以指数增长达到阈值时就停止指数增长,按照线性增长方式增加;
线性增长达到网络拥塞时(重传计数器超时)立即"乘法减小",拥塞窗口置回 1,进行新一轮的“慢启动”,同时新一轮的阈值变为原来的一半;




总结:

转载请标明出处,原文地址:https://blog.csdn.net/weixin_41835916 如果觉得本文对您有帮助,请点击赞支持一下,您的支持是我写作最大的动力,谢谢。