第3章 运输层
3.1 概述和运输层服务
运输层协议为运行在不同主机上的应用进程提供了逻辑通信功能。
3.1.1 运输层和网络层的关系
一方面,运输协议能够提供的服务常常受制于底层网络层协议的服务模型。如果网络层协议无法为主机之间发送的运输层报文段提供时延或带宽保证的话,运输层协议也就无法为进程之间发送的应用程序报文提供时延或带宽保证。
另一方面,即使底层网络协议不能在网络层提供相应的服务,运输层协议也可以提供某些服务。既便底层网络协议是不可靠的,运输协议也能为应用进程提供可靠的数据传输服务。即使网络层不能保证运输层报文段的机密性,运输协议也能使用加密来确保应用程序报文不被入侵者读取。
3.3.2 因特网运输层概述
TCP UDP IP
IP的服务模型是尽力而为交付服务(best-effort delivery service),但它不确保报文段的交付,不保证报文段的按序交付,不保证报文段中数据的完整性。由于这些原因,IP被称为不可靠服务(unreliable service)。
UDP和TCP最基本的责任是:将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务。
将主机间交付扩展到进程间交付被称为运输层的多路复用(transport-layer multiplexing)与多路分解(demultiplexing)。
3.2 多路复用与多路分解
多路分解:将运输层报文段中的数据交付到正确的套接字的工作。
多路复用:在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息(这将在以后用于分解)从而生成报文段,然后将报文段传递到网络层。
端口号是一个16比特的数,其大小在065535(2^16 - 1)之间。01023范围的端口号称为周知端口号(well-known port number),是受限制的,它们保留给诸如:
- HTTP(80)
- FTP(21)
- SMTP(25)
- POP3(110)
- DNS(53)
一个UDP套接字是由一个二元组全面标识的,该二元组包含一个目的IP地址和一个目的端口号。因此,如果两个UDP报文段有不同的源IP地址和/或源端口号,但具有相同的目的IP地址和目的端口号,那么这两个报文段将通过相同的目的套接字被定向到相同的目的进程。
TCP套接字是由一个四元组(源IP地址,源端口号,目的IP地址,目的端口号)来标识的。与UDP不同,两个具有不同源IP地址或源端口号的TCP报文段将被定向到两个不同的套接字,除非TCP报文段携带了初始创建连接的请求。
注意:报文段和套接字是两个概念。报文段-->套接字。运输层报文段包含源端口号和目的端口号信息来指示该报文段所要交付到的套接字;而套接字具有唯一标识符。
- 源端口号:用作“返回地址”的一部分。
- 目的端口号:将每个报文定向(分解)到相应的套接字。
3.3 无连接运输:UDP
UDP: User Datagram Protocol 用户数据报协议
有许多应用更适合UDP,原因主要有以下几点:
- 发送什么数据以及何时发送这些数据的应用层控制更为精细。采用UDP时,只要应用进程将数据传递给UDP,UDP就会将此数据打包并立即将其传递给网络层。实时应用通常对最小的发送速率有要求,不希望过分地延迟报文段的传送,且能容忍一些数据丢失。
- 无须连接建立。UDP不会引入建立连接的时延。
- 无连接状态。TCP需要在端系统中维护连接状态,而UDP不需要。
- 分组首部开销小。每个TCP报文段都有20字节的首部开销,而UDP仅有8字节的开销。
3.3.1 UDP报文段结构
UDP首部只有4个字段,每个字段由两个字节组成,一共8字节(64比特)
长度字段:指示了在UDP报文段中的字节数(首部加数据)。
校验和:检测UDP用户数据报在传输中是否有错。有错就丢弃。进行校验和操作时在UDP首部增加12字节的伪首部,用于计算校验和。
3.3.2 UDP检验和
UDP检验和提供了差错检验功能。即用于确定当UDP报文段从源到达目的地移动时,其中的比特是否发生了改变。
具体方法:发送方的UDP对报文段中的所有16比特字的和进行反码运算,求和时遇到的任何溢出都被回卷。
为什么?:在既无法确保逐链路的可靠性,又无法确保路由器内存中的差错检测的情况下,如果端到端数据传输服务要提供差错检测,UDP就必须在端到端基础上在运输层提供差错检测。
这是一个在系统设计中被称颂的端到端原则(end - end principle)的例子,该原则表述为因为某种功能(此时为差错检测)必须基于端到端实现:“与在较高级别提供这些功能的代价相比,在较低级别上设置的功能可能是冗余的或几乎没有价值的。
虽然UDP提供差错检测,但它对差错恢复无能为力。UDP的某种实现只是丢弃受损的报文段;其他实现是将受损的报文段交给应用程序并给出警告。
3.4 可靠数据传输原理
3.4.1 构造可靠数据传输协议
rdt (reliable data transfer)
udt (unreliable data transfer)
有限状态机 (Finite-State Machine, FSM)
自动重传请求 (Automatic Repeat reQuest, ARQ) 协议
肯定确认:ACK (acknowledgement)
否定确认:NAK (negative acknowledgement)
停等 (stop-and-wait) 协议(当发送方处于等待ACK或NAK的状态时,它不能从上层获得更多的数据,仅当接收到ACK并离开该状态时才能发生rdt_send()事件)
序号 (sequence number)
冗余数据分组 (duplicate data packet)
倒计数定时器 (coutdown timer)
3.4.1.1 rdt1.0 ---- 经完全可靠信道的可靠数据传输
| 发送端(等待来自上层的调用) | 接收端(等待来自下层的调用) |
|---|---|
| rdt_send(data) 接收来自较高层的数据 | rdt_rcv(packet) 接收来自底层信道的分组 |
| packet = make_pkt(data) 打包封装 | extract(packet, data) 解封装 |
| udt_send(packet) 发送分组给底层信道 | deliver_data(data) 将数据上传给较高层 |
3.4.1.2 rdt2.0 ---- 经具有比特差错信道的可靠数据传输
| 发送端(等待来自上层的调用;等待ACK或NAK) | 接收端(等待来自下层的调用) |
|---|---|
| rdt_send(data) 接收来自较高层的数据 | rdt_rcv(rcvpkt)&&corrupt(rcvpkt) 收到一个分组,并发现有错 |
| sndpkt = make_pkt(data, checksum) 封装一共带有检验和(checksum)的分组 | sndpkt = make_pkt(NAK) 打包封装一个NAK分组 |
| udt_send(sndpkt) 发送分组给底层信道 | udt_send(sndpkt) 发送 |
| rdt_rcv(rcvpkt)&&isACK(rcvpkt) 收到一个ACK分组,协议返回到等待来自上层数据的状态 | rdt_rcv(rcvpkt)&¬corrupt(rcvpkt) 收到一个分组,并发现正确 |
| rdt_rcv(rcvpkt)&&isNAK(rcvpkt) 收到一个NAK分组 | extract(rckpkt, data) 解封装 |
| udt_send(sndpkt) 重传,并等待ACK或NAK | deliver_data(data) 将数据上传给较高层 |
| sndpkt = make_pkt(ACK) 打包封装一个ACK分组 | |
| udt_send(sndpkt) 发送 |
PS : sndpkt == sendpacket
rcvpkt == receivepacket
sendpacket不一定等于receivepacket
3.4.1.3 rdt2.1 ---- 处理出错的ACK/NAK
新增:
- sndpkt = make_pkt(0, data, checksum)
- sndpkt = make_pkt(ACK, checksum)
发送端(等待来自上层的调用 0;等待ACK或NAK 0;等待来自上层的调用 1;等待ACK或NAK 1)
- 在分组中加入序列号
- 两个序列号(0,1)就够了(1bit)
- 必须检测ACK/NAK是否出错(需要差错检测checksum)
- 状态数变成了rdt2.0的两倍(必须记住当前分组的序列号是0还是1)
接收端(等待来自下层的调用 0; 等待来自下层的调用 1)
- 必须检测接收到的分组是否是重复的(状态会指示希望接收到的分组序号是0还是1)
3.4.1.4 rdt2.2 ---- 无NAK的协议
- 功能同rdt2.1,但只使用ACK(ACK要编号)
- 为后面的一次发送多个数据单位做准备
新增
- sndpkt = make_pkt(ACK, 0, checksum)
发送端(等待来自上层的调用 0;等待ACK 0;等待来自上层的调用 1;等待ACK 1)
- 当收到重复的ACK(如再次收到ACK 0)时,发送方与收到NAK采取相同的动作:重传当前分组。
接收端(等待来自下层的调用 0; 等待来自下层的调用 1)
- 对最后正确接收的分组发ACK,以替代NAK,且显式地包含被正确接收分组的序号,如snd = make_pkt(ACK, 0, checksum)
3.4.1.5 rdt3.0 ---- 经具有比特差错的丢包信道的可靠数据
新的假设:下层信道可能会丢失分组(数据或ACK)
解决方法:发送方等待ACK一段合理的时间
新增:start_timer, timeout, stop_timer
发送端(等待来自上层的调用 0;等待ACK 0;等待来自上层的调用 1;等待ACK 1)
- 发送方等待ACK一段合理的时间
- 引入冗余数据分组来处理冗余数据分组的情况
接收端(等待来自下层的调用 0; 等待来自下层的调用 1)
PS:链路层的timeout时间确定的;传输层timeout时间是适应式的;设置一个合理的超时时间很重要,至少保证重传时有极大概率把握已经是丢包了,又没有耽误太多时间。
3.4.2 流水线可靠数据传输协议
rdt3.0性能问题的核心在于它是一个停等协议,在今天的高速网络中,利用率低 。
发送方或信道的利用率(utilization):发送方实际忙于将发送比特送进信道的那部分时间与发送时间之比 。
流水线 (pipelining)技术:不以停等方式运行,允许发送方发送多个分组 。
解决流水线的差错恢复有两种基本方法:回退N步(Go-Back-N, GBN)和选择重传(Selective Repeat, SR) 。
3.4.3 回退N步
- N被称为窗口长度,GBN协议也常被称为滑动窗口协议(sliding-window protocol)。
- 序号范围:[0, 2k - 1],k是分组序号字段比特数。
- 在GBN协议中,对序号为n的分组的确认采用累积确认(cumulative acknowledgement)的方式,表明接收方已正确收到序号为n的以前且包括n在内的所有分组。
- 就像在停等协议中那样,定时器将再次用于恢复数据或确认分组的丢失。若出现超时,发送方重传所有已发送但还未被确认过的分组。
3.4.4 选择重传
- GBN本身也有一些情况存在性能问题。尤其是当窗口长度和带宽时延积(BDP)都很大时,单个分组的差错就能引起GBN重传大量的分组。
- 序号范围:[0, 2k-1],k是分组序号字段比特数。窗口长度必须小于或等于序号空间大小的一半。否则接收方无法区分是重传还是新分组。
- 接收方先收到接收缓存内靠后的分组:只缓存,不交付,等前面的都到了再按序交付。
- 接收缓存内的分组被重复收到:仍然必须产生一个ACK。
PS :
带宽时延积是一种 网络性能指标 。在 数据通信 中,带宽时延积(Bandwidth-Delay Product;或称带宽延时乘积、带宽延时积等)指的是一个数据链路的能力(每秒比特)与来回通信延迟(秒)的 乘积 。 其结果是以比特(或 字节 )为单位的一个数据总量,等同在任何特定时间该网络线路上的最大数据量——已发送但尚未确认的数据。
发送缓冲区与带宽时延积的关系:
- 如果发送缓冲区「超过」带宽时延积,超出的部分就没办法有效的网络传输,同时导致网络过载,容易丢包;
- 如果发送缓冲区「小于」带宽时延积,就不能很好的发挥出网络的传输效率。
所以,发送缓冲区的大小最好是往带宽时延积靠近。
小结:
3.5 面向连接的运输:TCP
TCP: Transmission Control Protocol 传输控制协议
Seq就是序号,ACK就是确认号,表示我当前的报文段数据时从第42个字节开始,希望从B那里获取从第79个字节开始的报文段
3.5.1 TCP连接
- TCP被称为是面向连接的(connection-oriented),这是因为在一个应用进程可以开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,即他们必须相互发送某些预备报文段,以建立确保数据传输的参数。
- TCP连接是一条逻辑连接,其共同状态仅保留在两个通信端系统的TCP程序中。
- TCP连接提供的是全双工服务(full-duplex service):如果一台主机上的进程A与另一台主机上的进程B存在一条TCP连接,那么应用层数据就可在从进程B流向进程A的同时,也从进程A流向进程B。
- TCP连接也总是点对点(point-to point)的,即在单个发送方与单个接收方之间的连接。
TCP可以从缓存中取出并放入报文段中的数据数量受限于最大报文段长度。
最大报文长度(Maximum Segment Size, MSS) :通常根据最大传输单元来设置。设置该MSS要保证一个TCP报文段(当封装在一个IP数据报中)加上TCP/IP首部长度(通常40字节)适合单个链路层帧。以太网和PPP链路层协议都具有1500字节的MTU,因此MSS的典型值为1460字节。(不包括首部)
最大传输单元(Maximum Transmission Unit, MTU):最初确定的由本地发送主机发送的最大链路层帧长度。
3.5.2 TCP报文段结构
【源端口】- 16bit
来源处的端口号;端口号有65536个,即2^16
【目的端口】- 16bit
目的处的端口号;
【序号】- sequence number field - 32bit
TCP在对数据进行分段的时候,会给每一个TCP报文段添加一个序号,序号字段的值其实是这个package数据部分的第一位在整个data stream中所在的位置。这么做的原因是,TCP是面向连接的可靠服务,这个序号可以保证数据在传输过程中保持有序性,接受端可以通过这个序号确认收到的数据的完整性和先后顺序;
【确认号】- acknowledgement number field - 32bit
确认号,是期望收到对方的下一个报文段的数据的第一个字节的序号;
【数据偏移】- 4bit
其实它本质上就是“首部长度” ,因为“数据偏移”是指TCP报文段的数据部分的起始处距离TCP报文段的起始处的距离,即首部长度。
数据偏移总共占4bit,因此最大能表示的数值为15。但TCP的报文头部至少为20字节。因此数据偏移的单位是“4字节”,此处的设计和IP数据报的设计是完全相同的,所以说TCP报文段首部的长度最长为15×4=60字节,且首部长度必须为4字节的整数倍。
【保留字段】- 6bit
IETF文档指出,这6bit在标准中是保留字段,留待以后使用,必须为0。我猜测,有两个目的,第一个是预留除URG/ACK/PSH/RST/SYN/FIN/之外的冗余功能位;第二个是为了对齐字节位。
PS: 在明确拥塞通告中使用了CWR和ECE比特(属于标志字段),故自顶向下书中保留字段是4bit。
【标志字段】- 6bit
又称为TCP flag,该字段从左到右分为以下六个字段,指明包的类型。同时用于控制TCP的状态机,同时ACK和SYN与三次握手协议有关,FIN与四次挥手协议有关。
①紧急字段URG - 1bit
当URG=1时,此字段告诉系统此报文段中有紧急数据,应尽快传送。
② 确认字段ACK - 1bit
当ACK=1时,表示确认,且确认号有效;当ACK=0时,确认号字段无效。
③ 推送字段PSH - 1bit
当PSH=1时,则报文段会被尽快地交付给目的方,不会对这样的报文段使用缓存策略。
④ 复位字段RST - 1bit
当RST为1时,表明TCP连接中出现了严重的差错,必须释放连接,然后再重新建立连接。
⑤ 同步字段SYN - 1bit
当SYN=1时,表示发起一个连接请求。
⑥ 终止字段FIN - 1bit
用来释放连接。当FIN=1时,表明此报文段的发送端的数据已发送完成,并要求释放连接。
【窗口字段】- 16bit
此字段用来控制对方发送的数据量(流量控制),单位为字节。
一般TCP连接的其中一端会根据自身的缓存空间大小来确定自己的接收窗口大小,然后告知另一端以确定另一端的发送窗口大小。该字段与TCP的流量控制服务有关。
【校验和字段】- 16bit
与IP协议的检验和不同,TCP的这个校验和是针对首部和数据两部分的。
【紧急指针字段】- 16bit
紧急指针指出在本报文段中的紧急数据的最后一个字节的序号。
【选项字段】- 长度可变
该字段用于发送方与接收方协商最大报文段长度(MSS)时,或在高速网络环境下用作窗口调节因子使用。首部字段中还定义了一个时间戳选项。很多的option字段都是在TCP三次握手过程中做了一次交互。
3.5.3 往返时间的估计与超时
EstimatedRTT = (1 - α) * EstiatedRTT + α * SampleRTT
α推荐值: α = 0.125
EstimatedRTT是一个SampleRTT值的加权平均值,这个加权平均对最近的样本赋予的权值要大于对旧样本赋予的权值,从统计学观点讲,这种平均被称为指数加权移动平均(Exponential Weighted Moving Average, EWMA)。
RTT偏差:用于估算SampleRTT一般会偏离EstimatedRTT的程度。
DevRTT = (1 - β) * DevRTT + β * |SampleRTT - EstiatedRTT| (当前这个SampleRTT与上一个EstiatedRTT的差)
β推荐值: β = 0.25
TCP重传超时间隔:
TimeoutInterval = EstimatedRTT + 4 * DevRTT(全是当前的)
3.5.4 可靠数据传输
TCP发送方有3个与发送和重传有关的主要事件:
- 从上层应用程序接收数据:打包;每个报文段都包含一个序号,序号为该报文段第一个数据字节的字节流编号;交给IP
- 定时器超时:重传,TCP重启定时器
- 收到ACK:TCP采取累积确认,y确认了字节编号在y之前的所有字节都已收到
- 一些有趣的情况:书上案例
- 超时间隔加倍:大多数TCP实现中所做的一个修改,每次定时器超时TCP重传时都会将下一次的超时间隔设置为先前值的两倍,而不是从EstiatedRTT和DevRTT推算出的值,以提供了一个形式受限的拥塞控制。但当另两个事件(即收到上层数据和收到ACK)中任意一个发生,TimeoutInterval由最近的EstiatedRTT值和DevRTT值推算得到。
- 快速重传:一旦收到3个冗余ACK,TCP就执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段。
- 是回退N步还是选择重传:选择确认(selective acknowledgement)。TCP看起来很像我们通常的SR协议,TCP的差错恢复机制也许最后被分类为GBN协议和SR协议的混合体。
3.5.5 流量控制
概率不要搞混:
- 流量控制:以消除发送方使接收方缓存溢出的可能性。
- 拥塞控制:应对TCP发送方因为IP网络的拥塞而被遏制的情况。
流量控制方法:
- TCP通过让发送方维护一个称为接收窗口(receive window)的变量来提供流量控制。接收窗口用于给发送方一指示——该接收方还有多少可用的缓存空间。接收窗口 - rwnd;接收缓存 - RcvBuffer。
- 主机A轮流跟踪两个变量,LastByteSent和LastByteAcked。通过将未确认的数据量控制在rwnd以内,就可以保证主机A不会使主机B的接收缓存溢出。
- 为了解决rwnd为0主机A被阻塞不能再发送数据的问题,TCP要求:当主机B的接收窗口为0时,主机A继续发送只有一个字节数据的报文段。这些报文段将会被接收方确认。最终缓存将开始清空,并且确认报文里将包含一个非0的rwnd值。这样主机A就可以继续发送数据啦,不会因为rwnd为0停发了。
PS:UDP不提供流量控制
3.5.6 TCP连接管理
TCP三次握手与四次挥手:
一种特殊情况:当一台主机接收到一个报文段,其端口号或源IP地址与该主机上进行中的套接字都不匹配:
- 接收到的是TCP报文段:主机向源发送一个特殊重置报文段,该TCP报文段将RST 标志位置为1
- 接收到的是UDP报文段:主机向源发送一个特殊的ICMP数据报
PS:SYN洪泛攻击(SYN flood attack)
服务器为了响应一个收到的SYN,分配并初始化连接变量和缓存。然后服务器发送一个SYNACK进行响应,并等待来自客户的ACK报文段。如果客户不发送ACK来完成该三次握手的第三步,最终(常为一分钟之后)服务器将终止该半开连接并回收资源。
经典的DoS攻击:SYN洪泛攻击,针对TCP连接管理协议。在这种攻击中,攻击者发送大量的TCP SYN报文段,而不完成第三次握手的步骤。随着这种SYN报文段纷至沓来,服务器不断为这些半开连接分配资源(但从未使用),导致服务器的连接资源被消耗殆尽。
防御方式:SYN cookie。简单来说就是服务器不会为初始的SYN分配任何资源,收到合法的ACK服务器才会生成一个具有套接字的全开的连接。
3.6 拥塞控制原理
3.6.1 拥塞原因与代价
-
情况1:两个发送方和一台具有无穷大缓存的路由器
分组的到达速率接近链路容量时,分组经历巨大的排队时延。
-
情况2:两个发送方和一台具有有限缓存的路由器
发送方必须执行重传以补偿因为缓存溢出而丢失的分组。
发送方在遇到大时延时所进行的不必要重传会引起路由器利用其链路带宽来转发不必要的分组副本。
-
情况3:4个发送方和具有有限缓存的多台路由器及多跳路径
当一个分组沿一条路径被丢弃时,每个上游路由器用于转发该分组到丢弃该分组而使用的传输容量最终被浪费掉了。
3.6.2 拥塞控制方法
-
端到端拥塞控制
TCP采用端到端的方法解决拥塞控制
-
网络辅助的拥塞控制
通常有两种方式:直接网络反馈;经由接收方的网络反馈(更通用)
3.7 TCP拥塞控制
拥塞窗口(congestion window, cwnd):限制一个TCP发送方能向网络中发送流量的速率。 |(不是接收窗口) 接收窗口:rwnd
TCP要求:LastByteSent - LastByteAcked ≤ min{cwnd, rwnd}
- 我们将一个TCP发送方的“丢包事件”定义为:要么出现超时,要么收到来自接收方的3个冗余ACK—>cwnd降
- 因为TCP使用确认来触发(或计时)增大它的拥塞窗口长度,TCP被说成是自计时(self-clocking)的。
TCP使5用下列指导性原则:
- 一个丢失的报文段表意味着拥塞,因此当丢失报文段时应当降低TCP发送方的速率
- 一个确认报文段指示该网络正在向接收方交付发送方的报文段,因此,当对先前未确认报文段的确认到达时,能够增加发送方的速率。
- 带宽探测(即综合利用以上一二两点。ACK和丢包事件相当于隐式信号)
TCP拥塞控制算法(TCP congestion control algorithm)
-
慢启动
当一条TCP连接开始时,cwnd的值通常初始置为一个MSS的较小值,这就使得发送速率大约为MSS / RTT。在慢启动状态,cwnd的值以1个MSS开始并且每当传输的报文段首次被确认就增加一个MSS(收到2个则加2个,三个则加4个)。这一过程每过一个RTT,发送速率就会翻倍。因此,TCP起始发送速率慢,但在慢启动阶段以指数增长。
何时结束这种指数增长?三种方法:
- 若存在一个由超时指示的丢包事件,TCP发送方将cwnd设置为1并重新开始慢启动过程。
- 当检测到拥塞时将慢启动阈值ssthresh置为拥塞窗口值的一半。(下次)当cwnd的值等于ssthresh时,结束慢启动并且TCP转移到拥塞避免模式。
- 如果检测到3个冗余ACK,这时TCP执行快速重传并进入快速恢复状态。
-
拥塞避免
一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时值的一半。因此,TCP无法每过一个RTT再将cwnd的值翻番,而是采取一种较为保守的方法,每个RTT只将cwnd的值增加一个MSS。通用的方法是收到一个ACK就增加一个(MSS / cwnd)字节,一个RTT即可增加一个MSS。
何时结束这种线性增长呢?
- 当出现超时时,TCP的拥塞避免算法行为相同。
- TCP对由三个冗余ACK触发的丢包事件的行为,相比于超时指示的丢包,会更加宽容:TCP将cwnd值减半(为了测量结果更好,计及已收到的3个冗余ACK,减半后要加上3个MSS),将ssthresh的值记录为cwnd的值的一半(ssthresh = cwnd / 2, cwnd = ssthresh + 3 · MSS) 。接下来进入快速恢复状态。
-
快速恢复
在快速恢复中,对于引起TCP进入快速恢复状态的缺失报文段,对收到的每个冗余的ACK,cwnd的值增加一个MSS。
最终,当对丢失报文段的一个ACK到达时,TCP进入拥塞避免状态。
如果出现超时事件,则迁移到慢启动状态。
快速恢复是TCP推荐的而非必须的构件。
TCP拥塞控制常常被称为加性增、乘性减拥塞控制方式(Additive-Increase, Multiplicative-Decrease, AIMD)。
3.7.1 公平性
TCP的AIMD算法是公平的。多个竞争的TCP会话:
- 加性增,斜率为1, 吞吐量增加
- 乘性减,吞吐量比例减少
最终会收敛到合理的的状态。
理想化情形中,我们假设仅有TCP连接穿过瓶颈链路,所有的连接具有相同的RTT值,且对于一个主机 - 目的地对而言只有一条TCP连接与之相关联。实践中,这些条件通常得不到满足,客户 - 服务器应用因此能获得非常不平等的链路带宽份额。
从TCP的观点来看,运行在UDP上的应用是不公平的,因为它们不与其他连接合作,也不适时地调整其传输速率。UDP源可能会压制TCP流量。
即使我们有一些办法迫使UDP流量具有公平的行为,但公平性问题仍然没有完全解决,这是因为我们没什么办法阻止基于TCP的应用使用多个并行连接。
3.7.2 明确拥塞通告:网络辅助拥塞控制
- TOS字段中2个bit被网络路由器标记,用于指示是否发生拥塞
- 拥塞指示被传送到接收主机
- 在接收方-到发送方的ACK中,接收方(在IP数据报中看到了拥塞指示)设置ECE bit,指示发送方发生了拥塞