运输层
如果说应用层报文是信,网络层报文是邮车,那么运输层就是负责把信放进邮车的邮递员。运输层不关心报文的具体内容, 也不关心报文的运输方式,而关心报文的发送方式,如何把进程中的报文提取给主机的发送端,如何把主机的接收端收到的报文交给某个正在运行的进程。Internet的运输层有两个,分别是传输控制协议(Transmission Control Protocol, TCP)和用户数据报协议(User Datagram Protocol, UDP)。这两个协议通过采用不同的发送方式提供不同的服务。特别的,TCP协议能够保证报文可靠地送到接收方手中,这是通过与接收方通信并在丢包时重传实现的;UDP协议不保证报文能可靠地转交,因而通常有更高的传输效率。
多路复用与多路分解(Multiplexing & Demultiplexing)
应用层服务于运行在端系统上的一个进程,运输层服务于一个端系统。当一个进程产生一个报文要发送时,需要把该报文传到主机的某个输出端口。接收端的主机在某个输入端口接收到报文时,需要把该报文传到正在端系统上运行的某个进程。这两个任务由运输层协议完成。如何实现呢?我们可以为应用层报文封装上首部信息,用以标识该报文从哪个进程来,到哪个进程去。然而运行在端系统上的进程是动态的,因此与其标识进程,我们标识一个socket。socket是由运输层协议规定的一个数字,例如当我们标识一条报文是从源主机的73号socket发送到目的主机的80号socket的,在接收方接收到该报文时就可以通过运输层协议解析出socket 80代表进程xxx。这样,不同进程的应用层报文就可以沿同一信道传输,来自同一信道的运输层报文也可以按照socket分配给不同进程。为应用层报文段封装上socket number的工作称为多路分解,识别socket number把应用层报文交给对应进程的工作称为多路复用。socket number是一个16bit的数,其中0-1023是周知端口号,是分配给专门的协议的。
理论上,只是为了发送的话,报文中不必包括源socket。但我们总是期待发送是有回复的。但就好像写信时我们不仅会写上对方的地址,还会写上自己的地址一样,把源socket告诉接收方有助于接收方回复的报文在源主机上的多路复用。
用户数据报协议(User Datagram Protocol, UDP)
UDP是最简单的运输层协议。它提供了运输层的最基本的服务,并除此之外几乎没提供任何服务。一个UDP报文封装了应用层报文,并包含四个首部字段:源端口号,目的端口号,数据报文长度,以及一个检验和。UDP的检验和用于检测差错(但无法恢复),计算方式对每个16bit字段做带wraparound的加法再取反。
传输控制协议(Transmission Control Protocol, TCP)
TCP连接的建立与拆除
与UDP协议不同,TCP协议是面向连接的。在发送实际内容的消息之前,发送方和接收方会进行三次握手建立一条TCP连接。我们把请求建立连接的一方称为客户,被请求的称为服务器,三次握手的具体过程如下:客户发送一条特殊的TCP报文请求连接;服务器回复一条特殊的TCP报文响应,并为连接分配一些缓存和变量;收到服务器相应以后,客户也分配一些缓存和变量,然后返回一个对响应的响应。三次握手以后,就可以在此TCP连接上持续发送多条报文了。
客户拆除TCP连接时,向服务器发送拆除请求,服务器返回确认报文;然后服务器发送终止报文,客户返回确认。至此,两台设备都释放所有资源,连接被拆除。
可靠数据传输(Reliable Data Transfer, RDT)
如果网络层协议能够保证数据可靠交付,那么运输层只需要封装数据并传输即可(就像UDP做的那样)。但网络层信道可能是不可靠的,例如可能丢包,数据报内容可能出现差错,可能打乱包的顺序等等。TCP能在网络层传输不可靠的基础上提供可靠数据传输(RDT)服务。RDT问题是计算机网络中最重要的问题(之一)。
简单起见,首先只考虑数据报内容出现差错而不考虑丢包和乱序的问题。此时我们可以通过引入重传机制来保证传输可靠。类比于人类协议,在听写时如果写的人听清楚了,及时回复“嗯”,否则回复“什么?没听清”。一个RDT协议也可以实现这一点,这样的协议称为自动重传请求协议(Automatic Repeat reQuest, ARQ)。为了实现这一点,接收方要能够进行差错检测,并发消息反馈给发送方(成功ACK,不成功NAK),发送方在接到差错反馈时重传数据。注意,发送方每发送一个数据报必须等待反馈到达自身以后才能发送下一个数据报,因此这样的协议被称为stop-and-wait协议。但是,如果反馈信息本身也出现差错了怎么办?为此,我们在封装报文时还可以封装报文的编号。如果接收到的反馈信息是模糊的,那么发送方默认重传,接收方会等待下一个序号的数据,当收到序号错误的数据时,就会意识到反馈出错了,继续等待同一序号即可。由于序号最多只会偏差1,因此我们用模2意义下的序号(0或1)即可。
如果丢包可能出现, 也即发送的报文或相应的报文可能传到一半丢失了。此时可以设置一个计时器,如果发送方发现接收方超时未响应就重传。如何设置计时器的时间间隔呢?显然,该时间间隔应该参考报文的一次往返时间(RTT)。在TCP中,我们用指数加权移动平均(EWMA)的方法估计RTT(用同样方法估计出偏差值的平均值),把时间间隔设定为\(\text{EstimatedRTT}+4\cdot \text{DevRTT}\)。
然而,以上这样的stop-and-wait协议对信道的利用率是很低的,大多数时候发送方都在等待,只在极小比例的时间传数据。一个提高效率的方式是把原来的方案修改为流水线(pipeline)版本:把序号0,1拓展到序号0,1,...,N,并在发送方与接收方处设置缓存。然而,流水线传输的差错恢复也会变复杂。解决流水线的差错恢复的两种基本方法(协议)为回退N步(Go-Back-N, GBN)和选择重传(Selective Repeat, SR)。
GBN协议中,发送方可以发送多个packet,一起等待确认。但是,最早的等待中的未确认的packet到当前要发送的packet之间packet的总数不能超过\(N\)个(就像一个滑动窗口,直到最早的packet被确认窗口才能右移)。如果收到一个来自应用层的待发送的报文,但发现窗口已满,那么GBN会返回该报文,让应用层待会儿再试(比如我们经常看到在应用层显示,系统繁忙请稍后再试)。在GBN中,只设置一个计时器,当一个包发送时如果计时器已经启动不会重置计时器。如果计时器超时,此时重发所有已发送但未确认的包(这就是为什么该方案称为回退N步)。接收方只有按序接收到一个包才返回ACK以及对应的编号,否则一律把收到的包丢弃,回复ACK以及上一个成功接收的包的编号(称为一个冗余ACK)。
GBN大大提高了信道的利用率,但如果窗口很大一个丢包通常会引起大量的重传。选择重传在重传上提供了一个更合理的机制。在选择重传中,如果接收方收到一个packet而发现这个packet顺序错误,它将返回ACK这个packet并缓存这个分组,直到所有序号更小的分组都成功到达以后再把分组上传。所以现在发送方和接收方都有一个长度为\(N\)的滑动窗口,但这两个窗口可能是不同步的。并且,每个包都需要维护一个单独的计时器以解决丢包问题。
TCP的差错恢复机制时GBN与SR的混合体。TCP只用一个定时器。如果定时器未运行就发送当前报文并启动定时器。一旦定时器超时,就重传最小序号的未确认报文,并重启定时器。在收到确认时,如果当前还有未确认的报文,就启动定时器。每次出现超时,就把该时间间隔翻倍。但是一旦有了新的对RTT的估计,就用该公式重新计算时间间隔。一旦接收方接收到三个冗余ACK,就启动快速重传(在定时器重启前就重传未确认的packet)。
流量控制(Flow Control)
由于接收方和发送方的TCP缓存大小都是有限的,因此有必要匹配发送和接收的速度以免缓存溢出。简单来说,TCP会在报文首部提供发送方与接收方剩余内存的信息,如果对方没有缓存空间了就不再发送。
拥塞控制(Congestion Control)
当网络中有大量数据报在传递时,会在路由器处形成排队造成拥塞。一旦拥塞超过路由器的缓存,则会被路由器丢弃造成丢包。TCP协议通过感知网络的用色成都来控制发送方的发送速率,这称为拥塞控制。其基本思想是,用丢包来反映出网络拥塞。当出现丢包时,降低发送速率;当数据成功发送时,增大发送速率。
TCP首先以慢启动(slow-start)的方式确定发送速率。初始时,发送速率为一个MSS。每次收到确认时,发送速率翻倍。可见TCP在慢启动阶段速率指数增长。当到达一个threshhold时,结束慢启动模式进入拥塞避免模式。当遇到因为超时而发生的丢包事件时,速率重置为一个MSS,并把threshold设置为当前速率的一半,重新开始慢启动。当遇到三个冗余ACK的丢包事件时,进入快速恢复模式。
在拥塞避免模式,每次收到确认只增加一个MSS,也即从指数增长改为线性增长。遇到因为超时而发生的丢包事件时, 速率重置为一个MSS,并把threshold设置为当前速率的一半,重新开始慢启动。当遇到三个冗余ACK的丢包事件时,进入快速恢复模式。
在快速恢复模式,每收到一个导致进入快速回复模式的报文的冗余ACK时增加一个MSS。当确认的ACK到达以后回到拥塞避免模式。
从以上方案可以看出,拥塞避免模式和快速恢复模式将会使得曲线呈锯齿状摆动。可以计算出吞吐量的平均值大约为\(\dfrac{3}{4}\cdot \dfrac{W}{\text{RTT}}\)。

浙公网安备 33010602011771号