编程知识 cdmana.com

计算机原理探险系列(三)-- TCP数据传输

上一篇文章中我们讲到了关于网络传输数据方面到内容,今天我们来深入了解下网络传输数据过程中可能会存在到问题。

数据包

正常到一次网络请求中,都会以数据包作为传输单位来判断。如果数据包到体积过大,那么就需要进行拆解。

tcp数据包信息基本结构如下:

在这里插入图片描述
包含了网络数据到发送端口,目标端口,ack,seq,还有一些和滑动窗口相关的数据信息。

mss值

mss值是一个缩写,全称是max segment size,主要是控制每次数据传输中的最大传输大小。每次进行数据包传输的时候,如果数据包大小超过了tcp里面限制的mss值,那么就需要分段传输,但是传输数据双方的mss值大小是不同的,因此最终分层传输是根据最小的mss值来限制。

如何达成通信双方的mss值大小约定?

在三次握手的时候会和服务端进行核对,并且达成一致协议。

握手建立的时候,通过wireshark抓包可以看到这option中mss参数:

在这里插入图片描述

mtu值

是网络传输过程中的最大传输单元,由于网络是分层传输数据的,因此不同层协议传输数据的时候都会有个最大传输单元的限制,也就是MTU(Maximum Transmission Unit,MTU)

在上一篇文章中我们有讲到过关于网络传输的细节点,在网络中传输数据的过程中,链路层主要是负责将数据包在不同的网关之间进行传输,不同的路由器它们之间的mtu大小是不一致的,所以在网络传输中,传输的性能主要取决于mtu最小的那台路由器。
如果MTU过大,在碰到路由器时会被拒绝转发,因为它不能处理过大的包。

场景思考:
当同一个网络上的两台主机互相进行通信时,该网络的MTU是非常重要。但是如果两台主机之间的通信要通过多个网络,每个网络的链路层可能有不同的MTU,那么这时重要的不是两台主机所在网络的MTU的值,而是两台主机通信路径中的最小MTU,称为路径MTU( Path mtu,PMTU)。

复杂场景的网络通信会是怎么样子的?

假设有一个场景下,a和b两台服务器需要进行网络通信,a需要先根据自己的路由设备的mtu计算出mss值。

通过以下方式可以查看到自己机器到mtu值


[[email protected] ~]# cat /sys/class/net/eth0/mtu 
1500

通常mss值为:MTU-20字节TCP报头-20字节IP报头得出,在以太网环境下,MSS值一般就是1500-20-20=1460字节。

通常在通信的发送端和接收端中间还有一系列复杂的路由器跳转,中间一旦出现任意机器上的mtu值小于传输数据包的体积值,那么就需要将该数据包进行遣返。这种情况我们一般称之为:PMTUD ( Path MTU Discovery )

ICMP错误报文

可以将它理解为个在网络链路传输中传输错误信息的一个角色。假设说在路由器的传输过程中出现了PMTUD情况,那么就需要返回一个ICMP错误报文包给到发送端,内部包含有具体的错误原因。
具体场景如下所示:
在这里插入图片描述
客户端在接收到对应的icmp错误报文之后,就会调整数据包体积的大小,然后进行数据包的分割,重新发送。

ICMP数据包也有可能会被误杀,有些网络管理员会在路由器、防火墙等中间设备上设置过滤ICMP报文的安全策略,这将导致ICMP差错报文被这些中间设备丢弃,无法达到发送方,从而引起PMTUD的失效。
现在有些厂商的路由器支持调整数据包的mss值,在进行传输数据的时候会动态调整数据包内option选项值的mss大小。

数据确认机制

在一次连接建立之后,客户端会给服务端发送相关的数据包信息,那么又该如何确认数据包是准确发送给到对方了呢?

RTO和RTT

RTT(Round Trip Time):一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值。

图片
RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传。

通常而言,RTO应当设置得比RTT略小一些,如果RTO小于RTT,那么可能会有重发情况发送,如果RTO太过大,那么有可能客户端会一直会处于等待状态,效率也不是那么高。

RTT和RTO 的关系
由于网络波动的不确定性,每个RTT都是动态变化的,所以RTO也应随着RTT动态变化。

SampleRTT

通常会选择一个已经发送但是未接受响应数据包的tcp连接作为参考对象,计算其RTT值来充当SampleRTT,这个SampleRTT会被代表所有连接的RTT。一旦该连接的ACK数据包返回之后,即可结束计时,得出SampleRTT值。

TCP维持一个估计RTT(称之为EstimatedRTT),一旦获得一个新SampleRTT时,则根据下式来更新EstimatedRTT:
EstimatedRTT = (1-a)* EstimatedRTT + a * SampleRTT
其中a通常取值为0.125,即:

EstimatedRTT = 0.875 * EstimatedRTT + 0.125 * SampleRTT

数据接收窗口

通常在通信的双方都会各自维护一个网络窗口,用于处理数据包的接收。大体如下图所示:
在这里插入图片描述
tcp通信的双方都具有收发数据的能力,因此服务端和客户端之间都会各自维护接收窗口和发送窗口,并且随着窗口的大小通常都是处于一个约等于的状态,因为有可能某一段会在传输过程中对窗口的大小做调整(通过windows的参数来设置),然后该数值在传输过程中可能会有短暂的延时,因此我们说两端的窗口大小会有细微出入。

接收窗口和发送窗口

窗口只有在前面所有的段都确认的情况下才会移动左边界。当在前面还有字节未接收但收到后面字节的情况下,窗口不会移动,并不对后续字节确认。以此确保对端会对这些数据重传。如下图:

滑动窗口已经耗尽
图片

滑动窗口仍有可用空间
图片

流量控制

流量控制方面主要有两个要点需要掌握。一是TCP利用滑动窗口实现流量控制的机制;二是如何考虑流量控制中的传输效率。

所谓流量控制,主要是接收方传递信息给发送方,使其不要发送数据太快,是一种端到端的控制。主要的方式就是返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。

这里面涉及到一种情况,如果B已经告诉A自己的缓冲区已满,于是A停止发送数据;等待一段时间后,B的缓冲区出现了富余,于是给A发送报文告诉A我的rwnd大小为400,但是这个报文不幸丢失了,于是就出现A等待B的通知||B等待A发送数据的死锁状态。为了处理这种问题,TCP引入了持续计时器(Persistence timer),当A收到对方的零窗口通知时,就启用该计时器,时间到则发送一个1字节的探测报文,对方会在此时回应自身的接收窗口大小,如果结果仍未0,则重设持续计时器,继续等待。

拥塞控制

拥塞的发生是因为路由器缓存溢出,拥塞会导致丢包,但丢包不一定触发拥塞。拥塞控制是快速传输的基础。由于tcp协议经常会发送不定长度的数据包,因此在网络中会有可能网络拥塞堵塞的情景。(当多个tcp同时并发进行请求的时候)。

市面上常见的拥塞算法有许多种,这里讲解最为经典的NewReno算法。

一个拥塞控制算法一般包括慢启动算法、拥塞避免算法、快速重传算法、快速恢复算法四部分。
在这里插入图片描述
不同拥塞算法慢启动的逻辑有所不同,经典的 NewReno 慢启动的算法如下:
连接建好的开始先初始化 cwnd = 10,表明可以传 10 个 MSS 大小的数据。
每当收到一个 ACK,cwnd 加 1。这样每当过了一个 RTT,cwnd 翻倍,呈指数上升。
还有一个 ssthresh(slow start threshold),是一个上限。当 cwnd >=ssthresh 时,就会进入“拥塞避免算法”。

Linux 3.0 后采用了 Google 的论文《An Argument for Increasing TCP’s Initial Congestion Window》的建议——把 cwnd 初始化成了 10 个 MSS。 而 Linux 3.0 以前,比如 2.6,Linux 采用了[RFC3390] 的建议,cwnd 跟 MSS 的值来变,如果 MSS < 1095,则 cwnd = 4;如果 MSS >2190,则 cwnd = 2;其它情况下,则是 3。

拥塞避免算法

当 cwnd 增长到 sshthresh 时,就会进入“拥塞避免算法”。拥塞避免算法下 cwnd 成线性增长,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍。这样就可以避免拥塞窗口快速增长的问题。

每收到一个 ack 时 cwnd 的变化:
cwnd = cwnd + 1 / cwnd
快速重传算法
快速重传算法主要用于丢包检测,以便能更快重传数据包,更早的调整拥塞状态机状态,从而达到持续升窗的目的。具体重传策略见第三节 重传机制。

快速恢复算法

当检测到丢包时,TCP 会触发快速重传并进入降窗状态。该状态下 cwnd 会通过快速恢复算法降至一个合理值。从历史发展来看,分为四个个阶段。

BSD 初始版本

收到 3 次重复 ACK,ssthresh 设为 cwnd/2,cwnd = cwnd / 2 + 3;

每收到一个重复 ACK,窗口值加 1;

收到非重复 ACK,窗口设为 ssthresh,退出

优点:在快速恢复期间,可以尽可能多的发送数据缺点:由于快速恢复未完成,尽可能多发送可能会加重拥塞。#### 5.4.2 [RFC3517]版本 1) 收到 3 次重复 ACK,ssthresh 设为 cwnd/2,cwnd = cwnd / 2 + 3; 2)每收到一个重复 ACK,窗口值加 1/cwnd; 3) 收到非重复 ACK,窗口设为 ssthresh,退出。

优点:在快速恢复期间,可以尽少量的发送数据(有利于拥塞恢复),且在快速恢复时间段的最后阶段,突发有利于抢带宽。

缺点:快速恢复末期的突发不利于公平性。

关于拥赛控制的实现算法还有很多种,这里贴出一条链接,感兴趣的朋友可以深入研究下:tcp拥塞控制详解【腾讯技术】

版权声明
本文为[Danny_idea]所创,转载请带上原文链接,感谢
https://blog.csdn.net/Danny_idea/article/details/115268564

Scroll to Top