《计算机网络》读书笔记——运输层

发布于 2019-05-15  867 次阅读


前言

众所周知在计算机网络的底部,网络层(数据报电路)仅向上提供简单灵活、无连接,尽最大能力的交付的数据报(IP数据报)服务。因此想要实现各种更加高级的特性(可靠交付、差错校验、流量控制等等)就必须在各个端(主机)之间(运输层)实现更加细致的协议来进行约束与控制,反映到实际上就是增加了许多首部字段的约束以及对字段使用的一些算法。

运输层主要有两个协议:

  • 用户数据报协议(User Datagram Protocol)
  • 传输控制协议(Transmission Control Protocol)
UDP TCP
连接 无连接 面向连接
传输单位 数据报(面向报文) 报文段(面向字节流)
特性 简单的差错校验;尽最大努力交付(IP层特性,UDP数据报的首部并没有添加更多字段来约束,因此首部开销也更小);提供一对一、一对多、多对一、多对多的通讯(各类广播);无序 提供可靠的交付;全双工的通信;点对点通讯;拥塞控制;流量控制;有序

UDP

UDP数据报首部

共8字节

''

  • 源端口源端口号。在需要对方回信时选用。不需要时可用全0。
  • 目的端口目的端口号。这在终点交付报文时必须使用。
  • 长度UDP用户数据报的长度,其最小值是8(仅有首部)。
  • 检验和检测UDP用户数据报在传输中是否有错。有错就丢弃。

UDP对应用程序下发的报文不作任何处理,仅添加首部后向下交付给IP层,若报文太长,则可能在IP层分片,拉低效率;若太短则数据区占整个报文比例太小。UDP仅仅提供了有限的差错校验(相较IP层的首部校验,UDP多了数据部分)以及一些简单的功能,因此首部简单,开销小。

TCP

TCP报文首部

报文首部前20个字节固定,后面4n个字节是根据需要而增加的选项,因此TCP报文首部最小为20个字节

'Snipaste_2019-05-14_09-50-07'

报文首部各字段简介

  • 源端口与目的端口

    在报文首部中各占2字节,传输层依赖端口实现进程与进程之间端到端的通讯,与UDP类似,TCP依赖端口实现分用复用的功能。

  • 序号

    占4字节,范围为[0,(2^32)-1],即一共2^32个序号。序号使用mod 2^32进行计算。TCP是面向字节流的,字节流中每一个字节都按照顺序编号。整个要传送的字节流的其实序号必须在连接建立时设置,首部中的序号指的是本次发送字节流的第一个字节的序号。

  • 确认号

    占4字节,代表期望收到对方的下一个报文段的第一个数据字节的序号,若确认号=N,则表明:到N-1为止的所有数据都已经正确收到。

  • 数据偏移

    占4位,指出TCP报文段的数据起始位置距离整个TCP报文段的起始位置有多远,即实际上指出的是TCP报文段首部的长度。由于数据偏移字段长度的限制,也限制了TCP报文首部的最大长度不能超过60字节(4位二进制最大能表示的十进制数为15,而数据偏移单位为32位,即最大480位,则为60字节),即选项长度不能超过40字节。

  • 保留

    占6位,保留今后使用,目前置为0。

  • 紧急URG(URGent)(与接下来5个皆为Flag标志位,各占1位)

    当一个报文段首部中URG位被置为1时,表明此报文段中有紧急信息应该被优先传送,与紧急指针配合使用以指出紧急数据的末尾位置,发送方会将此部分数据放在本报文段数据的最前面。

  • 确认ACK(ACKnowledgment)

    当ACK位置为1时,确认号才有效,TCP规定在建立连接后的所有传送报文的ACK位都必须置为1(许多特性依赖于确认号字段)。

  • 推送PSH(PuSH)

    在发送方希望立即获得响应,从而将PSH置为1后,接收方收到此报文后将直接向上交付(而不是等待接受缓冲区满了之后向上交付)。

  • 复位RST(ReSeT)

    当RST置为1时表明当前TCP连接出现不可恢复的严重差错(主机崩溃或其他原因),因此必须释放当前连接后再重新建立连接,RST也可以用于拒绝非法报文段与拒绝打开一个链接。

  • 同步SYN(SYNchronization)

    通常在建立连接时使用,用于同步序号。

  • 终止FIN(FINis)

    用于释放链接,表明此报文段的发送方已经没有更多的数据需要发送,请求关闭连接。

  • 窗口

    占2字节,范围为[0,(2^16)-1],窗口值指的是本报文发送方的接收窗口大小,即告知对方他下一次发送的报文段应当小于这个值(接收缓存是有限的),窗口值作为接收方让发送方设置其发送窗口大小的依据其大小常常在变化。

  • 检验和

    占2字节,检验包括首部和数据区,在计算检验和时要给报文段添加伪首部来辅助计算。

  • 紧急指针

    占2字节,在URG置为1时才有意义,指出了本报文段中紧急数据的长度,值得注意的是,当窗口值为0时,仍然可以发送紧急数据。

  • 选项

    长度可变,最大值为40字节。

    TCP最初只规定了最大报文段长度MSS作为选项,该字段指出每一个TCP报文段中数据字段的最大长度,该字段的意义在于提高网络的利用率,即选取一个在IP层不会被再分片且对方能接收的最大长度以提高整个报文中数据字段所占的比例。

    随着互联网的发展,又陆续增加了几个选项,例如窗口扩大与时间戳。

    窗口扩大选项是为了扩大窗口值,首部字段中窗口长度为16位,因此最大窗口大小为64k字节,这对于一些高延时高带宽的信道来说是不够的(需要提高每次传输的大小来提高效率)。窗口扩大选项在双方建立连接时进行协商。

    时间戳选项占10字节,最主要的字段是时间戳值字段(4字节)和时间戳回送回答字段(4字节)。时间戳选项的两个最主要的用途为:计算往返时间RTT与方式首部字段中的序号绕回(序号重复时能使用时间戳进行区分)。

可靠传输实现

滑动窗口机制

在收到确认报文后,接收方依据报文首部中的窗口值以及确认号构造出发送窗口。例如,现假定A收到了B发来的确认报文段,其中窗口是20字节,而确认号是31(这表明B期望收到的下一个序号是31,而序号30为止的数 据已经收到了)。根据这两个数据,A就构造出自己的发送窗口。

'Snipaste_2019-05-14_12-07-16'

A的发送窗口含义为,可以在未收到B确认报文的情况下将窗口内的全部数据发送出去,凡是已经发送的数据在未收到确认之前都必须保留在窗口中以便后续超时重传。

窗口中的序号表示允许发送的序号,可以看出窗口值越大,则允许在未收到确认之前能发送的序号就越多,则传输效率就越高,发送窗口值主要由确认报文首部中的接收方接收窗口值与当前的网络拥塞程度来决定。窗口大小可以收缩,但并不建议这样做。'Snipaste_2019-05-14_12-08-03'

如图所示,接受窗口必须按序收到后沿开始的数据才可以往前移动,假定此时收到31,则接收窗口和发送窗口都将往前移动。

''

下图画出发送方维持发送缓存与发送窗口以及接收方维持接受缓存与接收窗口,需要强调的两点是,缓存空间与序号都是有限的,将被循环使用;实际的缓存与窗口数值是非常大的。

''

可以看出发送窗口通常是发送缓存的一部分,他们的后沿相互重合,发送缓存通常用来存放:1)发送应用程序传送给发送方TCP准备发送的数据;2)TCP已经发送但尚未收到确认的数据;应用程序应当控制写入缓存的速度来保证缓存不会溢出。

接收方缓存用来暂时存放:1)按需到达的、但尚未被接受应用程序读取的数据;2)未按时到达的数据;若收到的分组未能通过差错校验将会被丢弃,若接受应用程序未能及时讲缓存中数据读出,则缓存大小会耗尽,接受窗口值将变为0,若能及时读取则相反,接受窗口值可以进一步增大,但最大不能超过接收缓存的范围。

除了以上,还有必须强调的三点:

  • 即便发送窗口是根据接受窗口来设置的,但是由于网络传输有时延所以会产生一定程度的滞后性,因此发送方还可能根据当前网络状态的拥塞情况来适当主动减小发送窗口的值。
  • TCP标准并未规定对未按序到达的数据应当如何处理,通常是将未按序到达的数据暂时存放在接收窗口中,等到收到缺失的数据后再向上交付给应用程序。
  • 为了降低传输开销,TCP要求接收方有累计确认的功能,即不能每收到一个报文段就确认一次。接收方可以选择在一个适当的时候发送确认报文段(例如在自己有数据需要发送时捎带上确认信息),但是有两点需要注意:确认延迟时间不能超过0.5秒且若收到一连串具有最大长度的报文段,必须每隔一个报文段就确认一次;二是捎带的情况并不经常发生。
超时重传时间选择

由于互联网环境的复杂性与实时性,TCP重传的时间选择一直是最复杂的问题之一。(以下直接复制)

TCP采用了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间RTT。TCP保留了RTT的一个加权平均往返时间RTTs(这又称为平滑的往返时间,S表示Smoothed。因为进行的是加权平均,因此得出的结果更加平滑)。每当第一次测量到RTT样本时,RTTs值就取为所测量到的RTT样本值。但以后每测量到一个新的RTT样本,就按下式重新计算一次RTTs:

''

在上式中,0≤a<1。若a很接近于零,表示新的RTTs值和旧的RTTs值相比变化不大,而对新的RTT样本影响不大(RTT值更新较慢)。若选择a接近于1,则表示新的RTTs值受新的RTT样本的影响较大(RTT值更新较快)。已成为建议标准的RFC6298推荐的a值为1/8,即0.125。用这种方法得出的加权平均往返时间RTTs就比测量出的RTT值更加平滑。 显然,超时计时器设置的超时重传时间RTO(RetransmissionTime-Out)应略大于上面得出的加权平均往返时间RTTs。RFC6298建议使用下式计算RTO:

''

而RTTp是RTT的偏差的加权平均值,它与RTTs和新的RTT样本之差有关。RFC6298建议这样计算RTTD。当第一次测量时,RTTn值取为测量到的RTT样本值的一半。在以后的测量中,则使用下式计算加权平均的RTTD:

''

这里β是个小于1的系数,它的推荐值是1/4,即0.25。

选择确认SACK

流量控制实现

流量控制发生在收发两方速率不匹配时,目的是让发送方降低速率至接收方来得及接收。

滑动窗口实现流量控制

利用滑动窗口机制可以实现对发送方的流量控制(在应答报文中变动接收窗口大小),当发送方收到窗口值为0的应答报文段时,将不再发送数据,知道再次收到带有新窗口值的应答报文。那么这时候就会出现一个问题,若在窗口值为0的报文之后,接收方接受缓存产生了空余并再次发送新的窗口值的报文段,但是这个报文段在传送过程中丢失了,那么发送方将会一直等待接收方的新应答报文而接收方也会一直等待发送方的数据,此时形成死锁。为了解决这个问题,在TCP连接的一方收到0窗口值的应答报文段时,启动一个持续计时器,若持续计时器设置的时间到了,则发送一个0窗口探测报文段(仅携带1字节的数据),对方就在确认这个报文段的同时发送当前的窗口值,若仍然为0,则重复以上步骤,若不为0则开始重新传送数据。

TCP传输效率

TCP发送报文段的策略:

  • 维持一个变量,等于最大报文段长度,只要发送缓存中的字节数达到这个数值则组装成报文段发送
  • 发送方的应用进程明确要求立即发送,即PSH位为1的推送操作
  • 发送方计时器期限到了则把当前发送缓存中的缓存数据(不超过MSS)组装成报文段发送
  • Nagle算法:若发送应用进程把要发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据到达较快而网络速率较慢时,用这样的方法可明显地减少所用的网络带宽。Nagle算法还规定,当到达的数据已达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。这样做,就可以有效地提高网络的吞吐量。

糊涂窗口综合征:接收方接受缓存已满,而应用程序一次仅从缓存中读取一个字节,而读取完一个字节又向发送方发送窗口值为1的应答报文,如此周而复始,极大地降低了网络效率。

要解决这个问题,可以让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段,或者等到接收缓存已有一半空闲的空间。只要出现这两种情况之一,接收方就发出确认报文,并向发送方通知当前的窗口大小。此外,发送方也不要发送太小的报文段,而是把数据积累成足够大的报文段,或达到接收方缓存的空间的一半大小。

拥塞控制实现

计算机网络的构成是复杂且多样的,当对网络中某一资源的需求超过了该资源所能提供的可用部分,则会产生拥塞(congestion)

''

网络拥塞是一个非常复杂的问题,并不能单一的增加某种资源来缓解,更多的可能是转移了瓶颈,例如提升节点的缓存容量,而输出链路的带宽与中间处理机的速度没有得到对应的提升,将导致缓存队列增大,则尾部大量分组失效而进行超时重传,加剧网络拥塞。如此,问题的实质往往是各个部分的不匹配,只有均衡各个部分,问题才能解决。

拥塞控制与流量控制的区别
流量控制 拥塞控制
点对点(接收方与发送方) 全局性(整个网络)
限制发送方发送速率 防止过多的数据被注入到网络

''

拥塞控制是一个动态的问题,从大的方面看可以分为开环控制与闭环控制(差别有点类似悲观乐观策略)

  • 开环控制

    设计网络时力求将所有会发生拥塞的因素考虑到,一旦系统运行起来便不再做修改

  • 闭环控制

    1)检测网络系统一边检测拥塞在何时何地发生

    2)把拥塞发生的信息传送到可以采取行动的地方

    3)调整网络的运行以解决问题

网络拥塞指标

缺少缓存空间而被丢弃分组作占的百分比、平均队列长度、超时重传的分组数、平均分组时延、分组时延的标准差等等。

一般在监测到拥塞发生时,要将拥塞发生的信息传送到产生分组的源站。当然,通知拥塞发生的分组同样会使网络更加拥塞。另一种方法是在路由器转发的分组中保留一个比特或字段,用该比特或字段的值表示网络没有拥塞或产生了拥塞。也可以由一些主机或路由器周期性地发出探测分组,以询问拥塞是否发生。此外,过于频繁地采取行动以缓和网络的拥塞,会使系统产生不稳定的振荡。但过于迟缓地采取行动又不具有任何实用价值。因此,要采用某种折中的方法。但选择正确的时间常数是相当困难的。

TCP拥塞控制方法

TCP进行拥塞控制的方法有四种(都是基于窗口的拥塞控制,发送方维护一个拥塞窗口的状态变量,发送方让自己的发送窗口大小等于拥塞窗口),即慢开始(slow-start)、拥塞避免(congestion avoidance)、快重传(fast retransmit)、快恢复(fast recovery)

慢开始

探测性,按照指数趋势由小到大逐渐增大拥塞窗口,具体规定如下

''

慢开始规定再每收到一个对新报文段的确认后,可以把拥塞窗口增加最多一个SMSS的数值,更具体的,是取SMMS与原先未被确认的、但现在刚被收到的确认报文段所确认的字节数中较小的那个。

若开始时SMSS为x字节,cwnd(拥塞窗口)为2x字节,则每次能发送两个报文段,收到两次确认后,cwnd增加到2x+2x字节,则此时可以同时发送的报文段看做4个,再次收到确认后,cwnd将增加到4x+4x字节。以此类推,在顺利情况下,没经过一次传输轮次,cwnd就翻倍。

为了防止慢开始算法将拥塞窗口增长过大引起的拥塞,还需要设置一个慢开始门限ssthresh状态变量,规则如下:

当cwnd<ssthresh时,使用上述的慢开始算法。 ​ 当cwnd>ssthresh时,停止使用慢开始算法而改用拥塞避免算法。 ​ 当cwnd=ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法。

拥塞避免

拥塞避免算法思想是让拥塞窗口进行线性缓慢增大,即“加法增大”,具体切换过程见下图(横坐标为传输轮次),当超时被判定为拥塞时,ssthresh=cwnd/2。。

''

快重传

另一个需要注意的点为点4的3-ACK,当拥塞窗口cwnd=16时(图中的点④),出现了一个新的情况,就是发送方一连收到3个对同一个报文段的重复确认(图中记为3-ACK)。

关于这个问题要解释如下。有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收 不到确认,就会产生超时,就会误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口cwnd又设置为1,因而降低了传输效率。采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失。快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认。如图5-26所示,接收方收到了M1和M2后都分别及时发出了确认。现假定接收方没有收到M3但却收到了M4。本来接收方可以什么都不做。但按照快重传算法,接收方必须立即发送对M2的重复确认,以便让发送方及早知道接收方没有收到报文段M3。发送方接着发送M5和M6。接收方收到后也仍要再次分别发出对M2的重复确认。这样,发送方共收到了接收方的4个对M2的确认,其中后3个都是重复确认。快重传算法规定,发送方只要一连收到3个重复确认,就知道接收方确实没有收到报文段M3,因而应当立即进行重传(即“快重传”),这样就不会出现超时,发送方也不就会误认为出现了网络拥塞。使用快重传可以使整个网络的吞吐量提高约20%

''

快恢复

因此,在图5-25中的点④,发送方知道现在只是丢失了个别的报文段。于是不启动慢开始,而是执行快恢复算法。这时,发送方调整门限值ssthresh=cwnd/2=8,同时设置拥塞窗口cwnd=ssthresh=8(见图5-25中的点6),并开始执行拥塞避免算法。

拥塞控制流程图

发送窗口上限在接受窗口和拥塞窗口中取较小者。

''

网络层对于拥塞的控制策略——主动队列管理AQM

为了防止路由器执行尾部丢弃而导致的一连串TCP连接(丢弃的分组中可能包含多个不同TCP连接的分组)重传分组的全局同步(这些TCP连接将同时进入慢同步状态),1998年提出了主动队列管理,即不再到队列满时才丢弃分组转为主动检测队列长度在某个值时进行丢弃。

TCP连接管理

连接建立

1)使每一方都能感知对方的存在

2)要允许双方协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等)。

3)能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。

''

为什么A最后还要发送一次确认呢?这主要是为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。所谓“已失效的连接请求报文段”是这样产生的。考虑一种正常情况,A发出连接请求,但因连接请求报文丢失而未收到确认。于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接。A共发送了两个连接请求报文段,其中第一个丢失,第二个到达了B,没有“已失效的连接请求报文段”。

现假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用报文握手,那么只要B发出确认,新的连接就建立了。

由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。

连接释放

''

为什么A在TIME-WAIT状态必须等待2MSL的时间呢?这有两个理由。 第一,为了保证A发送的最后一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着A重传一次确认,重新启动2MSL计时器。最后,A和B都正常进入到CLOSED状态。如果A在TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后立即释放连接,那么就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常步骤进入CLOSED状态。 第二,防止上一节提到的“已失效的连接请求报文段”出现在本连接中。A在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。B只要收到了A发出的确认,就进入CLOSED状态。同样,B在撤销相应的传输控制块TCB后,就结束了这次的TCP连接。我们注意到,B结束TCP连接的时间要比A早一些。

TCP的有限状态机

''


面向ACG编程