TCP协议传输的特点主要就是面向字节流、传输可靠、面向连接。
TCP协议如何确保传输的可靠性的?
确保传输可靠性的方式
TCP协议保证数据传输可靠性的方式主要有:
1.校验和
2.序列号
3.确认应答
4.超时重传
5.连接管理
6.流量控制
7.拥塞控制
1.校验和
发送方:在发送数据之前计算检验和,并进行校验和的填充。
接收方:收到数据后,对数据以同样的方式进行计算,求出校验和,与发送方的进行比对。
注意:如果接收方比对校验和与发送方不一致,那么数据一定传输有误。但是如果接收方比对校验和与发送方一致,数据不一定传输成功。
校验和如何计算和验证呢?
先看看IP包头部结构:
当发送IP包时,需要计算IP报头的校验和:
1、 把校验和字段(16位首部校验和)置为0;
2、 对IP头部中的每16bit进行二进制求和;
3、 如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
4、 将该16bit的值取反,存入校验和字段。
◆当接收IP包时,需要对报头进行确认,检查IP头是否有误,算法同上2、3步,然后判断取反 的结果是否为0,是则正确,否则有错。
例如:
IP包头部:
45 00 00 31
89 F5 00 00
6E 06 00 00(校验字段,上面第1步置0)
DE B7 45 5D -> 222.183.69.93
C0 A8 00 DC -> 192.168.0.220
计算:
4500 + 0031 +89F5 + 0000 + 6E06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4
0003 + 22C4 = 22C7(上面第3步)
~22C7 = DD38 (上面第4步) ->即为应填充的校验和
当接收方接受到IP数据包时,要检查IP头是否正确,则对IP头进行检验,方法同上:
计算:
4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC
0003 + FFFC = FFFF
~FFFF = 00000 ->正确
2.确认应答与序列号
发送方与接收方之间的通信是一个数据传输过程。通信的消息将以数据包形式进行传输。
分块发送:应用数据被分割成TCP认为最适合发送的数据块。由TCP传递给IP的信息单位称为报文段或段(segment)
正确排序:由于IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
数据从主机传送到另一个主机往往要经过路由器、网关等设备。这些设备都要对经过的数据进行处理。由于这些设备处理数据有一定的限制,不能处理超过额定字节的数据,所以发送的时候需要确定发送数据包的最大字节数。
这个最大字节数被称为最大消息长度(Maximum Segment Size,MSS)。当要发送的数据超过该值,就需要将数据分为多个包,依次发送。该操作被称为数据分片。
MSS 是 TCP 数据包每次能够传输的最大数据量。通常,最大值为 1460 字节。如果发送的数据包大小大于 MSS 值,数据包将会被分片传输。
第 1 次和第 2 次握手包的 TCP 首部包含 MSS 选项,互相通知对方网络接口能够适应的 MSS 的大小,然后双方会使用较小的 MSS 值进行传输。
在进行数据传输时,如果传输的数据比较大,就需要拆分为多个数据包进行发送。TCP 协议需要对数据进行确认后,才可以发送下一个数据包,如图所示
发送方每发送一个数据包,都需要得到接收方的确认应答以后,才可以发送下一个数据包。
序列号:TCP传输时将每个字节的数据都进行了编号,这就是序列号(上图 1-1000,1001-2000,2001-3000等)。
确认应答:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答(上图下一个发送1001,下一个发送2001等)。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。
序列号的作用不仅仅是应答的作用,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据。这也是TCP传输可靠性的保证之一。
推荐阅读:TCP数据传输过程详解
3.超时重传
在进行TCP传输时,由于确认应答与序列号机制,也就是说发送方发送一部分数据后,都会等待接收方发送的ACK报文,并解析ACK报文,判断数据是否传输成功。
如果发送方发送完数据后,迟迟没有等到接收方的ACK报文,这该怎么办呢?
TCP在解决这个问题的时候引入了一个新的机制,叫做超时重传机制。简单理解就是发送方在发送完数据后等待一个时间,时间到达没有接收到ACK报文,那么对刚才发送的数据进行重新发送。
由于TCP传输时保证能够在任何环境下都有一个高性能的通信,因此这个最大超时时间(也就是等待的时间)是动态计算的。
在Linux中(BSD Unix和Windows下也是这样)超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。
重发一次后,仍未响应,那么等待2500ms的时间后,再次重传。等待时间(n500ms)以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接。
4.连接管理
看另外一篇文章:计算机网络-TCP三次握手、四次挥手过程及原理
5.流量控制
如果发送端的发送速度太快,导致接收端的结束缓冲区很快的填充满了。此时如果发送端仍旧发送数据,那么接下来发送的数据都会丢包,继而导致丢包的一系列连锁反应,超时重传等。而TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制。
在TCP协议的报头信息当中,有一个16位字段的窗口大小。16位窗口大小的内容实际上是接收端接收数据缓冲区的剩余大小。数字越大,证明接收端接收缓冲区的剩余空间越大,网络的吞吐量越大。
滑动窗口是接受数据端使用的窗口大小,用来告知发送端接收端的缓存大小,以此可以控制发送端发送数据的大小,从而达到流量控制的目的。
接收端会在确认应答发送ACK报文时,将自己的即时窗口大小填入,并跟随ACK报文一起发送过去。
发送方根据ACK报文里的窗口大小的值的改变进而改变自己的发送速度。如果接收到窗口大小的值为0,那么发送方将停止发送数据。并定期的向接收端发送窗口探测数据段,让接收端把窗口大小告诉发送端。
16位的窗口大小最大能表示65535个字节(64K),但是TCP的窗口大小最大并不是64K。在TCP首部中40个字节的选项中还包含了一个窗口扩大因子M,实际的窗口大小就是16为窗口字段的值左移M位。每移一位,扩大两倍。
6.拥塞控制
一个连接的TCP双端只是网络最边缘的两台主机,他们不知道整个网络是如何工作的,因此他们不知道彼此之间的有效吞吐量。因此,他们必须找到一种方法来确定它。我们称之为拥塞窗口 (CWND)。
拥塞窗口是决定任何时候可以发出的字节数的因素之一。拥塞窗口由发送方维护,是阻止发送方和接收方之间的链路因流量过多而过载的一种手段。这不应与发送方维护的滑动窗口相混淆,滑动窗口的存在是为了防止接收方过载。拥塞窗口是通过估计链路上有多少拥塞来计算的。
拥塞控制包括 慢启动(slow-start)、拥塞避免(congestion avoidance)、快速重传(fast retransmit)、快速恢复(fast recovery)。
快速重传和快速恢复的目的是:快速恢复丢失的数据包。
慢启动(slow-start)
当主机开始发送数据时,如果立即将大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。
通常在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。
每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。不过“传输轮次”更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。
另,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。
RTT (Round-Trip Time):往返时延。
慢启动的整个过程如下:
初始化 cwnd = 1
经过1个RTT,即收到一个ACK,cwnd = 2^(1) = 2
经过2个RTT, cwnd = 2^(2) = 4
经过3个 RTT, cwnd = 2^(3) = 8
拥塞避免(congestion avoidance)
让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
无论在慢启动阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢启动ssthresh设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生 拥塞的路由器有足够时间把队列中积压的分组处理完毕。
“拥塞避免”并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。“拥塞避免”是说在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞
快速重传(fast retransmit)
TCP 有一个快速传输特性——在它的计时器到期之前重新传输丢失的段。为了允许快速传输,我们需要为发送方和接收方设置一些规则。
- 作为接收者,它应该始终发送它期望接收的序列号。例如,当接收方接收到第 1 段(segment)时,它以 ACK2 响应,
- 作为发送方,它应该忽略定时器并在收到 3 个重复的ACK 后立即开始重传丢失的段。
用一句话概况,就是发送端在收到3个重复无序的ACK时候,它假定数据包丢失并重传该数据包,而无需等待重传计时器到期。
而在此时,拥塞窗口的变化过程如下:
- ssthresh设置为拥塞窗口的1/2
- 拥塞窗口大小设置为ssthresh
- 重新进入拥塞避免阶段
快速恢复(fast recovery)
- 当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。
- 每次收到另一个重复的ACK时,cwnd增加1个报文段大小并发送1个分组(如果新的cwnd允许发送)。
- 当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认。另外,这个ACK也应该是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。这一步采用的是拥塞避免,因为当分组丢失时我们将当前的速率减半。
参考:https://cloud.tencent.com/developer/article/1857368