朝小闇的博客

海上月是天上月,眼前人是心上人

计算机网络自顶向下方法学习笔记(三)运输层

1.运输层概述

运输层服务过程:在发送端(端系统),运输层将从发送端应用程序进程接收到的报文转换为运输层分组(报文段),进而传递给网络层,网络层将其封装成网络层分组(数据报)并向目的地发送;在接收端(端系统),网络层从数据报中提取运输层报文段,并根据该报文段(应用程序决定使用哪一个运输层协议)上交给运输层。

1.1 运输层和网络层的关系

网络层提供了主机之间的逻辑通信,而运输层为运行在不同主机上的进程之间提供逻辑通信

1.2 因特网运输层概述

因特网提供两种运输层协议:

  • UDP:用户数据报协议,为应用程序提供不可靠、无连接的服务;
  • TCP:传输控制协议,为应用程序提供可靠的、面向连接的服务,除此之外提供附加服务:
    • 可靠数据传输服务,通过使用流量控制、序号、确认和定时器,TCP确保正确地、按序地将数据从发送进程交付给接收进程;
    • 拥塞控制服务,通过调节TCP连接的发送端发送进入网络的流量速率来防止任何一条TCP连接占用过多带宽;
  • UDP和TCP最基本的功能是将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务,即应用层只考虑进程交互,而不必关心这两个进程的具体位置;
    • 该基本服务被称为运输层的多路复用多路分解

2.多路复用和多路分解

  • 多路分解:在一个接收端将运输层报文段中的数据交付到正确的套接字的过程就是一次分解,每一个进程拥有一个或多个套接字,使用一个标识符特定申明其为TCP或UDP套接字,套接字收到报文段传输给自己的数据之后再传输给它所属的进程;

  • 多路复用:在源主机上,从不同的套接字中收集数据块,并为每个数据块封装首部信息从而生成报文段,然后将报文段传递到网络层的整个过程;

  • 报文段通过端口号字段标识需要交付的套接字:

    • 该特殊字段分为源端口号和目的端口号字段,每一个端口号是一个16比特的数,大小在0~65535之间:
      • 0~1023是周知端口号,这是受限制的,计算机主机保留给HTTP(80端口号)、FTP(21端口号)之类的周知应用层协议来使用的;
      • 我们开发一个新的应用程序时,必须为其分配一个端口号,而主机上的每个套接字分配一个端口号;
      • 运输层通过检查报文段中的目的端口号,将其定向到相应的套接字,然后报文段中的数据通过套接字进入其所连接的进程;

    image-20201101143437667

2.1 无连接的多路复用及多路分解(UDP)

以整个过程为例解释说明:

  1. 主机运行的程序创建一个UDP套接字,创建时运输层会从1024~65535范围内自动分配一个当前未被使用的端口号;
  2. 可以关联特定端口号,如果应用程序开发者所编写的代码实现的是一个周知协议的服务器端,则分配一个相应的周知端口号,通常,应用程序的客户端让运输层自动分配端口号,而服务器端则分配一个固定的端口号;
  3. 假定主机A上正在运行的进程具有UDP端口12345,需要发送数据给主机B上另一个具有UDP端口23456的进程;
  4. 主机A运输层创建一个运输层报文段,包括数据、源端口号(12345)、目的端口号(23456)和其它值;
  5. 运输层将得到的报文段传递到网络层;
  6. 网络层将该报文段封装在一个IP数据报中,并交付给接收主机B;
  7. 该报文段到达接收主机B,接收主机运输层检查报文段中的目的端口号(23456)并将该报文段交付给端口号23456所标识的套接字,注意:一个UDP套接字由二元组(目的IP地址,目的端口号)唯一标识,因此,如果两个UDP报文段有不同的源IP地址或源端口号,但具有相同的目的IP地址和目的端口号,那么这两个报文段将通过相同的目的套接字被定向到相同的目的进程;
  8. 当B需要回发一个报文段给A时,就取用源报文段的源端口号作为新报文段的目的端口号;

2.2 面向连接的多路复用及多路分解(TCP)

TCP套接字由四元组(源端口号、源IP地址、目的端口号、目的IP地址)唯一标识,当一个源主机向目的主机发送一个连接报文段请求TCP连接时,目的主机会根据该报文段中源端口号、源IP地址、目的端口号、目的IP地址创建一个TCP连接套接字,并在其四元组中通过这四个值来标识,之后到达的数据报文段只有四元组全部匹配才会被定向到该报文段,即一个套接字只与一个特定的进程联系

2.3 Web服务器和TCP

一台Web服务器为每条连接生成一个新进程,每个进程都拥有自己的连接套接字,通过这些套接字可以接收到HTTP请求和发送HTTP响应

连接套接字和进程之间并非总是一一对应,现在的高性能Web服务器只使用一个进程,但为每个新客户创建一个具有新连接套接字的新线程(线程可看做轻量级的子进程),对于这样一台服务器而言,套接字和进程就不是一一对应的。

  • 如果客户和服务器使用持续HTTP,则在整条连接持续期间,客户与服务器之间经由一个服务器套接字交换HTTP报文;
  • 如果使用非持续HTTP,则对每一对请求/响应都创建一个新的TCP连接并在随后关闭,这种套接字的频繁创建和关闭会严重影响一个繁忙的Web服务器的性能;

3.无连接运输:UDP

UDP除了实现复用和分解功能及少量的差错检测外,几乎没有对IP增加别的东西,应用程序几乎时直接和IP协议打交道

UDP优点:

  • 关于发送什么数据以及何时发送的应用层控制更为精细:
    • 只要应用进程将数据传输给UDP,UDP就会将其打包进入UDP报文段并立即发送给网络层;
    • 而TCP拥塞机制不管可靠交付的时间,在拥塞时会自动延迟报文段的传送;
  • 无需建立连接:
    • UDP不会引入建立连接的时延;
    • 而TCP会在数据传输之前进行三次握手协议;
  • 无连接状态:
    • TCP需要在端系统中维护一系列连接状态;
    • UDP不维护连接状态也不跟踪参数,当应用程序运行在UDP上时服务器能支持更多的活跃客户;
  • 分组首部开销小:
    • 每个TCP报文段有20字节的首部开销,而UDP只有8字节的首部开销;

如图所示:

  • 电子邮件、远程终端访问、Web及文件传输都运行在TCP协议上,因为它们都需要TCP的可靠传输服务;
  • UDP用于:
    • 承载网络管理数据,网络管理应用程序通常必须在网络重压状态时运行,而这个时候可靠的拥塞控制的数据传输难以实现;
    • 多媒体应用程序,但在UDP上运行多媒体应用具有争议,因为没有拥塞控制,那么当很多人都启用流式高比特视频而不使用任何拥塞控制则会使得路由器中大量分组溢出,以至于非常少的UDP分组能够到达目的主机;

image-20201101160411184

3.1 UDP报文段结构

如图所示:

  • 长度字段:指示了在UDP报文段中的字节数(首部加数据);
  • 检验和:接收方使用检验和来检查该报文段是否出现了差错;
  • 首部四个字段,每个字段由两个字节组成;

image-20201101161512050

3.2 UDP检验和

UDP检验和提供了差错检测功能,用于确定当UDP报文段从源到达目的地移动时,其中比特是否发生变化

  • 检验和计算过程:发送方的UDP对报文段中所有16比特的和进行反码运算,求和时遇到的任何溢出都回卷,得到的结果被放在UDP报文段中的检验和字段;

举例:

image-20201101162908522

  • 注意最后一次求和时高位比特有溢出现象,需要回卷到低位比特;
  • 反码运算得1011010100111101,这个值就是检验和的值;
  • 在接收方,全部的四个16比特的和比特必须全为1,如果比特之一为0,则分组中出现了差错;
  • 虽然UDP提供差错检测,但它对差错恢复无能为力,一般措施是丢弃或者交给应用程序并给出警告;
  • 端到端原则:某种功能必须基于端到端实现,如此时的差错检验;

4.可靠数据传输原理

由于可靠数据传输的实现问题不仅出现在运输层,也会出现在链路层和应用层,其下层协议未必可靠,因此需要实现可靠传输,这里假定底层信道只能丢失分组而不会对分组进行重排(即顺序到达)。且本节仅考虑单向数据传输,即数据传输是从发送端到接收端的,但对于协议而言需要在发送端和接收端两个方向上传输分组。

image-20201102183101917
  • 其中udt表示不可靠数据传输,rdt表示可靠数据传输;

4.1 构造可靠数据传输协议

4.11 经完全可靠信道的可靠数据传输:rdt1.0

假定底层信道完全可靠情形

看到如图所示rdt1.0发送方和接收方有限状态机(FSM):

  • 圆框中为状态;
  • 箭头指示协议从一个状态变迁到另一个状态;
  • 横线上方指示引起变迁的事件;
  • 横线下方指示事件发生时采取的动作,如果没有事件或动作则分别使用符号Λ表示;
  • 虚线表示FSM的初始状态;
  • rdt1.0过程如下:
    • 在发送端,从初始状态【等待来自上层的调用】出发,通过rdt_send(data)事件接收来自较高层的数据,实质上rdt_send(data)事件是由较高层应用的过程调用产生的;
    • make_pkt(data)动作产生一个分组packet,并通过udt_send(packet)将分组发送到信道中;
    • 经过事件和动作后状态变迁,重回【等待来自上层的调用】状态;
    • 在接收端,从初始状态【等待来自下层的调用】出发,rdt通过rdt_rcv(packet)事件从较低层信道接收一个分组,该事件由较低层协议的过程调用产生;
    • 经过extract(packet,data)动作从分组中取出数据,并通过deliver_data(data)将数据上传给较高层;
    • 经过事件和动作后状态变迁,重回【等待来自下层的调用】状态;

image-20201102183921310

4.12 经具有比特差错信道的可靠数据运输:rdt2.0

假定底层信道可能出现比特受损(不丢失分组)情形

自动重传请求协议(ARQ):基于报文肯定确认或否定确认反馈并进行重传的可靠数据传输协议。

ARQ协议需要另外三种协议功能来处理存在比特差错的情况:

  • 差错检测;
  • 接收方反馈;
  • 重传;

rdt2.0 FSM图示:

  • 使用检验和进行检验;
  • 使用了停等协议,没有收到ACK肯定确认之前一直停等或重传;
  • 该版本协议具有致命缺陷:ACK/NAK可能受损!
image-20201102190427731

rdt2.1 FSM图示:

  • 使用一个比特位字段来存放序号,分组确认完整发送后变换序号,该序号是用来解决发送方未正确接收到肯定确认时进行重传引起的冗余分组;
  • 协议状态反映出目前正发送的分组或希望接收的分组序号;
  • 两种接收状态:
    • 接收到和上次不同序号的分组时,接收方发送肯定确认ACK;
    • 接收到受损的分组,接收方发送一个否定确认NAK;
image-20201102191720662 image-20201102191748791

rdt2.2 FSM图示:

  • 在2.1的基础上实现无NAK的可靠数据传输协议:
    • 接收到受损分组,接收方对上次正确接收的分组发送一个ACK,即实现NAK功能;
    • 实现方法是在返回确认时增加参数分组序号,返回ACK 0或ACK 1;
image-20201102194613476 image-20201102194631080

4.13 经具有比特差错的丢包信道的可靠数据传输:rdt3.0

假定底层信道传输时比特可能受损也可能丢包情形

两个问题:怎样检测丢包以及发生丢包后应该采取什么措施。

倒计数定时器:

  • 每次发送一个分组(包括第一次分组和重传分组)时,就启动一个定时器;
  • 响应定时器中断并采取动作;
  • 终止定时器;
  • 发送方通过定时器进行等待,一旦定时器中断,就采用重传操作,有效解决两个问题;
image-20201103074201864

由于分组序号在0和1之间交替,rdt3.0又被称作比特交替协议。

image-20201103074248098

4.2 流水线可靠数据传输协议

rdt3.0协议的核心问题在于它是一个停等协议,虽然功能正确,但并不能运用到实际问题中,否则发送方信道的利用率会极低。因此提出一个简单的解决方案:不以停等方式运行,允许发送方发送多个分组而无需等待确认,这种技术被称作流水线

流水线操作:

  • 增加序号范围,每个输送中的分组必须有唯一的序号,也有多个在输送中的未确认报文;
  • 协议的发送方和接收方缓存多个分组,最低限度要缓存已发送但未确认的分组,或许还要缓存一些已正确接收的分组;
  • 所需序号范围和对缓存的要求取决于数据传输协议如何处理丢失、损坏及延时过大的分组。
  • 解决流水线差错恢复两种方式:
    • 回退N步;
    • 选择重传;

image-20201103075310783

4.3 回退N步(GBN)

允许发送方发送多个分组而不需要等待确认,其中未确认的分组数不能超过某个最大允许数N,N常被称作窗口长度,GBN协议也被称为滑动窗口协议,解决了停等协议中利用率低的问题

序号:

  • 基序号(base):最早未确认分组的序号;
  • 下一个序号(nextseqnum):最小的未使用序号;
  • 序号范围:
    • [0,base-1]段对应已发送并已被确认的分组;
    • [base,nextseqnum-1]段对应已发送尚未确认的分组;
    • [nextseqnum,base+N-1]段对应将要被发送的分组序号;
    • [base+N+1~]段需要到当前未被确认的分组得到确认为止才能使用;
    • 一个序号承载在分组首部的一个固定长度字段中,序号空间看成长度为2k的闭环,序号2k-1紧接着序号0;

image-20201103084832055

提出问题:为什么不能将有限N转换为无限呢?

  • 流量控制是原因之一;

扩展FSM:

  • 基于ACK、无NAK的GBN协议的发送方和接收方的FSM描述,增加了变量base和nextseqnum;
  • GBN发送方必须响应三中类型的事件:
    • 上层调用:
      • 上层直接调用rdt_send(),发送方检查自身发送窗口是否已满:
        • 未满则产生分组并将其发送,并相应地更新变量;
        • 已满则将数据返回给上层,隐式地指示上层该窗口已满;
      • 使用同步机制(信号量或标识),允许上层在仅当窗口不满时才可调用rdt_send()
      • 缓存而并不立刻发送该分组;
    • 收到一个ACK:
      • 正常累积确认,对序号为n的分组发送ACK,表明接收方已正确接收到序号为n及以前的未确认的所有分组;
      • 对于收到失序(即丢包)、受损的分组,接收方发送上一次序号ACK,表示请求重传上一次序号之后的所有未确认分组;
    • 超时事件:
      • 对于最早未确认分组(base序号)使用一个定时器,如果丢失或者时延过长则重传所有未确认分组,并重新启动定时器;
  • GBN接收方:
    • 接收到失序或受损分组时丢弃该分组并发送上一次序号ACK;
    • 直接对已正确接收的分组发送ACK;
    • 丢弃所有失序分组:
      • 优点:接收缓存简单,接收方不需要缓存任何失序分组,只需要维护下一个按序接收的分组序号并将其值存于expectedseqnum变量中;
      • 缺点:随后对该分组的重传也许会丢失会出错,即可能需要更多次的重传;

image-20201103090002171

实例:窗口长度N为4的GBN协议运行情况:

  1. 由于窗口长度限制,发送方只能先发送0~3序号的分组,然后进行等待;
  2. 接收到连续的ACK 0和ACK 1 之后才能继续发送序号为4和5的分组;
  3. 接收方分组2丢失,因此分组序号3、4、5被发现是失序分组并被丢弃,接收方重新发送ACK 1;
  4. 发送方重传分组2、3、4、5;

image-20201103092101884

4.4 选择重传(SR)

当窗口长度和带宽延时都很大时,流水线中分组过多,GBN对于单个分组的差错而引起的重传大量分组则显得极为过分,选择重传基于解决这部分问题而设置。SR通过让发送方仅重传那些可能出错的分组而避免不必要的重传,而这种个别的、按需的重传要求接收方逐个地确认正确接收的分组。

序号空间:

  • 使用窗口长度N限制流水线中未完成未被确认的分组数,但发送方能够收到窗口中某些分组的ACK;

image-20201103093226708

SR发送方采取的动作:

  • 从上层收到数据:从上层接收到数据后,SR发送方检查下一个可用分组的序号,该序号位于发送方的窗口内部时将数据打包发送,否则和GBN相同处理;
  • 超时:使用定时器防止分组丢失,但是是每个分组都需要拥有自己的逻辑定时器;
  • 收到ACK:收到ACK时,若分组序号在窗口内,则将该序号分组标记为已接收;若序号等于send_base,则窗口基序号向前移动到最小序号的未接收分组处;如果窗口移动了而此时又有序号位于窗口内的未发送分组,则发送这些新分组;

SR接收方采取的动作:SR接收方将确认一个正确接收的分组而不管其是否失序,如果失序则缓存直到所有丢失分组都被收到时统一交付给上层

  • 序号在[rcv_base,rcv_base+N-1]范围内的分组被正确接收:收到的分组落在接收方的窗口内,接收方发送回一个该序号的ACK
    • 如果该分组以前没有收到过,则缓存该分组;
    • 如果该分组的序号等于接收窗口的基序号(rcv_base),则该分组以及以前缓存的序号连续的(起始于rcv_base的)分组交付给上层,然后接收窗口向前移动分组的编号;
  • 序号在[rcv_base-N,rcv_base-1]范围内的分组被正确接收:在此情况下(即原ACK可能在返回时丢失),必须产生一个ACK,即使该分组是接收方已经确认过的分组;
  • 其它情况:忽略该分组;

N=4的实例SR分组丢失操作图示:

image-20201103104615086

在序列号范围有限(或者说窗口长度接近序列号范围长度时),发送方和接收方窗口间缺乏同步会产生严重的后果!

举例:N=3,分组序号为0~3:

  • 假定发送了分组0~2,并在接收方被正确接收且确认了,此时,接收方期待分组序列落在4、5、6分组上,其对应序列分别为3、0、1:
    • ①如图a:对前三个分组的ACK丢失,因此发送方重传这些分组,而接收方下一步接收的序号为0的分组所对应的就是1号分组而非其期待接收的4号分组;
    • ②如图b:前三个分组的ACK都正确交付,因此发送方向前移动窗口并发送4、5、6个分组,其序号对应为3、0、1,若此时序号为3的分组丢失,但序号为0的分组到达,则对应SR接收方动作中一二种情形等同,没有办法区分是第一个分组的重传还是第五个分组的初次传输;
  • 窗口长度必须小于或等于序列空间大小的一半;

image-20201103104823611

可靠数据传输原理总结:

image-20201103110050615

5 面向连接的运输:TCP

5.1 TCP连接

TCP连接是一条逻辑连接,只在源和目的端系统中维持连接状态,而不会在中间网络元素中存留连接状态

全双工服务:如果一台主机上的进程A与另一台主机上的进程B存在一条TCP连接,那么应用层数据就可在A、B进程之间双向流通。

点对点:每一次发送过程,都是在单个发送方和单个接收方之间的连接。

TCP连接建立的过程:

  1. 客户应用进程通知客户运输层,它想与服务器上的一个进程建立一条连接:
    1. 三次握手协议:客户-服务器-客户,只有第三次客户发送的响应报文才可承载有效载荷(应用层数据);
  2. 建立连接后,客户进程通过套接字传递数据流,TCP将这些数据流引导到该连接发送缓存里,发送缓存是发送三次握手协议期间设置的缓存之一;
  3. TCP在它方便的时候会从该连接的发送缓存中取出一块数据并配上TCP首部形成TCP报文段,再传递给网络层,也就是发送数据;
  4. 接收端接收到报文段后,该报文段的数据就被放入该TCP连接的接收缓存中;
  5. 应用程序通过套接字从接收缓存中读取数据;
  • 注意:
    • TCP可从发送缓存里取出并放入报文段中的数据数量受限于最大报文段长度(MSS)
    • MSS通常根据最初确定的由本地发送主机发送的最大链路层帧长度(最大传输单元(MTU))来设置,且MSS是指在报文段里应用层数据的最大长度,而不是包括首部的TCP报文段的最大长度;

image-20201103123411798

由此可知,TCP连接的组成包括:

  • 客户与服务器主机上的缓存、变量和与进程连接的套接字;

5.2 TCP报文段结构

如图所示:TCP报文段由首部和一个数据字段组成,数据字段包含一块应用数据,而MSS限制了报文段数据字段的最大长度(交互式应用通常传送长度小于MSS的数据块),首部包含:

  • 源端口号:多路复用来自上层的数据;
  • 目的端口号:多路分解将数据传输到上层;
  • 检验和字段;
  • 序号字段;
  • 确认号字段;
  • 接收窗口字段:16比特,用于指示接收方愿意接收的字节数量,控制流量;
  • 首部长度字段:4比特,由于选项字段可变,TCP首部的长度也是可变的通常选项字段为空时首部长度为20字节;
  • 选项字段:可选与变长;
  • 标志字段:6比特,ACK比特用于指示确认字段中的值是有效的,即该报文段包括一个对已被成功接收报文段的确认,其它比特位后续讲到;
  • 紧急数据指针字段;

image-20201103210636179

5.21 序号和确认号

TCP把数据看成一个无结构的、有序的字节流,序号建立在传送的数据的字节流上,每一个数据字节都拥有自己的序号,一个报文段的序号是该报文首字节的字节流编号

image-20201103212015084

确认号:主机A向主机B发送报文段时,确认号是主机A期待主机B下一次能够向它发送字节流数据的序号,同时也是确认该流中到第一个丢失字节为止的序号。举例:主机A收到主机B给自己发送的序号为0-535和900-1000的两个报文段数据,那么当它向B发送数据时,确认号就是536,期望下一次主机B能够发送从536序号开始的字节流数据报文段。

在上述例子中产生问题:第三个报文段900-1000序号失序到达,这时接收方有两个选择:

  • 立即丢弃失序报文段,这可以简化设计;
  • 保留失序字节,并等待缺少的字节,实践中一般采用此方法;

且在一条TCP连接过程,连接双方可以随机选择初始序号,这样做可以减少可能出现的与旧报文序号相同的误会。

5.22 序号和确认号的一个学习案例

会话过程:

  1. 主机A(客户)数据初始序号为42,主机B(服务器)数据初始序号为79,TCP首先建立连接;
  2. 主机A向主机B发送第一个报文段,初始序号为42,确认号为79,数据内容为C
  3. 主机B向主机A回显它收到的数据C,其初始序号为79,确认号为43;
  4. 主机A向主机B发送一个响应报文,申明自己已经收到数据,虽然没有数据,但是依然使用了43的序号,这是因为序号字段必须填入某个序号;

其中,主机B向主机A发送的响应数据其实已经包含确认,这种确认号被装载在一个承载数据内容的报文段的确认被称为是捎带的。

image-20201103214621223

5.3 往返时间的估计与超时

超时/重传机制最明显的一个问题就是超时间隔长度的设置,这一节讨论这个问题

5.31 估计往返时间

报文段样本RTT(SampleRTT):从某报文段被发出(交给IP)到对该报文段的确认被收到之间的时间量,也就是估计往返时间

  • 由于TCP一般只在某一个时刻做一次SampleRTT测量,所以这个样本估计量是不够准确的,因此以平均RTT的方式解决准确性问题;
  • TCP维持一个SampleRTT均值(EstimatedRTT),一旦获得一个新的SampleRTT时,TCP按照以下公式更新EstimatedRTT值:
    • 其中α推荐值是0.125;
    • 这个加权平均对最近的样本赋予的权值要大于对旧样本赋予的权值,因为越近的样本越能反映当前网络的拥塞状况,这种平均又被称为指数加权移动平均

image-20201104102754129

RTT偏差(DevRTT):测量RTT的变化,估算SampleRTT一般会偏离EstimatedRTT的程度。

  • β的推荐值是0.25;

image-20201104103344966

5.32 设置和管理重传超时间隔

超时间隔设置为EstimatedRTT加上一定余量,且当SampleRTT值波动较大时,这个余量应该大些,当波动较小时,这个余量应该小些。

超时间隔(TimeoutInterval):初始值推荐为1秒

  • 出现超时后,TimeoutInterval值加倍;
  • 一旦收到报文段并更新EstimatedRTT,就使用公式TimeoutInterval=EstimatedRTT+4×DevRTT计算;

5.4 可靠数据传输

定时器,前面研究可靠数据传输时假定每一个已发送但未确认接收的报文段都拥有一个独立的定时器,但由于这样一来定时器的开销太大,实际开发中,TCP定时器管理过程对多个已发送但未接收的报文段仅使用单一的重传定时器。

TCP使用超时机制和冗余确认技术实现差错报文段的恢复

超时机制:

  • 发送方三个与重传有关的主要事件:
    • 从上层应用程序接收数据:将接收到的数据封装到报文段并发送给IP,当报文段被传给IP时,TCP就启动定时器,该定时器的超时间隔为TimeoutInterval
    • 定时器超时:TCP通过重传引起超时的报文段来作为响应,并且重启定时器,超时间隔加倍;
    • 收到ACK:发送方接收到来自接收方的确认报文段(ACK)时,TCP将ACK的y值与它的变量SendBase进行比较
      • SendBase是最早未被确认的字节的序号;
      • TCP采用累积确认,所以y确认了字节序号在y之前的所有字节都已经正确收到;
      • 如果y>SendBase,则该ACK是在确认一个或多个先前未被确认的报文段,发送方更新SendBase值;如果当前有未被确认的报文段,TCP重启定时器?

image-20201104222955779

5.41 简化模型特殊情形

讨论只采用超时机制的简化模型中三种特殊情形:

  1. 主机A向主机B发送的报文段正确到达,但是主机B响应的确认ACK丢失了,超时后,主机A会重传上一次报文段,而主机B接收到后会直接丢弃重传的报文段;
  2. 主机A连续发送两个报文段,然而在超时之前没有任何一个确认ACK到达,那么主机A重传第一个报文段,并且重启定时器,只要在这一次超时之前第二个报文段的确认ACK到达,主机A就不会再重传第二个报文段;
  3. 主机A连续发送两个报文段,第一个报文段的确认ACK丢失,但第二个报文段的确认ACK按时到达,主机A就会知道第二个确认ACK序号之前的字节都已到达,所以主机A并不会重传任何一个报文段;

image-20201104224510503

image-20201104224532551

5.42 超时间隔加倍

定时器时限过期后,每次TCP重传时都会将下一次的超时间隔设置为前一次的两倍,而不会使用从EstimatedRTT和DevRTT推算出的值,一直循环重传直到收到确认。在这个过程中,只要定时器的另外两个事件收到上层的数据和收到ACK中任何一个启动,TimeoutInterval都会根据最近的EstimatedRTT和DevRTT重新修改值

定时器时限过期一般都是由网络拥塞引起的。

5.43 快速重传

超时触发重传存在的问题之一就是超时周期可能相对较长,端到端时延增加

冗余ACK:发送方收到对某一报文段的确认后再次收到该报文段的ACK确认,可以通过冗余ACK检测丢包情况。

  • 当TCP接收方收到一个序号大于下一个所期望的、按序的报文段,它就检测出中间数据流出现间隔,即报文段丢失,它就会对已经接受到的最后一个按序字节数据进行重复确认,即产生冗余ACK;
  • 如果TCP发送方接收到对相同数据的三个冗余ACK,说明跟在这个已被确认过三次的报文段之后的报文段已经丢失,就执行快速重传操作,即在该报文段定时器过期之前重传该丢失的报文段;

TCP接收方的ACK生成策略:

image-20201104225512504

5.44 是回退N步还是选择重传

TCP是一个GBN协议还是SR协议?

情形一:TCP发送方仅需维持已发送过但未被确认的字节的最小序号(SendBase)和下一个将要发送的字节的序号(NextSeqNum);

情形二:TCP会将正确接受但失序到达的报文段缓存起来;

情形三:当发送方发送一组报文段1,2,···,N,并且所有的报文段都正确按序到达接收方,而分组n<N的确认报文丢失,其余N-1个确认ACK全部在超时之前到达

  • 对于GBN而言,它会重传所有序号为n以及之后的报文段;
  • 而TCP最多只会重传序号为n的一个报文段,甚至如果报文段n+1的确认报文在超时之前到达的话,TCP不会重传任何报文段;

TCP提供了一种方案:选择确认,允许TCP接收方有选择地确认失序报文段,而不是累积确认最后一个接收的有序报文段;实质上TCP提供的可靠数据传输更像是一种GBN和SR的混合协议。

5.5 流量控制

一条TCP连接的每一侧主机都拥有一块接收缓存和一块发送缓存(全双工),如果某应用程序读取数据相对缓慢,而发送方发送数据太多、太快,则有可能使得接收方的接收缓存溢出,流量控制主要针对这一情形

流量控制:是一个速度匹配服务,发送方的发送速率和接收方应用程序读取数据的速率要相匹配,以消除发送方使接收方接收缓存溢出的情形。

拥塞控制:TCP发送方因为IP网络的拥塞而被遏制发送。

两者得到的结果都是控制,但产生的原因不一样。

解决方案,以主机A向主机B发送大文件为例:

  • 接收窗口(rwnd):该接收方还剩下多少接收缓存空间,由接收方放入报文段接收窗口字段发送回发送方,发送方通过维持该值来了解接收缓存的剩余空间,并且控制速率;
  • LastByteRead:主机B上的应用程序从缓存读出的数据流的最后一个字节的序号;
  • LastByteRcvd:从网络中到达的并已被放入主机B接收缓存中的数据流的最后一个字节的序号;
  • 接收缓存(RcvBuffer);
  • 则应该满足:
    • rwnd = RcvBuffer - [LastByteRead - LastByteRcvd] >=0

image-20201104234730901

主机A轮流跟踪两个变量LastByteReadLastByteRcvd,注意LastByteRead - LastByteRcvd值为主机A发送到连接中但未被确认的数据量。

考虑一个问题:假设主机B的接收缓存已经存满,即rwnd=0,在将rwnd=0通告给主机A之后,主机B没有任何数据需要发送(而TCP仅当在它有数据需要发送或者有确认需要发送时才会发送报文段给主机A)。而主机B上的应用程序将接收缓存清空之后,接收缓存就已经没有数据了,而主机A依然会被阻塞而不能再发送数据给主机B。

解决方案:TCP规范要求当主机B的接收窗口为0时,主机A继续发送只有一个字节数据的报文段。这样主机B就会实时刷新rwnd值并传递给主机A,让主机A知道接收缓存的空间余量。

UDP和TCP不同,它并不提供流量控制服务,报文段由于缓存溢出就可能在接收方丢失。

5.6 TCP连接管理

观察如何建立和拆除一条TCP连接

建立过程:

  • 客户应用首先通知客户TCP,说明它想建立一个与服务器之间某个进程之间的连接;

  • TCP通过三次握手协议与服务器TCP建立一条TCP连接:

    • 客户端TCP首先向服务器端TCP发送一个特殊的TCP报文段,该报文段不包含数据,但在其首部的一个标志位SYN比特被置为1(该报文段又被称为SYN报文段)。此外,客户随机选择一个初始序号(client_isn),并将此序号放置于该SYN报文段的序号字段中。
    • TCP SYN报文段的IP数据报到达服务器主机,服务器会从该数据报提取SYN报文段并为TCP连接分配变量和缓存(易受到SYN洪泛攻击,有修改),同时向客户端TCP发送允许连接的SYNACK报文段,其不能包含数据但首部包含三个信息:
      • SYN比特置为1;
      • 确认号字段被置为client_isn+1
      • 服务器选择自己的初始序号(server_isn),并放置到报文段首部的序号字段;
    • 客户收到SYNACK报文段,给该TCP连接分配变量和缓存,并向服务器发送另一个报文段。该报文段可以承载数据,SYN比特被置为0,且确认号字段存放值server_isn+1

    在以后的每一个报文段中,SYN比特都被置为0。

image-20201105003237246

拆除过程:双方都能终止该连接,连接结束后,主机中资源将被释放,假设客户打算关闭连接(一般都是客户主动关闭)

  • 客户TCP发送一个特殊的报文段,标志位FIN比特置为1;
  • 服务器接收到客户报文段,回送一个确认报文段;
  • 服务器再发送自己的终止报文段,标志位FIN比特置为1;
  • 客户对服务器发送的终止报文段进行确认,之后两台主机上的资源都被释放;

image-20201105003343640

TCP状态:在一个TCP连接的生命周期内,运行在每台主机上的TCP协议在各种TCP状态中变迁。

客户TCP经历的典型TCP状态:

  • 客户TCP开始时处于CLOSED(关闭)状态;
  • 发送过SYN报文段后,客户TCP进入SYN_SENT状态;
  • 收到服务器报文段进行确认且SYN置为1,客户TCP进入ESTABLISHED(已建立)状态,在此状态下,TCP客户就能发送和接收包含有效载荷数据的TCP报文段了;
  • 假设客户应用程序决定关闭该连接,客户TCP发送一个带有FIN比特被置为1的特殊报文段,进入FIN_WAIT_1状态;
  • 客户收到来自服务器TCP的确认报文段,进入FIN_WAIT_2状态;
  • 客户收到来自服务器TCP带有FIN被置为1的特殊报文段,客户TCP对服务器的报文进行确认,并进入TIME_WAIT状态;
    • 假定客户发送的确认ACK丢失,TIME_WAIT状态使TCP客户重传最后的确认报文段;
    • TIME_WAIT状态消耗的时间与具体实现相关,典型的数值是30秒、1分钟、2分钟;
    • 经过这段时间等待后,连接正式关闭,客户端所有资源(包括端口号)将被释放;
image-20201105003901345

服务器TCP经历的典型TCP状态(假设客户开始终止连接):

没有描述某些不正常的情形(如当连接的双方同时发起或终止一条连接),具体如图:

image-20201105005036960

nmap端口扫描工具工作原理:

  • 假设探索特定的TCP端口6789,nmap将对那台主机的目的端口6789发送一个特殊的TCP SYN报文段,有三种可能输出:
    • 源主机从目标主机接收到一个TCP SYNACK报文段,这意味着在目标主机上一个应用程序使用TCP端口6789运行,nmap返回“打开”;
    • 源主机从目标主机接收到一个TCP RST报文段,这意味着该SYN报文段到达了目标主机,但目标主机没有运行一个使用TCP端口为6789的应用程序;
    • 源什么也没收到,这表明该SYN报文可能被中间的防火墙所阻挡,无法到达目标主机;

除此之外,扩展一下SYN洪泛攻击

由于服务器在响应客户TCP SYN报文段时会分配并初始化连接变量和缓存,如果某客户不发送ACK来完成该三次握手的第三步,最终(在一分多钟后)服务器将终止该半开连接并回收资源。

  • SYN洪泛攻击:攻击者发送大量的TCP SYN报文段,而不完成第三次握手协议,服务器不断为这些半开连接分配资源(但从未使用),导致服务器的连接资源被消耗殆尽;
  • SYN cookie:防御SYN洪泛攻击的一种手段,主要实现是服务器不会为SYN报文段的到来生成一个半开连接;

6.拥塞控制原理

分组重传是网络拥塞的象征,为了处理网络拥塞问题,需要一些机制以在面临网络拥塞时遏制发送方

6.1 拥塞原因与代价

6.11 情形一:两个发送方和一台具有无限缓存的路由器

两台主机(A、B)都有一条连接,且这两条连接共享源与目的地之间的单跳路由:

  • 假设主机A、B中的应用程序以**λin**字节每秒的平均速率将数据发送到连接中;
  • 运输层协议使用UDP,不执行差错恢复、流量控制和拥塞控制;
  • 忽略添加运输层和较低层首部的额外开销;
  • 假设路由器缓存无限大;
  • 过程分析:
    • 来自主机A、B的分组通过一台路由器,在一段容量为R的共享式链路上进行传输;
    • 当分组到达速率超过输出链路的容量时存储“入分组”;
image-20201105075324553

主机A的连接性能图示:

  • 左边的图形描绘了接收方每秒接收的字节数与该连接发送速率之间的函数关系:
    • 当发送速率在0-R/2之间时,接收方吞吐量等于发送方的发送速率;
    • 当发送速率大于R/2时,吞吐量依然只能达到R/2;
    • 这个吞吐量上限R/2是由两条连接之间共享链路的容量决定的,链路完全不能以超过R/2的稳定状态速率向接收方交付分组;
  • 右边图形描绘了发送速率与时延的函数关系:
    • 当发送方发送速率接近R/2左右时,其平均时延就会越来越大;
    • 当发送速率超过R/2时,路由器中的排队时延就会无限增大;
  • 拥塞网络代价一:当分组的到达速率接近链路的容量时,分组会经历巨大的排队时延;
image-20201105100811705

6.12 情形二:两个发送方和一台具有优先缓存的路由器

假定路由器缓存有限:

  • 分组到达一个已满的缓存时会被丢弃;
  • 假定连接为TCP连接,即连接可靠,分组丢失可以被重传;
  • 应用程序将初始数据以**λin**字节每秒发送到套接字中;
  • 运输层以λ’in**字节每秒向网络层发送报文段,该速率又被称为网络的供给载荷**;
image-20201105101709219

理想情况:不丢失分组

  • 主机A能够以某种方式确认路由器中的缓存是否空闲,再决定是否发送分组,这时不会产生丢包,λin=λ’in**,连接的吞吐量也等于λin**;
  • 平均主机发送速率不能超过R/2;

实际情况:进行重传

  • 供给载荷λ’in=R/2,此时数据被交付给接收方应用程序的速率为R/3,在所发送的0.5R单位数据中,平均而言,R/3字节每秒是初始数据,R/6字节每秒是重传数据
    • 拥塞网络代价二:发送方必须执行重传以补偿因为缓存溢出而丢失的分组;
  • 接收方收到报文段,而发送方可能没能及时接收到ACK报文段,这时发送方重传的报文段数据接收即丢弃
    • 拥塞网络代价三:发送方遇到大时延时所进行的不必要重传会引起路由器利用其链路带宽来转发不必要的分组;
    • 图c假定每个分组被路由器平均转发两次时,吞吐量与供给载荷的对比情况:
      • 当供给载荷接近R/2时,其吞吐量渐进R/4;
image-20201105102525147

6.13 情形三:四个发送方和具有有限缓存的多台路由器及多跳链路

四台主机发送分组,每台都通过交叠的两跳链路传输:

  • 假定使用TCP连接,采用超时/重传机制实现可靠传输服务;
  • 所有主机都有相同的**λin**值,所有路由器的链路容量都是R字节每秒;
  • 如图:
    • A-C与D-B连接共享路由器R1;
    • A-C与B-D连接共享路由器R2;
    • 对极小的**λin**而言:
      • λin**的增大会导致λout**的增大
      • 路由器缓存溢出很少;
    • **λin**(λ’in也增长)很大时:
      • 不管**λin**的值多大,到达路由器R2的A-C流量的到达速率最大是R,这也是R1到R2的链路容量;
      • λin**很大,λ’in**对于所有连接都很大,那么在R2上,B-D流量的到达速率可能远大于A-C流量的到达速率
        • A-C和B-D流量必须参与R2上的链路缓存的竞争;
        • 来自B-D连接的供给载荷越大,A-C连接通过的流量就会越小;
        • 极限情况下,R2的空闲缓存会被B-D连接的分组占满,A-C端到端吞吐量将趋向于0;
        • 此时,第一跳路由器所使用的将分组转发到第二跳路由器的传输容量用来传输不同的分组可能更有效益;
        • 拥塞网络代价四:当一个分组沿一条路径被丢弃时,每个路由器用于转发该分组到丢弃该分组而使用的传输容量全部浪费了;
image-20201105103808051

6.2 拥塞控制方法

两种主要的拥塞控制方法:

  • 端到端拥塞控制:网络层不为运输层拥塞控制提供显性支持;
  • 网络辅助的拥塞控制:路由器向发送方提供关于网络中拥塞状态的显示反馈信息,拥塞信息从网络反馈到发送方有两种方式:
    • 经由接收方的网络反馈;
    • 直接网络反馈:
      • 由网络路由器发送,采用阻塞分组的形式;
      • 更通用的是,路由器标记或更新从发送方流向接收方的分组中某个字段来指示拥塞的产生,至少要经过一个完整的往返时间;
image-20201105105809022

7.TCP拥塞控制

由于IP层不向端系统提供显式的网络拥塞反馈,TCP必须使用端到端拥塞控制。TCP采用的方法是让每一个发送方根据所感应到的网络拥塞程度来限制其发送速率,但这个方法有三个极为重要的问题:

问题一:TCP发送方如何限制它向接收方发送的速率?

  • 运行在发送端的TCP拥塞控制机制存在一个额外变量拥塞窗口(cwnd),它对一个TCP能向路径上发送的速率进行了限制
    • LastByteSent - LastByteAcked <= min{cwnd,rwnd}
    • 假设TCP接收缓存无限大,即可以忽略接收窗口(rwnd)的限制,并且假设发送方一直有数据需要发送,由于发送未确认报文段的存在,所以会限制发送方的发送速率;
    • 考虑一个丢包和发送时延均可忽略不计的连接,在每个往返时间(RTT)的起始点,上述限制条件允许发送方向该连接发送cwnd个字节的数据,在该RTT结束时发送方接收到来自接收方的确认报文段,因此,该发送方的发送速率大概为 cwnd/RTT 字节每秒;
    • 通过调节cwnd的值,发送方能够调节它相连接发送数据的速率;

问题二:TCP如何感知从它到目的地之间的路径上存在拥塞?

  • 丢包事件(拥塞检测)定义为:出现超时,或者收到来自接收方的三个冗余ACK;

  • 考虑网络没有拥塞的情形:

    • TCP的发送方接收到对于以前未确认的报文段的确认,并使用该确认来增加窗口的长度(及其传输速率);
    • 如果确认以相当慢的速率到达,则该拥塞窗口以相当慢的速率增加,如果确认以高速率到达,则该拥塞窗口的速率快速增加;
    • 由于TCP使用确认来触发增大它的拥塞窗口长度,所以TCP又被称为自计时的;
  • TCP又应该怎样确定它应该以怎样的速率发送数据呢?

    • 一个丢失的报文段意味着拥塞,因此当丢失报文段时应该降低其发送速率;
    • 一个确认报文段指示网路正在向接收方交付发送方的报文段,因此当到达一个未确认的确认报文段,增加其发送速率;
    • 带宽探测:TCP调节其传输速率的策略是增加其速率以响应到达的ACK,仅出现丢包事件时,才减小发送速率;

问题三:当发送端感知到拥塞,又采用何种算法来改变其发送速率?

  • TCP拥塞控制算法
    • 慢启动;
    • 拥塞避免;
    • 快速恢复;

其中,慢启动和拥塞避免是TCP强制必需部分,而快速恢复则只是推荐而非必需。

7.01 慢启动

一条TCP连接开始时,cwnd的值通常初始化为一个MSS的较小值,初始发送速率约为MSS/RTT,例如MSS=500字节,RTT=200ms,则初始速率约为20kbps,这个速率相较于带宽而言要小得多

慢启动状态:cwnd的值以一个MSS开始并且每当报文段首次被确认就增加一个MSS(是发送方对每个确认报文段给拥塞窗口增加一个MSS,即呈指数增长),如图:

image-20201105215713739

何时结束慢启动状态呢?

  • 如果出现超时丢包事件,TCP发送方将cwnd设置为1并重新启动慢启动过程,还将第二个状态变量ssthresh的值设置为cwnd/2;
  • 当cwnd的值等于ssthresh的值时,结束慢启动并且TCP转移到拥塞避免模式;
  • 检测到三个冗余ACK时,TCP执行快速重传并进入快速恢复状态;

以下是TCP拥塞控制算法的完整FSM描述:

image-20201105220553024

7.02 拥塞避免

一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时值的一半。收到确认时,TCP也不会再每过一个RTT将cwnd的值翻番,而是每个RTT只将cwnd的值增加一个MSS

何时结束拥塞避免的线性增长?

  • 出现丢包时,TCP将ssthresh的值记录为cwnd值的一半;
  • 收到三个冗余ACK时,TCP将cwnd的值减半(但是收到的三个冗余ACK还是会线性增加三个MSS),再将ssthresh的值记录为cwnd值的一半,并且接下来进入快速恢复状态;

7.03 快速恢复

  • 进入快速恢复状态,重传之后当对丢失报文段的一个ACK到达时,TCP在降低cwnd后进入拥塞避免状态;
  • 出现超时丢包事件,cwnd的值被设置为1个MSS,并将ssthresh的值设置为cwnd值的一半,并迁移到慢启动状态;

7.04 回顾TCP拥塞控制

忽略一条TCP连接的初始慢启动阶段,考虑正常运行的一般状态,即拥塞避免时:

  • 此时TCP拥塞控制是:每个RTT内cwnd线性增加1MSS,出现3个冗余ACK时cwnd减半,这种拥塞控制又被称为加性增、乘性减(AIMD)
image-20201105223325409

7.05 对TCP吞吐量的宏观描述

对于TCP的锯齿状行为,考虑一个存活期长的TCP连接的平均吞吐量(忽略短暂的慢启动阶段):

  • 在一个特定的往返间隔内,窗口长度为W字节,往返时间为RTT秒,TCP发送速率约为W/RTT;
  • TCP每经过1个RTT将为W增加一个MSS,直到发送丢包事件,并如此往复;
  • 假设在持续连接期间RTT和W保持不变,那么TCP的传输速率在W/(2×RTT)到W/RTT之间;
  • 这条假设导出TCP稳态行为高度简化的宏观模型:
    • 当速率增长到W/RTT时,网络丢弃来自连接的分组,发送速率减半,进而没过一个RTT发送速率就增加MSS/RTT,直到再次到达W/RTT,重复过程;
    • 一条连接的平均吞吐量=0.75×W/RTT;
-------- 本文结束 感谢阅读 --------