03:运输层

本文最后更新于:1 年前

写在前面

纸上得来终觉浅,绝知此事要躬行

一、概述和运输层服务

运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信(logic communication)功能。
逻辑通信
概览

1.1 运输层和网络层的关系

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

1.2 因特网运输层概述

因特网网络层协议有一个名字叫IP,即国际协议。IP尽最大努力交付报文段,但不做任何确保,因此它是不可靠服务。每台主机至少有一个IP地址。

UDP和TCP最基本的责任是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务,这种服务被称为运输层的多路运输多路分解。其次,UDP和TCP还可以通过在其报文段首部中包括差错检查字段而提供完整性检查。UDP仅能提供这两种服务,TCP还有附加服务(可靠数据传输、拥塞控制等)。

二、多路复用与多路分解

多路分解:将运输层报文段中的数据交付到正确的套接字。
多路复用:在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息(用于多路分解)从而生成报文段,然后将报文段传递给网络层。

每个报文段使用源端口号字段(sourse port number field)和目的端口号字段(destination port number field)来指示该报文段所要交付到的套接字。端口号是一个16比特的数,其大小在0-65535之间。0-1023范围的端口号称为周知端口号(well-known port number),它们保留给注入HTTP和FTP之类的周知应用层协议。
运输层报文段中的源与目的端口字段

2.1 无连接的多路复用与多路分解

在运输层,无连接的网络传输是通过UDP来实现的。UDP报文中只有源端口号和目的端口号,一个UDP套接字是由一个含有目的IP地址和目的端口号的二元组来全面标识的。

例如主机A产生了一个UDP报文段,报文段中就会包括源端口号(11111)、目的端口号(22222)、程序数据(还有两个其他的值,在这里我们不关心)。然后,运输层将生成的报文段交给网络层。网络层将其放到一个IP数据报中,并提供尽力而为的交付,将其发送到主机B中。如果该报文到达主机B,主机B运输层就会检查该报文的端口号,并将该报文段传递给套接字的端口号为接收到的报文段的目的端口号(22222)的套接字。从而实现了进程间的网络通信。而源端口号的作用是为了让主机B能向主机A发送信息的,也就是说,当主机B在接收到主机A的数据后,要向主机A发送一个回应时,主机B发送的报文段的目的端口号就是11111.

2.2 面向连接的多路复用与多路分解

在运输层中面向连接的网络传输多使用TCP,而TCP套接字和UDP套接字之间有一个细微的差别,就是,TCP套接字是由一个四元组(源IP地址、源端口号,目的IP地址,目的端口号)来标识的。这样,当一个TCP报文段从网络到达一台主机时,主机会使用全部4个值来将报文段定向,即多路分解到相应的套接字。

与UDP不同的是,两个具有不同源IP或源端口号的到达的TCP报文段将被重定向到两个不同的套接字。

尽管如此,而TCP的多路利用和多路分解的工作原理与无连接的UDP的多路复用和多路分解的原理还是大致一样的。

2.3 Web服务器与TCP

  • 连接套接字与进程之间并非总是有着一一对应的关系 。
  • 当今的高性能 Web服务器通常只使用一个进程,但是为每个新的客户连接创建一个具有新连接套接字的新线程。(线程可被看作是一个轻量级的子进程。)
  • 如果客户与服务器使用持续 HTTP ,则在整条连接持续期间,客户与服务器之间经由同一个服务器套接字交换 HTTP 报文 。然而,如果客户与服务器使用非持续连接,则对每一对请求/响应都创建一个新的 TCP连接并在随后关闭,因此对每一对请求/响应创建一个新的套接字并在随后关闭 。 这种套接字的频繁创建与关闭会严重地影响一个繁忙的Web服务器的性能(虽然有许多操作系统技巧可用来减轻这个问题的影响) 。

三、无连接运输:UDP

UDP只提供复用和分解,它有如下优点:

  • 关于何时、发送什么数据的应用层控制更为精细:这是因为一旦应用程序将数据交给UDP,UDP就会打包将其发送给网络层,不会受到传输层的调节,这在一些实时应用中比较实用;当然,应用程序还可以通过UDP+自主开发一些功能的模式来扩展UDP。
  • 无需建立连接:所以就不会引入额外的时延。这也可能是DNS使用UDP而不是TCP的主要原因,如果使用TCP的话,DNS服务将会慢很多;HTTP使用TCP的主要原因是对TCP的可靠性的依赖超过对速度的要求;
  • 无需维护连接状态:TCP为了实现可靠数据传输和拥塞控制需要在端系统中维护一些参数,这些参数包括:接收和发送的缓存、拥塞控制参数、确认号和序号;这些参数信息都是必须的;而UDP因为不建立连接,所以自然也就不需要维护这些状态,这就减少了时空开销;
  • 分组首部更小:TCP有20字节的首部开销,而UDP只有8字节;

3.1 UDP报文段结构

UDP报文段结构

3.2 UDP检验和

UDP检验和的需要计算UDP头部(计算的时候校验和部分的16位需要置0)加数据部分,还需要加上UDP伪头部。计算步骤如下:

  1. 将UDP伪头部、UDP头部和数据部分全部用16进制数表示。
  2. 将第一个16进制数与第二个16进制数相加,得到一个32位的数,如果32位数的高16位大于0,需要将高16位与低16位再相加,得到一个32位的数,直到高16位为0,得到这一次相加的结果。
  3. 将上一步得到的16位数与第三个数16进制的数相加,重复第二步,直到累加完所有的16进制数,并且得到的结果为16进制数。
  4. 将累加最后得到的16进制数取反,得到校验和。

四、可靠数据传输原理

提供的服务
服务实现

4.1 构造可靠数据传输协议

4.1.1 rdt 1.0

rdt1.0是基于理想情况下的协议,假设所有信道都是可靠的,没有比特位的翻转,没有数据包的丢失与超时,所以rdt1.0的传输功能就是发送方发送数据,接收方接受数据。
用于完全可靠信道的协议

4.1.2 rdt 2.0

rdt2.0在rdt1.0的基础上解决了比特位差错的问题,这里的比特位差错发生在运输层下面的不可信信道中数据包中的1可能会变0,0可能会变成1。

rdt2.0增加了3种新机制:

  • 差错检测
  • 接收者反馈接受信息(ACK,NAK)
  • 重传

在运输层对应用层的数据进行打包处理时,新增checksum(校验和),从而接收端可以对其数据包进行检验,如果正确,返回ACK,发送者继续发送下一个数据包;如果不正确,返回NAK,发送者重传数据。


但是rdt2.0有着一个致命的缺点,只考虑了发送方到接收方的数据传输,如果反馈信息ACK、NAK传输时发生比特位翻转会出现什么情况?如果ACK发生翻转,那么发送方会再次重复的发送相同的数据包;如果NAK发生翻转,那么发送方会认为数据传输情况很好,但是接收方却已经收到了一个错误的数据包。

4.1.3 rdt 2.1

在rdt2.0的基础之上,发送方在打包数据包时添加了0或者1编号,同样ACK,NAK字段上也添加了0、1字段,表示0、1号字段的确认或者否定。发送方就有了2种状态发送0号数据包,1号数据包,接收方也有了2种状态等待0号数据包和等待1号数据包。

现在假设情景发送方向接收方发送0号数据包,如果接收方接收到0号数据包,返回ACK,但是ACK出现翻转,接收方处于等待1号数据状态,发送方重复发送0号数据,接收方会拒绝0号数据,避免重复。如果接收方接收到0号数据包出现错误,返回NAK,但是NAK出现翻转,接收方处于等待0号数据状态,发送方继续发送1号数据,接收方会拒绝1号数据,避免错序。

4.1.4 rdt 2.2

dt2.2是在rdt2.1上的基础之上做了小小的改善,摒弃了NAK,只需采用ACK。我们在ACK的信息上加上了期望的顺序号。

现在假设情景发送方向接收方发送0号数据包,如果接收方接收到0号数据包,返回(ACK,1),发送方接着发送1号数据包。如果接收方接收到0号数据包出现错误,返回(ACK,0),发送方重传0号数据包。

rdt2.2之前的版本都重在处理数据包的比特位翻转情况,却没有考虑到数据包在传输过程中出现的数据包丢失问题,这样数据包丢失会使得网络处于拥塞状态。

4.1.5 rdt 3.0

rdt3.0在rdt2.2的基础之上处理了数据包丢失的情况,增加了计时器的机制,如果在RTT时间段内,发送方没有接收到反馈信息,那么发送方默认数据包已经丢失了,会自动重传。

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

rdt 3.0性能不够,它的利用率很低:

Usender=L/RRTT+L/RU_{sender}=\frac{L/R}{RTT+L/R}

流水线可靠数据传输协议 :不使用停等方式,允许发送方发送多个分组而无需确认等待,这样会大大提升发送方信道的利用率。

流水线可靠数据传输协议

  • 必须增加序号范围,每个分组(不包含重传的)必须有唯一的序号
    我们知道一个分组的序号存储在分组首部的固定长度字段中,序号字段长度为kbit,则该序号范围是[0,2^k-1].
  • 发送方和接收方也需要能够缓存多个分组
    发送方:至少能够缓存那些已被发送但未被确认的分组
  • 所需序号范围和缓存大小取决于协议是如何处理丢失、损坏及时延过大的分组,这里这类分组有两种方法:回退N步协议和选择重传协议

4.3 回退N步(GBN或滑动窗口)

在GBN协议中,允许发送方发送多个分组而不需等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N。

GBN中发送方看到的序号

通过基于ACK、无NAK的GBN协议的FSM图来了解GBN的原理:

GBN发送方的扩展FSM描述
GBN接收方的扩展FSM描述

4.4 选择重传(SR协议)

GBN协议虽然实现了流水线式传输数据,提高了发送方信道的利用率,但是GBN有一个很大的缺陷就是一旦超时,会有许多不必要重传的分组被重新传送。尤其是当窗口长度N很大时,随着差错率的提升,信道中会充斥着不必要重传的分组。

SR协议原理:

  • 发送方
    为每一个分组设置一个定时器,这样可以在某一个分组超时时对其单独进行重传。
  • 接收方
    SR接收方将确认一个正确接收的分组而不管其是否按序。在未出现分组丢失之前,正确接收到的分组都会被交付给上层。一旦出现分组丢失,失序的分组将被接收方缓存起来,直到所有丢失分组(即序号更小的分组)皆被接收为止,这时将缓存中的分组一起交付给上层。

五、面向连接的运输:TCP

5.1 TCP连接

5.1.1 TCP连接的特点

  • 面向连接:在一个应用进程可以开始向另一个应用进程发送数据之前,两个进程必须相互“握手”。
  • 全双工服务:如果一台主机上的进程A与另一台主机上的进程B存在一条TCP连接,那么应用层数据就可在进程B流向进程A的同时,也从进程A流向进程B。
  • 点对点:在单个发送方与单个接收方之间的连接。

5.1.2 三次握手

TCP是面向连接的,所以每次传输数据之前,必须要建立TCP连接,在TCP建立连接时主要解决三个层面问题:

  • 使连接的每一方都能确认对方的存在
  • 协商连接中参数,比如各方窗口值,时间戳等
  • 各方对运输资源如缓存大小、连接表等进行分配

三次握手
默认情况下客户端client和服务端sever的TCP进程都处于CLOSED(关闭)状态
服务端TCP服务进程先建立传输控制块TCB,然后服务端进入LISTEN状态,等待客户端连接请求。

  • 第一次握手:客户端TCP进程也先建立传输控制块TCB,然后向服务端发送连接请求报文段,此时SYN=1,随机选定一个初始序号seq=x,,此报文不能携带数据,但是要消耗掉一个序号,发送完毕后,客户端进入SYN-SENT(同步已发送)状态
  • 第二次握手:服务端收到客户端请求连接报文段后,若同意建立连接,则发送确认报文,确认报文中SYN=1、ACK=1,确认号ack=x+1,同时随机选定一个自己序号seq=y,确认报文段同样不能携带数据,但是也要消耗掉一个序号,发送完毕后服务端进入SYN-RCVD(同步收到)状态
  • 第三次握手:客户端收到确认报文后,检查ACK=1,ack=x+1是否正确,若正确,则向服务端发送确认报文,确认报文中ACK=1,ack=y+1,seq=x+1,发送后进入ESTAB-LISHED状态,服务端收到确认报文后,也进入ESTAB-LISHED状态,此时双方TCP连接正式建立。

上面的连接建立过程就是TCP三次握手

5.2 TCP报文段结构


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!