文章目录
第三章、传输层——TCP/UDP
1.1、构造可靠数据传输协议
1.1.1、rdt服务模型和服务实现
1.1.2、经完全可靠信道的可靠数据传输:rdt1.0(假设了底层信道完全可靠)
1.1.3、rdt2.0(假设了底层通信信道为比特受损模型)
因为rdt1.0版本中,底层通信信道不可能完全可靠,我们这里假设底层通信信道是可能是比特受损的模型,它不会出现比特发送顺序变化的情况。
那么我们如何保证可靠的传输呢?如果出现了报文比特丢失受损的情况,我们需要让发送方知道发送的比特受损了,所以接受方需要给我们发送方一些关键的确认信息
- 肯定确认(positive acknowledgment)(“OK”)——告知没有受损
- 否定确认(negative acknowledgment)(“NO”)——告知受损,需要重发一次
基于这样的重传机制的可靠数据传输协议称为:自动重传请求(Automatic Repeat reQuest,ARQ)协议。
ARQ协议还需要额外的三种协议来处理存在比特差错的情况:
- 差错检测——类似于UDP的差错检测,我们需要一种机制来使接受方检测到何时出现了比特差错,因而报文段里面需要添加额外的字段比特来进行该功能的实现,这些比特被汇集于rdt2.0数据分组的分组检验和字段中
- 接受方反馈——发送方需要了解接受方的接受情况,那么就需要接受来自接受方的反馈信息。成功接受完整分组,则接受方发送“肯定确认”(ACK分组),没有接受或者接收到差错比特分组,则发送“否定确认”(NAK分组),该分组大小为1比特,0标识否定,1标识肯定
- 重传——接受方收到差错分组,发送方需要重新发送分组。
1.1.4、rdt2.1——(如何处理确认分组受损时的情况)
第二种可能实现的代价太高我们不采用。最后我们采用第三种。
rdt2.1里面,接受方发送的ACK或者NAK分组是响应发送方最近一次发送的数据分组而生成的,所以不含有数据分组序号
数据分组序号包含在发送方发送的分组数据的序号(sequence number)字段里面
FSM:
1.1.5、rdt2.2——无NAK的协议
我们不需要两种确认消息,毕竟确认消息类型越多逻辑越复杂。我们在ACK里面添加上被确认的分组序列号就可以实现了NAK的功能。如ACK0,ACK1,发送方就能知道是否被接受了,发送方发送分组0,收到的ACK却是ACK1,则说明分组0没有被收到,则重新发送分组0。
FSM:
目前我们已经使用了检验和、序号、ACK分组、重传等机制。
1.1.6、rdt3.0——信道既可能发生比特受损也可能丢失分组的情况
假定现在除了比特受损,底层信道还可能发送丢包,那么我们需要处理的两个问题是:
- 怎么样检测丢包
- 发送丢包后我们要做什么?
如果发送丢包——我们无法收到ACK
如果发生ACK丢失——我们无法收到ACK
如果收到的ACK很久很久,无法忍受——我们相当于没有收到ACK
综上所述,我们的解决办法就是重传,我们只需用设置一个超时时间,如果我们在设定的时间内没有接收到ACK或者在发送分组的时候定时器就已经超时了或者ACK接受超时,或者ACK丢失,直接重传分组。
也就是说我们所有的问题都可以解决了!
FSM:
接受方的FSM同rdt2.2的接受方,无需改变。
典型的几种工作状态:
思考下面的状态机应该如何做:
1.1.7、rdt3.0性能分析
1.2、流水线可靠数据传输协议
正因为rdt3.0的性能上的缺陷,我们需要改善,那么如何改善呢?
1.2.1、回退N步(Go-Back-N协议,GBN协议又叫滑动窗口协议,累积确认)
通过JAVA小程序演示:
从上面,我们定义的值含义如下:
- base——基序号,定义为最早未确认分组的序号
- nextseqnum——下一个序号,定义为最小的未使用序号(即下一个待发送分组的序号)
- [base,nextseqnum-1]段对于已经发送但未被确认的分组
- 窗口长度为N(windows size)
原书描述为:
扩展FSM:
关于发送方,需要具备的:
书本描述:
接受方:
GBN协议
GBN协议综合了TCP可靠传输协议构建时遇到的所有技术——使用序号、累积确认、检验和超时/重传。
1.2.2、选择重传(SR)——选择确认
GBN协议解决了停等协议的性能问题,但是它本身也存在性能问题。
因而我们需要选择根据丢失分组的必要性来选择重传——SR协议,选择重传协议(Selective Repeat协议)。
定义:
对应发送方和接受方的动作:
实际例子:
考虑困境——发送方和接受方缺少同步带来的严重后果(发送方和接受方不总是看到一样的结果,窗口不一定总是一致):
1.2.3、分组重排问题
1.3、面向连接的传输——TCP
根据前面学习的面向连接的可靠运输协议,TCP使用了很多技术:
- 差错检测
- 重传
- 累积确认
- 定时器
- 序号和确认号的首部字段
- ·······
1.3.1、TCP连接
TCP连接是面向连接的(connection-oriented),应用程序进程可互发数据前,两个进程必须先握手——相互发送一些预备报文段,以建立确保数据传输的参数,连接的双方都将初始化TCP连接的许多TCP状态变量。
TCP连接不是一条像电路交换网络中的端到端的TDM(分时复用)或者FDM(分频复用),而是一条逻辑连接,其共同的状态保留在两个通信端系统的TCP程序中,中间的网络元素不会维持TCP连接状态
TCP连接是全双工服务,点对点(point-to-point)连接,发送的同时可接收数据,连接仅允许建立在两台主机之间,即单发送方和单接受方。
连接的建立——三次握手过程
发送和接受数据
1.3.2、TCP报文段结构
下面我们重点来学习序号和确认号:
1、序号
TCP把数据看成一个无结构的、有序的字节流。
- 序号是建立在传送的字节流之上的,而不是建立在传送的报文段序列之上
- 一个报文段的序号(sequence number for a segment)因此是该报文段首字节的字节流编号
例子:
2、确认号
1.3.3、往返时间的估计和超时间隔的设定
我们使用超时重传机制实现可靠传输——处理报文段的丢失问题,但是超时的时间具体是多少呢?每个未确认的报文段是否需要各设一个定时器?
我们如果要设定超时时间,我们必须知道当前链路的往返时间的估值。
估计往返时间
网络是波动的,不同时间拥塞情况不同进而导致往返时间的波动,TCP里面采用:
- 样本RTT(sample RTT)——某个报文段被发出到对该报文段的确认被接受之间的时间量
- 测量样本RTT——仅在某个时刻做一次SampleRTT的测量,而不是为每个发送但尚未确认的报文段测量一个SampleRTT,即任意时刻,仅为一个已经发送的但目前尚未被确认的报文段估计SampleRTT
- 决不为已被重传的报文段计算SampleRTT,仅为传输一次的报文段测量SampleRTT
因为路由器的拥塞和端系统的负载变化,这些报文段的SampleRTT值会随之波动导致任何给定的SampleRTT也许都是非典型的(无代表性),所以这里采用了加权平均的办法,得到EstimatedRTT:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2N7jQ8IM-1613128577692)(C:\Users\86158\AppData\Roaming\Typora\typora-user-images\image-20210209163445505.png)]
我们除了测量RTT,我们还要测量RTT的变化,得到一个RTT偏差——DevRTT,用于估算SampleRTT一般会偏离EstimatedRTT的程度:
设置和管理重传超时间隔
从数学思维上,我们能很快的知道,超时间隔应该等于均值加上一个波动范围的估计值,因而计算公式如下:
实际上并不是所有的TCP实现都是超时才重传,有些版本的TCP使用来隐式NAK机制,即没有超时之前如果连续接受到三个对于某个特定报文段的冗余ACK作为对后面报文段的一个隐式NAK,从而在超时之前就触发对该报文的重传。
1.3.4、可靠数据传输
我们先来看一下高度简化的TCP发送方描述(发送方只用超时来恢复报文段的丢失):
这种高度简化的版本存在以下古怪的情况——造成不必要的重传:
对于情况1,假设这个丢失是因为网络拥塞导致的,那么这个时候会一直拥塞导致了超时事件一直触发,重传一直触发。于是我们想到了一个策略——超时间隔加倍。
我们动态地对超时间隔进行加倍修改,而不是从EstimatedRTT和DevRTT的推算里面得出,发送了重传,我们就将超时间隔加倍,因此每次重传,超时间隔呈指数型增长。但是一旦收到一个ACK确认或者上层的应用数据,我们的超时间隔将由最近的EstimatedRTT和DevRTT值计算得到。这种方式,提供了一个形式受限的拥塞控制。每个发送方的重传都是经过越来越长的时间间隔后进行的。
上面的方式存在一个问题,就是重传的时候,超时间隔加倍了,但是如果这样的话,我们下一次发送重传的时间大大加长了,这就会添加端到端的时延,比如我们一直没有收到ACK100,导致我们把整个超时时间等完了才重发丢失的报文段,这样时延变大很多。我们采取了一个比较机制的方法——因为接受方只发送它已经确认的分组ACK,那在这个超时时间端内如果我们连续收到的ACK都是冗余ACK,即收到ACK99,ACK99,ACK99,发送方之前就知道某个报文段已经确认了,现在连续三次收到该报文段的确认ACK,说明有报文段丢失了!也就是说我们可以提前重发了。
这就是快速重传!
现在我们来考虑一个问题:TCP是一个GBN协议还是SR协议?
- 从TCP发送方只需用维持发送过但未被确认的字节的最小序号(SendBase)和下一个要发送的字节序号(NextSeqNum)角度,TCP像是GBN风格的协议
- TCP实现会把正确接受但失序的报文段缓存起来,这像SR协议
- 考虑重传的问题,假设发送方发送了1,2,···,N,并且所有的分组都是按序无差错到达接受方。进一步假设有一个分组n<N的确认报文丢失了而其余的确认报文都在超时之前达到发送端。
- 如果是GBN,则会重传分组n,还会重传其后的n+1,n+2,···,N分组
- TCP协议,如果报文段n+1的确认报文段在报文段n超时之前到达,则不重传报文,否则重传报文段n
从上面的分析我们看得出,TCP使用的是选择确认(selective acknowledgement),它允许TCP接受方有选择地确认失序报文段,而不是累积确认最后一个正确接受的有序报文段。所以TCP的差错恢复机制最好被分类为GBN协议与SR协议的混合体。
1.3.5、流量控制
现在假设一个问题——发送方不断的快速地发送报文段给接受方,接受方则将数据缓存,接受方的上层则读取缓存中的数据,如果接收方读取数据的速度很慢,以至于读出不够接受地快,导致接受方的缓存溢出。
所以TCP为它的应用程序提供了**流量控制服务(flow-control service)**来消除发送方使得接受方缓存溢出的可能性,该服务是一个速度匹配服务,使得发送方发送速率和接受方接受速率相匹配。
因为IP网络的拥塞,导致了发送方为了避免大量重传分组而受到遏制,这种控制叫做拥塞控制,它和流量控制有着本质的区别
如何实现流量控制?
还记得我们说过TCP报文里面的接受窗口字段是用来实现流量控制的吗?发送方和接受方都维护一个接受窗口,告知对方自己还剩下多少缓存空间,从而协调速率。
1.3.6、TCP连接管理
我们首先来回顾以下TCP报文段的结构:
从图中我们知道RST、SYN和FIN用于连接的建立和拆除,它属于6比特的标志字段里面的位。
TCP连接的建立——三次握手
为什么叫做三次握手,因为他们之间发送了三个分组才完成建立TCP连接。
TCP连接的关闭——四次挥手
他们之间发送了4个分组。
TCP状态
RST标志位重置报文
几个问题:
1、在结束连接的过程中,为什么在收到服务器端的连接释放报文段之后,客户端还要继续等待2MSL之后才真正关闭TCP连接呢?
这里有两个原因:第一个是:需要保证服务器端收到了客户端的最后一条确认报文。假如这条报文丢失,服务器没有接收到确认报文,就会对连接释放报文进行超时重传,而此时客户端连接已关闭,无法做出响应,就造成了服务器端不停重传连接释放报文,而无法正常进入关闭状态的状况。而等待2MSL,就可以保证服务器端收到了最终确认;若服务器端没有收到,那么在2MSL之内客户端一定会收到服务器端的重传报文,此时客户端就会重传确认报文,并重置计时器。
第二个是:存在一种“已失效的连接请求报文段”,需要避免这种报文端出现在本连接中,造成异常。
这种“已失效的连接请求报文段”是这么形成的:假如客户端发出了连接请求报文,然而服务器端没有收到,于是客户端进行超时重传,再一次发送了连接请求报文,并成功建立连接。然而,第一次发送的连接请求报文并没有丢失,只是在某个网络结点中发生了长时间滞留,随后,这个最初发送的报文段到达服务器端,会使得服务器端误以为客户端发出了新的请求,造成异常。
2、若通信双方同时请求连接或同时请求释放连接,情况如何?
这种情况虽然发生的可能性极小,但是是确实存在的,TCP也特意设计了相关机制,使得在这种情况下双方仅建立一条连接。双方同时请求连接的情况下,双方同时发出请求连接报文,并进入SYN-SENT状态;当收到对方的请求连接报文后,会再次发送请求连接报文,确认号为对方的SYN+1,并进入SYN-RCVD状态;当收到对方第二次发出的携带确认号的请求报文之后,会进入ESTAB-LISHED状态。 双方同时请求释放连接也是同样的,双方同时发出连接释放报文,并进入FIN-WAIT-1状态;在收到对方的报文之后,发送确认报文,并进入CLOSING状态;在收到对方的确认报文后,进入TIME-WAIT状态,等待2MSL之后关闭连接。需要注意的是,这个时候虽然不用再次发送确认报文并确认对方收到,双方仍需等待2MSL之后再关闭连接,是为了防止“已失效的连接请求报文段”的影响
1.4、拥塞控制原理
1.4.1、网络拥塞的三种情况
情况1:两个发送方和一台具有无限缓存的路由器
我们这里还将假设TCP不会执行差错恢复(重传)、流量控制、拥塞控制。
λin表示应用程序将初始数据发送到套接字的速率。
情况2:两个发送方和一台具有有限缓存的路由器
路由器具有有限缓存则结果是当一个分组到达一个已满的缓存时会被丢弃,其次我们假设可靠数据传输,则一个包含运输层报文段的分组在路由器被丢弃,它最终会被发送方重传。
λin表示应用程序将初始数据发送到套接字中的速率,λin‘表示运输层向网络中发送报文段(含有初始数据或者重传数据)的速率(它有时被称为网络的供给载荷,offered load)。
情况3:4个发送方和具有有限缓存的多台路由器及多跳路径
1.4.2、拥塞控制——两种主要的拥塞控制方法
1.5、TCP拥塞控制——端到端拥塞控制
我们已经知道常用的拥塞控制有两种——端到端拥塞控制和网络辅助拥塞控制。
TCP采用的是端到端拥塞控制,它让每一个发送方根据所感知的网络拥塞程度来限制其连接发送流量的速率。如果发现网路没有拥塞,则发送方加大发送速率,如果出现拥塞则降低发送速率。但是这样提出了三个问题需要我们具体解决:
- 一个TCP发送方如何限制它向其连接发送流量的速率
- 一个TCP发送方如何感知到它从目的地之间存在拥塞
- 当发送方感知到拥塞时,采用何种算法能合理地有效地调整发送速率
问题1:一个TCP发送方如何限制它向其连接发送流量的速率
问题2:一个TCP发送方如何感知到它从目的地之间存在拥塞
如果网络没有拥塞时:
问题3:当发送方感知到拥塞时,采用何种算法能合理地有效地调整发送速率
当网络拥塞了,我们要降低发送速率,但是降低多少我们不知道,网络不拥塞,我们可以提高发送速率,但是提高多少我们也不知道。我们必须有一个策略,使得网络带宽利用率尽可能地大。
1.5.1、TCP拥塞控制算法
广受赞誉的TCP拥塞控制算法(TCP congestion control algorithm)包括3个主要的部分:
- 慢启动(强制实现)
- 避免拥塞(强制实现)
- 快速恢复(可选实现)
1.慢启动
从这里我们先看拥塞控制TCP的状态机:
一个实例:
2.拥塞避免
3.快速恢复
4、回顾和吞吐量计算、公平性
现在我们回顾TCP拥塞控制,我们忽略开始时的慢启动阶段,假定丢包由3个冗余的ACK而不是超时,则TCP的拥塞控制是:每个RTT内cwnd线性(加性)增加1MSS,然后出现了3个冗余ACK事件时,则cwnd减半(乘性减)。因此TCP拥塞控制被称为——加性增、乘性减(Additive-Increase,Multiplicative-Decrease,AIMD)拥塞控制方式。
计算TCP吞吐量的宏观描述——高度简化的模型:
公平性
TCP的AIMD算法是一种公平的算法,关于公平的定义如下:
从TCP的观点来看,运行在UDP上的多媒体应用是不公平的,因为他们不与其他连接合作,也不适时调整其传输速率。
1.5.2、典型的TCP拥塞控制算法实现(Reno、google的BBR)
总的实现算法有:
参考链接:Google BRR 维基百科
最早的算法实现是TCP Tahoe 和 Reno。我们这里看Google的BBR算法:
以往大部分拥塞算法是基于丢包来作为降低传输速率的信号,而BBR则基于模型主动探测。该算法使用网络最近出站数据分组当时的最大带宽和往返时间来创建网络的显式模型。数据包传输的每个累积或选择性确认用于生成记录在数据包传输过程和确认返回期间的时间内所传送数据量的采样率。[39]该算法认为随着网络接口控制器逐渐进入千兆速度时,与缓冲膨胀相关的延迟相比丢包更应该被认为是识别拥塞的主要决定因素,所以基于延迟模型的拥塞控制算法(如BBR)会有更高的吞吐量和更低的延迟,可以用BBR来替代其他流行的拥塞算法,例如CUBIC。Google在YouTube上应用该算法,将全球平均的YouTube网络吞吐量提高了4%,在一些国家超过了14%。
的每个累积或选择性确认用于生成记录在数据包传输过程和确认返回期间的时间内所传送数据量的采样率。[39]该算法认为随着网络接口控制器逐渐进入千兆速度时,与缓冲膨胀相关的延迟相比丢包更应该被认为是识别拥塞的主要决定因素,所以基于延迟模型的拥塞控制算法(如BBR)会有更高的吞吐量和更低的延迟,可以用BBR来替代其他流行的拥塞算法,例如CUBIC。Google在YouTube上应用该算法,将全球平均的YouTube网络吞吐量提高了4%,在一些国家超过了14%。