第三章—传输层
传输层位于应用层和网络层之间,是分层的网络体系结构中重要的部分,该层为运行在不同主机上的应用进程提供直接的通信服务起着至关重要的作用。在这里我们将讨论两个大的问题:将网络层在不同端系统之间的通信服务扩充到运行在两个端系统上不同的应用层进程之间的通信服务(如何实现进城之间通信的可靠传输)和控制传输层实体的传输速度以避免网络拥塞或者从网络拥塞中恢复过来,这里需要考虑的有拥塞的后果和原因以及常见的拥塞控制手段,特别的,我们将了解TCP协议中的拥塞控制。
3.1、概述和传输层服务
传输服务和协议
- 为运行在不同主机上的应用进程提供逻辑通信
- 传输协议运行在端系统
- 发送方:将应用层的报文分成报文段,然后传给网络层
- 接收方:将报文段重组成报文,然后传递给应用层
- 网络层服务:主机之间的逻辑通信
- 传输层服务:进程间的逻辑通信
- 依赖于网络层的服务:延时、带宽
- 对网络层的服务进行增强:数据丢失、顺序混乱、加密
Internet传输层协议
- 可靠的、保序的传输:TCP(传输控制协议)
- 多路复用、解复用
- 拥塞控制
- 流量控制
- 建立连接
- 不可靠、不保序的传输:UDP(用户数据报协议)
- 多路复用、解复用
- 没有为尽全力而为的IP服务添加更多的其他额外的服务
- 都不提供的服务:延时保证、带宽保证
3.2、多路复用与解复用
多路复用/解复用
在发送方主机:从多个套接字接受来自多个进程的报文,根据套接字对应的IP地址和端口号等信息对报文段用头部加以封装(该头部信息用于以后的解复用)
在接收方主机:根据报文段的头部信息中的IP地址和端口号将接收的报文段发给正确的套接字(和对应的应用进程)
多路解复用工作原理
- 解复用作用:TCP或者UDP实体采用那些信息,将报文手段的数据部分交给正确的socket,从而交给正确的进程
- 主机收到IP数据报
- 每个数据报有源IP地址和目标地址
- 每个数据报承载一个传输报文段
- 每个报文段有一个源端口号和目标端口号
- 主机联合使用和将报文段发送给合适的套接字
无连接(UDP)多路复用
- 在接收端,UDP套接字用二元组标识(目标IP地址、目标端口号)
- 当主机收到UDP报文段:
- 检查报文段的目标端口号
- 用该端口号将报文段定位给套接字
- 如果两个不同源的IP地址/源端口号的数据报,但是有相同的目标IP地址和端口号,则被定位到相同的套接字
- 具备相同目标IP地址和目标端口号,即使是源IP地址或源端口号的IP数据报,将会被传到相同的目标UDP套接字上
面向连接(TCP)的多路复用
- TCP套接字:四元组本地标识
- 源IP地址
- 源端口号
- 目的IP地址
- 目的端口号
- 解复用:接收主机用着四个值将来数据报定位到合适的套接字
- 服务器能在一个TCP端口号上同时支持多个TCP套接字
3.3、用户数据报协议:UDP
UDP的特点:
- UDP是无连接的
- UDP发送端和接收端之间没有握手
- 每个UDP报文段都被独立地处理
- UDP使用尽最大努力交付:容易造成报文丢失和送到应用进程的报文乱序
- UDP是面向报文的
- UDP没有拥塞控制
- UDP支持一对一、一对多、多对一和多对多的交互通信
- UDP首部开销小,只有8个字节
TCP提供了可靠数据传输,并且提供了拥塞控制,为什么人们还需UDP呢?事实上,有些应用很适合UDP(因为没有连接过程啊,因为不会受拥塞控制的调节啊,更自由);UDP有以下好处:
- 关于何时、发送什么数据的应用层控制更为精细:这是因为一旦应用程序将数据交给UDP,UDP就会打包将其发送给网络层,不会受到传输层的调节,这在一些实时应用中比较实用;当然,应用程序还可以通过UDP+自主开发一些功能的模式来扩展UDP。
- 无需建立连接:所以就不会引入额外的时延。这也可能是DNS使用UDP而不是TCP的主要原因,如果使用TCP的话,DNS服务将会慢很多;HTTP使用TCP的主要原因是对TCP的可靠性的依赖超过对速度的要求;
- 无需维护连接状态:TCP为了实现可靠数据传输和拥塞控制需要在端系统中维护一些参数,这些参数包括:接收和发送的缓存、拥塞控制参数、确认号和序号;这些参数信息都是必须的;而UDP因为不建立连接,所以自然也就不需要维护这些状态,这就减少了时空开销;
- 分组首部更小:TCP有20字节的首部开销,而UDP只有8字节;
UDP的首部格式
- 源端口:源端口号,在需要对方回信时选用,不需要时刻全为0
- 目的端口:目的端口号。在终点交付报文时必须使用
- 长度:UDP用户数据的长度吗,其最小值是8
- 检验和:检测UDP用户数据报在传输中是否有错
UDP校验和
- 目标:检测在被传输报文段中的差错
- 发送方:
- 将报文段的内容视为16比特的整数
- 校验和:报文段的加法和(1的补位运算)
- 发送方将在校验和放在UDP的校验和字段
- 接收方:
- 计算接收到的报文的校验和
- 检查计算出的校验和与校验和字段是否相等
- 不相等:检查到差错
- 相等:没有检测到差错,但可能还有差错(残存错误)
3.4、可靠数据传输原理(RDT)
可靠数据传输为上层实体提供的服务抽象是:数据可以通过一套可靠的信道进行传输,借助于可靠信道,传输数据就不会受到损坏或者丢失;并且所有数据都可以按照其发送顺序进行交付。而这正是TCP向调用它的应用所提供的服务模型
单方向的可靠数据传输流程大概是这样的:可靠数据传输->不可靠数据传输->不可靠的传输信道->可靠数据接收->上传Data
3.4.1、构造可靠信道的可靠数据传输
一个可靠数据传输协议,将要面对以下问题:分组丢失、分组损坏到达、分组乱序到达。总结可靠传输需要的技术:检验和、序号、定时器、肯定和否定确认分组。
- 经完全可靠信道的可靠数据传输:rdt1.0: 最简答的情况,底层信号完全可靠
- 经具有比特差错信道的可靠数据传输:rdt 2.0
假设所有发送的分组都可以按其发送顺序被接收。基于重传机制的可靠数据传输协议称为自动重传请求协议(ARQ)。增加了ACK和NCK
ARQ协议中还需要另外三种协议功能来处理存在比特差错的情况:差错检测,接收方反馈,重传。
rdt 2.0的发送端每发送一个分组需要等待接收端的确认信号,这种协议被称为停等协议。
- rdt 2.1
rdt 2.0 中有一个致命的缺陷,就是没有考虑到 ACK 和 NAK 分组受损的可能性。
考虑ACK和NAK受损的个两可能性:
- 增加足够的校验和比特
- 当接受到模糊不清的ACK和NAK分组时,只需要重传当前数据分组。这引入了冗余分组
解决这个新问题的一个简单的方法就是在数据分组中添加一个字段,让发送方对其数据分组编号,即将发送数据分组的 序号 放在该字段。于是,接收方只需要检查序号即可确定收到的分组是否一次重传。
- rdt 2.2
如果不发送NAK,而是对上次正确接收的分组发送一个ACK,我们也能实现同样的效果。
发送方接收到对一个分组的两个ACK(冗余ACK)后,就知道接收方没有正确接收到跟在确认两次的分组后面的分组。
rdt 2.2 是在有比特差错信道上实现的一个无NAK的可靠数据传输协议。
rdt 2.1和rdt 2.2的区别在于,接收方此时必须包括由一个ACK报文所确认的分组序号
- 经具有比特差错的丢包信道的可靠数据传输:rdt 3.0
在 rdt 3.0 中,丢包的问题让发送方解决。不管是发送的分组丢失,还是接收方返回的确认分组丢失,只要在经过一定的时延后,让发送方重发该分组即可。
由此产生的 冗余数据分组 则由接收方通过序号处理。为了实现基于时间的重传机制,需要一个倒计时定时器
因为分组序号在 0 和 1 之间交替,因此 rdt 3.0 有时被称为 比特交替协议。
3.4.2、流水线可靠传输协议
- 流水线协议
rdt 3.0 是一个功能正确的协议,但是由于它是一个停等协议,大部分的时间都浪费在等待确认上面,所以性能不好。
解决这种特殊性能问题的一个简单的方法是:不使用停等方式运行,允许发送方发送多个分组而无需等待确认。这种技术被称为 流水线。
要使用流水线技术,则须:
- 增加序号范围。因为要传送多个分组,而每个传输中的分组必须有一个单独的序号。
- 协议的发送方和接收方两端必须能缓存多个分组。发送方至少得能缓存那些已发送但未确认的分组,而接收方或许也需要缓存那些已经正确接收的分组。
- 所需序号的范围和对缓冲的要求取决于数据传输协议如何处理丢失、损坏及延时过大的分组。
流水线的差错恢复有两种基本方法:
- 回退N步
- 选择重传
3.4.3、回退N步(GBN)
在回退N步中,发送方维护一个N——窗口大小和一个base——发送方期待收到的最小待确认分组序号,同样也是窗口的起点,还有一个next Sequence变量,表示上层需要发送分组时,可以使用的序号。这样全部序号就被划分为0-base-1,这一部分的分组是已发送且收到接收方确认的分组,base~next Sequence-1这一部分的分组是已发送但是尚未收到确认的,其中base是尚未收到确认的最小序号;next-1~base+N-1表示当前发送方可以使用的序号,表示一种发送能力;当发送方收到确认号为base的确认分组后就会向前移动窗口,所以回退N步也被称为滑动窗口协议
这是发送方需要维护的数据,同时发送方需要响应的事件有:上层调用、收到ACK、超时事件;
- 上层调用:检查next Sequence是否在窗口之内,如果在,这说明发送方还有发送能力,发送之;
- 收到ACK:回退N步策略对序号为n的分组采取累积确认的方式,即当收到序号为n的ACK时,表明序号小于等于n的分组全部到位;发送方收到的ACK毕竟来自接收方,收到ACK的情况还得看接收方如何发送;
- 超时事件:如果发生超时事件,那么发送方会重发所有已发送但是未确认的分组,即分组号在base和next sequence-1之间的所有分组;这也是为什么叫“回退N步”,如果收到一个ACK,则定时器会重行启动;如果没有待确认的分组,定时器将被终止;
在GBN中发送方看到的序号
N为窗口长度,GBN协议也经常被称为
运行中的GBN:
窗口长度为4个分组的GBN协议的运行情况。因为该窗口长度的限制,发送方发送分组0-3,然后在继续发送之前,必须等待直到一个或多个分组被确认。当接收到每一个连续的ACK(例如ACKO和ACK1)时,该窗口便向前滑动发送方便可以发送新的分组(分别是分组4 和分组5)。在接收方,分组2丢失,因此分组34和5被发现是失序分组并被丢弃。
3.4.4、选择重传
选择重传(SR)协议通过让发送方仅重传那些它怀疑在接收方出错(即丢失或受损)的分组而避免了不必要的重传。这种个别的、按需的重传要求接收方逐个地确认正确接收的分组。再次用窗口长度N来限制流水线中未完成、未被确认的分组数。然而,与GBN不同的是,发送方已经收到了对窗口中某些分组的ACK。图3-23 显示了SR发送方看到的序号空间。
选择重传(SR)发送方和接收方的序号空间
SR操作
SR 接收方将确认一个正确接收的分组而不管其是否按序。失序的分组将被缓存直到所有丢失分组(即序号更小的分组) 皆被收到为止,这时才可以将一批分组按序交付给上层。
3.5、面向连接的传输TCP
3.5.1、TCP协议的特点
- TCP是面向连接的运输层协议
- 点对点
- 全双工数据
- 发送和接收缓存
- 有流量控制
- 可靠的、按顺序的字节流
3.5.2、TCP的报文段
TCP报文段结构,从整体上来说由首部+数据字段组成;其中数据字段来自应用层,其长度不能大于MSS;首部的常规长度为20字节,但是值得注意的是,TCP首部是可变长的;TCP首部是以32比特为单位组织的,如下图:
- 源端口和目标端口:各占2个字节
- 序号:占4个字节。也称为
- 确认号:占4字节,期望收到对方下一个报文段的第一个数据字节的序号
- 数据偏移:占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远
- 保留:占6字节,为今后使用
- 紧急URG:当URG=1时,表明紧急指针字段有效。当URG=1时告诉TCP有紧急数据要传送
- 确认ACK:仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效
- 检验和:占2字节,检验和字段检验范围包括首部和数据两部分
- 选项:长度可变,最长可达40个字节。就是一个选项
TCP的序号和确认号
3.5.3、TCP的往返延时(RTT)和超时
- 超时:比RTT要长
- 太短:太早超时,引发不必要的重传
- 太长:对报文段丢失反应太慢,消极
设置往返延时
加权平均往返时间RTT:新的RTT = (1 - a) * 旧的RTT + a * 新的RTT样本
设置超时
加权平均往返时间RTT:新的RTT = (1 - b) * 旧的RTT + b * 新的RTT样本
3.5.4、TCP:可靠数据传输
- TCP在IP不可靠服务的基础上建立RDT
- 管道化的报文段:GBN或者SR
- 累计确认(GBN)
- 单个重传定时器
- 通过一下事件触发重传
- 超时(只重发最早未确认段)
- 重复的确认
3.5.5、TCP重传
3.5.6、TCP流量控制
3.5.7、TCP连接管理
TCP运输连接的建立和释放时每一次面向连接必不可少的过程,分为三个阶段:连接建立、数据传送、连接释放
TCP连接建立需要解决:
- 要使每一方能够通知对方的存在
- 要允许双方能够协商一些参数
- 能够对运输实体资源(如缓存大小、连接表中的项目)进行分配
TCP三次握手
第一次握手
客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。
PS1 :SYN=1,ACK=0表示该报文段为连接请求报文。
PS2 :x为本次TCP通信的字节流的初始序号。
TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。
该应答发送完成后便进入SYN-RCVD状态。
PS1 :SYN=1,ACK=1表示该报文段为连接同意的应答报文。
PS2 :seq=y表示服务端作为发送者时,发送字节流的初始序号。
PS3 :ack = x+1表示服务端希望下一个数据报发送序号从x+1开始的字节。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。
客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
TCP连接建立的三次握手过程
TCP连接的释放
第一次挥手
若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为:
FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态。
- FIN=1表示该报文段是一个连接释放请求。
- seq=u,u-1是A向B发送的最后一个字节的序号。
第二次挥手
B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含:
ACK=1,seq=v,ack=u+1。
- ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答。
- seq=v,v-1是B向A发送的最后一个字节的序号。
- ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。
- A收到该应答,进入FIN-WAIT-2状态,等待B发送连接释放请求。
第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。
第三次挥手
当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。B便进入LAST-ACK状态。
第四次挥手
A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。
为什么A要先进入TIME-WAIT状态,等待时间后才进入CLOSED状态?
为了保证B能收到A的确认应答。
若A发完确认应答后直接进入CLOSED状态,那么如果该应答丢失,B等待超时后就会重新发送连接释放请求,但此时A已经关闭了,不会作出任何响应,因此B永远无法正常关闭。
3.6、拥塞控制原理
3.6.1、拥塞原因与代价
计算机网络拥塞的原因是因为网络中的分组太多,而链路带宽和路由器缓存容量都是有限的;
- 当分组的到达速率接近链路容量时,分组将经历巨大的排队时延;
- 发送方必须执行重传已补偿因为缓存溢出而丢弃的分组
- 发送方遇到大时延时所进行的不必要重传会引起路由器利用其链路带宽来转发不必要的分组副本。
- 当一个分组沿着一条路径被丢弃时,每个上游路由器用于转发该分组到丢弃该分组而使用的传输容量最终被浪费掉了;
3.6.2、拥塞控制的方法:
- 拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况;
- 网络层并没有向传输层拥塞控制提供显式支持,即便网络中存在拥塞,端系统也必须通过对网络行为的观察(如分组丢失与时延)来判断;TCP必须通过端到端的方法解决拥塞控制,因为IP层不会像端系统提供有关网络拥塞的反馈信息。TCP报文段的丢失(超时或者收到3次冗余确认而得知)被认为是网络拥塞的一个迹象,TCP将相应地减小窗口长度;
- 流量控制:流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收。
- 网络层会向发送方提供关于网络中拥塞状态的显式反馈消息;比如使用一个比特位来指示网络是否拥塞;拥塞信息从网络反馈到发送方一般有两种方式,其中直接反馈信息可以由网络路由器发送给发送方,这种方式的通知通常采用一种拥塞分组的形式;第二种形式的通知是路由器标记或者更新从发送方到接收方的分组中的某个字段来指示拥塞的产生,然后由接收方向发送方通知该网络发生了拥塞。
3.7、TCP拥塞控制
TCP必须使用端到端的拥塞控制而不是网络辅助的拥塞控制,因为IP并不会向端系统提供显式的网络拥塞反馈;TCP所采用的方法是让每一个发送方根据其所感知的网络拥塞程度来限制其能向连接发送流量的速率;如果TCP判断网络通畅,那么它会提高发送速率,如果TCP判断网络拥塞,那么它会限制发送速率;需要解决三个问题:TCP如何限制发送速率?TCP如何感知网络拥塞程度?TCP该以何种算法改变其发送速率?
我们知道,TCP连接的双方都维护着两个窗口,其中一个是作为发送方的窗口,也被称为拥塞窗口cwnd,它对发送方能向网络中发送流量的速率进行了限制,$last sent-last acked<=min{cwnd,rwnd}$;另一个自然是作为接收方的接收窗口。
我们假设,发送方可以在RTT时间范围内连续发送cwnd个字节的数据,所以发送速率即为cwnd/RTT;发送方通过调整窗口大小来对发送数据的速率加以控制
我们将TCP发送方的丢包事件定义为:要么超时,要么收到接收方的3个冗余ACK;如果网路拥塞,那么网络中的路由器就会发生缓存溢出,进而导致数据报被丢弃,然后就会引起发送方的丢包事件;此时,TCP发送方就可以认为TCP连接出现了拥塞
另外,TCP将接收方发送的ACK视为网络通畅的标志,如果ACK到达的速率较高,那么TCP的拥塞窗口就会以较高的速率扩大,如果ACK到达的速率较慢,那么TCP拥塞窗口的增加速度也会较慢;因为TCP使用ACK对拥塞窗口做出调节,所以也别称为自计时的;
TCP发送速率过高,网络就很容易拥塞;TCP发送方如果过于谨慎,那么就无法充分利用网络的带宽;所以TCP如何设置自己的发送速率,才能使得网络不会拥塞而且还充分利用带宽呢?关于这个问题,TCP使用下列指导性原则回答这些问题:
- 一个丢失的报文段意味着拥塞,因此当丢失报文段时应当降低TCP发送方的速率;
- 一个确认报文段指示该网络正在向接收方交付发送方的报文段,因此,当收到对先前报文段的确认时,可以增加发送方的速率;
- 带宽检测;TCP调节器传输速率的策略是增加其速率以响应到达的ACK,除非出现丢包,此时才减少发送速率;以为网络中没有明确的拥塞控制状态信令,ACK和丢包事件充当了隐式信号.
TCP拥塞控制算法
该算法包括三部分:慢启动、拥塞避免、快速恢复
慢启动
TCP连接在开始的时候,其cwnd常设置为一个MSS,然后在慢启动状态每收到一个ACK,cwnd就增加一个MSS;这样的话,在慢启动阶段,发送速率是指数增加的(1,2,4,8…)
何时结束这种指数增长?有三种情况:发送了超时、发生了冗余ACK以及cwnd达到ssthresh。ssthresh是慢启动阈值的速记;在慢启动阶段,如果发生了超时事件,那么ssthresh就被设置为当前cwnd的一半,然后将cwnd置为1;当cwnd逐步增加到ssthresh时,再翻倍增加cwnd就有一点鲁莽了,所以此时TCP结束慢启动,进入拥塞避免模式。在拥塞避免模式里,TCP将更谨慎地增加cwnd;如果收到冗余ACK,那么TCP会做一次快速重传,然后进入快速恢复阶段;
拥塞避免
一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时的一半,所以TCP在每个RTT中,只将cwnd增加一个1个MSS大小;也就是说在拥塞避免阶段,cwnd是线性增加的;
当出现超时时,TCP将cwnd设置为1,然后将ssthresh更新为cwnd的一半;当收到冗余ACK时,TCP将cwnd减半,然后将ssthresh置为cwnd值的一半,并且进入快速恢复状态;
快速恢复
- 在快速恢复阶段,对于引起TCP进入该状态的缺失报文段,每收到一个ACK,cwnd增加一个MSS;最终,当对丢失报文段的一个ACK到达时,TCP降低cwnd后进入拥塞避免状态;如果出现超时事件,快速恢复在执行如同慢启动和拥塞避免中相同动作后,进入慢启动状态。