第6章 传输层
传输服务
传输层所处的地位: - 面向信息处理:
应用层 - 面向传输:
传输层、网络层、数据链路层、物理层
- 用户功能:
应用层、传输层 - 网络功能:
网络层、数据链路层、物理层
传输层向应用层提供传输服务,属于面向传输部分的最高层。
同时传输层也是用户功能中的最低层,为应用层屏蔽任何与网络有关的操作。
传输层协议是端到端的。
传输层实现在不同主机上的进程之间的通信,每个应用进程都至少与一个传输层地址(端口)相关联。
向上层提供的服务:面向连接的传输服务和无连接的传输服务。
传输实体之间发送的消息:段 (segment) 或传输协议数据单元 (TPDU)。 - 段(传输层)被包裹在数据包(网络层)中,而数据包被包含在帧(数据链路层)中。
- 当一帧到达时,数据链路层对帧头进行处理,如果帧目标地址与本地传递地址匹配,则把帧的有效载荷字段中的内容传递给网络实体。
网络实体再对数据包头进行类似处理,然后把数据包的有效载荷字段内容向上传递给传输实体。
Berkeley 套接字
套接字 (socket)
Socket 原语 | 含义 |
---|---|
SOCKET | 创建一个新的通信端点 |
BIND | 将一个本地地址关联到一个套接字上 |
LISTEN | 宣布愿意接收连接,给出队列大小 |
ACCEPT | 阻塞调用方,直到有人企图连接上来 |
CONNECT | 主动尝试建立一个连接 |
SEND | 在指定的连接上发送数据 |
RECV | 从指定的连接上接收数据 |
CLOSE | 释放指定连接 |
- 服务器:依次执行
SOCKET
、BIND
、LISTEN
、ACCEPT
。 - 客户端:不需要
BIND
,依次执行SOCKET
、CONNECT
。 - 连接建立后,双方都可以使用
SNED
、RECV
收发数据。 - 数据传输结束后,双方使用
CLOSE
释放连接。
TCP & UDP 套接字编程实现
TCP: - 服务器依次执行 - socket
- bind
-
listen
- accept
-
此时可以接收客户端发来的连接请求 - recv
、send
- closesocket
- 客户端依次执行
socket
connect
- 在服务器执行
accept
之后
- 在服务器执行
send
、recv
closesocket
UDP: - 服务器依次执行 - socket
- bind
-
recvfrom
、sendto
- 在 recvfrom
调用后,服务器保持等待,直到从客户端收到数据报。 - 由于没有事先
connect
,故每次收发数据都需要附上接收/发送地址。
- 客户端依次执行
socket
sendto
、recvfrom
closesocket
传输协议的要素
寻址
当一个应用进程希望与另一个远程应用进程建立连接时,必须指定要连接到哪个应用进程上,即指定对方的传输服务访问点
(TSAP)。
网络层上也有类似端点,称为网络服务访问点
(NSAP),而无连接的网络层不需要提供 NSAP。IP 地址是 NSAP 的实例。
一个全局唯一的传输服务用户由 {主机地址,NSAP,TSAP} 表示,称为端点地址。
在 TCP/IP 中,NSAP 可略去,TSAP 称为端口号。
以 {IP 地址,port,协议类型} 唯一标识传输服务的用户。
{源/目的 IP 地址,源/目的端口号,协议类型} 五元组标识一个数据流。
利用 TSAP 传输消息
主机 2 上的邮件服务进程将自己关联到 TSAP 1522 上,等待入境连接请求的到来。
主机 1 上的应用进程希望发送一个邮件消息,于是把自己关联到 TSAP 1208 上,并发出一个
CONNECT
请求。
该请求指定主机 1 上的 TSAP 1208 作为源,主机 2 上的 TSAP 1522 作为目标。
这个动作在应用进程和服务器之间建立了一个连接。应用进程发送邮件消息。
作为响应,邮件服务器表示它将传递该消息。
传输连接被释放。
TSAP 获取方法
服务进程固定在特定的 TSAP 地址上。
只适用于少数永不改变的关键服务。
采用端口映射器/名字服务器。
用户与端口映射器建立一个连接。
用户通过这个连接发送一条消息指定它想要的服务名字;端口映射器返回相应的 TSAP 地址。
之后用户释放与端口映射器之间的连接,与所需的服务建立一个新连接。当一个新的服务被创建时,它必须向端口映射器注册,把它的服务名字与 TSAP 地址告诉端口映射器。端口映射器将该信息记录到它的内部数据库中。
采用初始连接协议。
每台希望向远程用户提供服务的机器有一个特殊的进程服务器充当不那么频繁使用的服务的代理。
进程服务器在同一时间监听一组端口,等待连接请求的到来。
用户发出一个连接请求,并指定所需服务的 TSAP 地址。如果该 TSAP 地址上没有服务器正在等着,则用户得到一条与进程服务器的连接。
在获取入境请求后,进程服务器派生出被请求的服务器,允许该服务器继承与用户的现有连接。新服务器完成要求的工作,而进程服务器继续回去监听新的连接要求。只适用于服务器可按需创建的场合。
连接
对于面向连接的传输服务,首先要和对端的对等实体建立连接。
传输层连接本质上是在发送端和接收端上为实现可靠船速而维护的一些参数状态。 - 发送/确认序列号的维护,发送/接收窗口的管理,接收端缓存大小等 - 各种计时器
数据传输完毕还需要释放连接,即释放维护连接参数的缓存。
建立连接:三次握手
正常情况:
主机 1 选择一个序号 x,并向主机 2 发送包含该序号的连接请求
CR (seq = x)
。主机 2 应答连接确认
ACK (seq = y, ACK = x)
,其中包含确认号 x 和序列号 y。主机 1 在其发送的第一个数据中采用序列号为 x,并确认主机 2 的序列号 x,即发送
DATA (seq = x, ACK = y)
。
出现旧的重复 CR
:
主机 2 收到旧的
CR (seq = x)
,而主机 1 并不知情。
主机 2 回复ACK (seq = y, ACK = x)
。主机 1 拒绝连接请求,即发送
REJECT (ACK = y)
。
主机 2 放弃连接。
出现旧的重复 CR
和 ACK
:
主机 2 收到旧的
CR (seq = x)
,而主机 1 并不知情。
主机 2 回复ACK (seq = y, ACK = x)
。主机 2 收到旧的
DATA (seq = x, ACK = z)
。
主机 2 根据确认是 z 而不是 y 知道这是一个旧确认包,因此不会建立连接。主机 2 收到 主机 1 发送的
REJECT (ACK = y)
,于是放弃连接。
释放连接
非对称释放:连接的任何一方都可以断开整个连接。
可能会导致数据的丢失。
如:主机 1 发送 DATA,它正确到达主机 2;主机 1 再次发送
DATA,但在到达之前主机 2 发出了
DISCONNECT
;因此连接被释放,但数据丢失了。
对称释放:把连接看作是由两个单独的单项连接,并要求单独释放每一个单项连接。 - 正常情况
主机 1 发送释放连接请求
DR
并启动一个计时器,但它不立即拆除连接,而是等待对方确认。主机 2 收到
DR
后也发回一个DR
,并启动一个计时器。当主机 1 收到主机 2 发送的
DR
后,它会返回一个ACK
段并释放连接。主机 2 收到
ACK
后,它也释放连接。最后一个
ACK
丢失通过主机 2 的计时器来补救。
如果在计时器超时前,主机 2 没有收到ACK
;则计时器超时后,主机 2 无论如何都释放连接;此时主机 1 已经释放了连接。主机 2 发送的
DR
丢失通过主机 1 的定时器来补救。
如果在定时器超时前,主机 1 没有收到来自主机 2 的DR
;则计时器超时后,主机 1 再次发送DR
。
流量控制 & 拥塞控制
流量控制
流量控制:根据接收端的缓存容量来动态地调整发送端的窗口大小,避免一个快速的发送端淹没一个慢速的接收端。
采用滑动窗口协议,但是使用可变大小的动态缓冲区,即一个可变大小的窗口。
- 初始时,发送端根据它期望的需求要求一定数量的缓冲区。
- 接收端根据它的能力尽可能多的缓冲区。
- 每次发送端传输一段,它必须减少分配给它的缓冲区数。
- 当分配给发送端的缓冲区数达到 0 时,完全停止发送。
- 接收端在逆向流量中捎带上单独的确认和缓冲区数。
- TCP 采用这种模式,将缓冲区的分配捎带在头的 Windows size 字段。
为了避免控制段丢失造成的死锁,每台主机应该定期地在每个连接上发送控制段,给出确认和缓冲区状态。
理想带宽分类
功率定义: \[
功率 = \dfrac{负载}{延迟}
\]
功率最初随着提交负载的上升而上升,延迟仍然很小并且基本保持不变;
但随着延迟快速增长,功率达到最大,然后开始下降。
达到最大功率的负载表示了传输实体放置在网络上的有效负载。
最大-最小公平:如果分配给一个流的带宽在不减少分配给另一个流带宽的前提下无法得到进一步增长,那么就不给这个流更多带宽。
拥塞控制:根据网络的承载容量来动态地调整发送端的窗口大小,避免发送端发送的数据超过了网络容量。
拥塞控制时网络层和传输层的共同责任。
一个良好的拥塞控制算法,应迅速收敛到理想的操作点,并跟踪这随时变化的操作点。
调整发送速率
发送速率可能受到两个方面因素的限制: - 在接收端没有足够缓冲区的情况下,进行流量控制。 - 在网络容量不足的情况下,进行拥塞控制。
显示拥塞协议 (XCP)
TCP 采用显示拥塞通知 (ECN):
路由器在经历拥塞的数据包中设置警告比特来警告发送端放慢速率,但它们不告诉源端该减缓多少。
增加或减少速率的方式由控制法则决定。
其中加法递增乘法递减 (AIMD)
法则是达到有效和公平操作点的适当流量规则:
用户加法递增他们的带宽,然后当收到拥塞信号时乘法递减他们的带宽。
Internet 传输协议
UDP
用户数据报协议 (UDP):提供无连接的服务。
头标
UDP 传输的段(数据报)由 8 字节的头和有效载荷字段构成。 - 源端口:2 字节。
目标端口:2 字节。
UDP 长度:2 字节。
包含 8 字节的头和数据两部分的总长度。
最小长度是 8 字节,刚好覆盖 UDP 头。
最大长度是 65515 字节。因为 IP 头的总长度字段为 16 位,最大长度为 65535 字节;而 IP 头最少需要 20 字节。UDP 校验和(可选):2 字节
校验 UDP 头、数据和一个概念性的 IP 伪头。
IPv4 伪头包括:
- 4 字节源地址
- 4 字节目的地址
- 1 字节 0
- 1 字节 UDP 的协议号 17
- 2 字节 UDP 长度(包括头,以字节为单位)
与 IP 头中的头校验和算法类似。
计算校验和时:- 当数据字段的长度为奇数字节时,则在数据字段填充一个额外的 0 字节(因为要以 16 位为单位计算校验和)。
- 当结果为全 0 时,将校验和置为全 1。
- 如果发送端没有计算校验和,则将该字段置为 0。 验算时的步骤也和 IP 头校验和的验算类似,只是求和范围改成上述 UDP 的校验范围。
应用
具有简单高效的特点。
客户-服务器模式应用,如域名系统 (DNS)。
话音、视频等实时多媒体应用。
TCP
传输控制协议 (TCP):在不可靠的互联网络上提供可靠的端到端字节流的传输协议。
服务模型
TCP 服务由发送端和接收端创建套接字来获得。 - 每个套接字有一个套接字编号(地址),该编号由主机的 IP 地址以及一个本地主机的 16 位数值(端口,TCP 的 TSAP 名字)。
一个套接字有可能同时被用于多个连接,即两个或多个连接可能终止于同一个套接字。
每个连接可以用两端的套接字标识符来标识,即
(socket1, socket2)
。
知名端口:1024 以下的端口号被保留,只能用作由特权用户启动的标准服务。 | 端口 | 协议 | 用途 | | ---- | ---- | --- | | 20、21 | FTP | 文件传输 | | 22 | SSH | 远程登录 | | 23 | Talnet | 远程登录 | | 25 | SMTP | 电子邮件 | | 80 | HTTP | 万维网 |
特点
字节流传输,端到端之间不保留消息的边界。
TCP 连接上的每个字节都有独有的 32 位序号,起始编号随机。
全双工,一个连接上传输两个方向的 TCP 数据,各自的编号是独立的。
点到点,不支持组播或广播传输模式。
发送端和接收端的 TCP 实体以段的形式交换数据。
TCP 段由一个固定的 20 字节的头(加上可选的部分)以及随后的 0 个或多个数据字节构成。
有两个因素限制了段的长度:- 包括 TCP 头在被的每个段,必须适合 IP 的 65515 个字节有效载荷。
- 每个网络有最大传输单元 (MTU)。
使用具有动态窗口大小的滑动窗口协议。
发送端每次发送的数据量由发送窗口大小决定,而不是以应用层递交的消息为单位;
接收端每次向上层递交的数据量由落在接收窗口中并且有序的数据决定。可靠,具有流量控制和拥塞控制。
- 发送端:超时重传。
- 接收端:发送确认,缓存处理乱序。
头
每个段的起始部分是一个固定格式的 20 字节头。
固定的头部之后可能有头的选项。
选项之后是最多可达 \(65535 - 20 - 20 =
65495\) 个字节的数据。(其中第一个 20 指 IP 头,第二个 20 指 TCP
头。
没有任何数据的 TCP 段也是合法的,通常被用作确认和控制消息。
源端口 (Source port):16 位
目的端口 (Destination port):16 位
序号 (Sequence number):32 位
标识本报文段的第一个字节在数据流中的位置。确认号 (Acknowledgment number):32 位
标识本段报文段的发送方下一个期待接收的字节编号。
是累计确认,用一个数字概括了接收到的所有数据。TCP 头长度 (TCP header length):4 位
指明 TCP 头的长度,以 32 位字为单位。保留 (Reserved):4 位
置 0。标志位:共 8 位,每个标志位 1 位
CWR & ECE:用于显示拥塞通知。
当 TCP 接收端收到了来自网络的拥塞指示后,就设置 ECE 以便给 TCP 发送端发 ENC-Echo 信号,告诉发送端放慢发送速率。
TCP 发送端设置 CWR,给接收端发 CWR 信号,告诉接收端已放慢发送速率,无需继续发送 ENC-Echo 信号。URG:置 1 时,紧急指针有效。
ACK:置 1 时,指示确认段有效。
PSH:置 1时,指出这是被推送的数据。
请求接收端一旦收到数据后立即将数据递交给应用程序。RST:连接复位。
用于拒绝不存在的端口连接请求、中止异常连接等。SYN:用于建立连接过程。
在连接请求中,(SYN = 1, ACK = 0)
,表明该段没有使用捎带确认字段。
在连接应答中,捎带了一个确认,因此(SYN = 1, ACK = 1)
。FIN:用于释放连接。
窗口大小 (Window size):16 位。
指定在已确认的字节之后还可以接收的字节数。
窗口大小为 0 标识希望对端不再发送数据,用于流量控制。校验和 (Checksum):16 位。
与UDP 的校验和类似,只是 IP 伪头中的协议号为 TCP 的协议号 6。紧急指针 (Urgent pointer):16 位
指向紧急数据的结束位置(相对于序列号的偏移量)。选项 (Options):0 或 32 位的倍数,不足部分用 0 填充 对于最长的 TCP 头,选项可以扩展到 40 个字节。
连接建立
使用三次握手法来建立连接。
通过三次握手,主机 1 和主机 2
之间完成初始序列、窗口大小(扩大因子)、最大数据段尺寸等参数的协商,并且分配连接所需要端点资源。
正常情况: - 主机 1 发送请求连接数据段(SYN 数据段):
SYN 标志置 1,ACK 标志置 0,选择初始序列号 SEQ = x
。
即发送 SYN (SEQ = x)
。
SYN 数据段不携带任何数据,但要消耗一个字节的序号。
主机 2 响应连接确认数据段(SYN+ACK 数据段):
SYN 标志置 1,ACK 标志置 1,选择初始序列号SEQ = y
;并对 x 进行确认,设置ACK = x + 1
。
即发送SYN (SEQ = y, ACK = x +1)
。
SNY+ACK 数据段也不携带数据,但消耗一个字节的序号。主机 1 响应连接确认数据段:
ACK 标志置 1,序列号SEQ = x + 1
;并对 y 进行确认,设置ACK = y + 1
。
即发送(SEQ = x + 1, ACK = y + 1)
。
ACK 数据段如果不携带数据就不消耗序号。
两端同时建立连接: - 主机 1 发送 SYN (SEQ = x)
,同时主机
2 发送 SYN (SEQ = y)
。
主机 2 收到消息后发送
SYN (SEQ = y, ACK = x + 1)
,主机 1 收到消息后发送SYN (SEQ = x, ACK = y + 1)
。实际上只建立了一个连接,并不是两个。
连接建立失败: - 主机 1 请求连接数据段(SYN 数据段)
- 主机 2 的 TCP 实体检查是否有进程已经在目标端口字段指定的端口上执行了
LISTEN。
如果没有,则它发送一个设置了 RST 的应答报文,拒绝主机 1 的连接请求。
连接释放
TCP 的全双工连接可以看成一个双单工的连接,每个单工连接都独立地释放。
通常情况下,释放一个连接需要 4 个 TCP 段:
通信双方都必须向对方发送 FIN = 1
的 TCP
段并得到对方的应答,即每个方向上一个 FIN
和一个
ACK
。
然而,第一个 ACK
和第二个 FIN
有可能被组合在同一个段中,从而将所需段总数降低到 3 个。
主机发送 FIN
数据段后,就不再向对方发送数据。
为防止半连接,必须使用定时器计时,对 FIN
数据段的应答在两个最大分组生命期内未到达,则发送端释放连接,对方也会因为超时释放。
连接管理模型
建立连接和释放连接所需的步骤可以用一个具有 11
个状态的有限状态机来表示。
每一种状态中,都存在特定的合法事件。当一个合法事件发生时,可能需要采取某个动作;当发生其他事件时,则报告一个错误。
状态 | 描述 |
---|---|
CLOSED | 没有活跃的连接或者挂起 |
LISTEN | 服务器等待入境呼叫 |
SYN RCVD | 到达一个连接请求,等待 ACK |
SYN SENT | 应用已经启动了打开一个连接 |
ESTABLISHED | 正常的数据传输状态 |
FIN WAIT1 | 应用没有数据要发了 |
FIN WAIT2 | 另一端同意释放连接 |
TIME WAIT | 等待所有数据包寿终正寝 |
CLOSING | 两端同时试图关闭连接 |
CLOSE WAIT | 另一端已经发起关闭连接 |
LAST ACK | 等待所有数据包寿终正寝 |
建立连接过程: - 客户端和服务器都是从
CLOSED
状态开始。
- 客户端:应用程序发出 CONNECT 请求,本地的 TCP
实体创建一条连接记录,并将它标记为
SYN SENT
状态,然后发送一个 SYN 段。- 在
SYN SENT
状态中,客户端等待服务器的 SYN+ACK 字段
- 在
- 服务器:收到来自客户端的 SYN 后,创建一条连接记录,将其标记为
SYN RCVD
状态,然后发送一个确认(即 SYN+ACK 段)。- 在
SYN RCVD
状态中,服务器等待来自客户端的最后一个 ACK。
- 在
- 客户端:收到来自服务器的 SYN+ACK 段后,发出三次握手中的最后一个 ACK
段,然后切换到
ESTABLISHED
状态。- 在
ESTABLISHED
状态中,连接已建立,双方可以进行数据传输。
- 在
- 服务器:收到来自客户端的 ACK 段后,进入
ESTABLISHED
状态,连接建立完成。
释放连接过程: - 客户端和服务器都是从
ESTABLISHED
状态开始。
- 客户端:应用结束,执行 CLOSE 原语。本地的 TCP 实体发送一个 FIN
段,将当前状态设置为
FIN WAIT1
,等待对应的 ACK。- 在
FIN WAIT1
状态下,客户端等待服务器的 ACK 和 FIN。
- 在
- 服务器:收到客户端的 FIN 段,发送 ACK 段,并进入
CLOSE WAIT
状态。- 在
CLOSE WAIT
状态下,服务器等待应用程序的CLOSE
操作,同时允许继续接收来自客户端的数据。
- 在
- 客户端:收到 ACK,状态迁移到
FIN WAIT2
,并且连接的一个方向被关闭。- 在
FIN WAIT2
状态下,客户端等待来自服务器的 FIN。
- 在
- 服务器:执行
CLOSE
,TCP 实体向客户端发送一个 FIN 段,并进入到LAST ACK
状态。- 在
LAST ACK
状态下,服务器等待来自客户端的 ACK。
- 在
- 客户端:收到来自服务器的 FIN 段,发送 ACK 段,进入
TIME WAIT
状态。- 在
TIME WAIT
状态下,TCP 要等待一段长度为最大数据包生存期两倍的时间。
以便确保在该阶段收到所有可能延迟或重复的数据包,并正确处理,避免对后续连接产生干扰。
- 计时器超时后,TCP 删除该连接记录,并进入
CLOSED
状态。
- 在
- 服务器:收到来自客户端的 ACK
段,同样等待长度为最大数据包生存期两倍的时间后,释放该连接并删除相应的连接记录,进入
CLOSED
状态。
滑动窗口
TCP 采用可变大小的动态缓冲区,缓冲区的大小由数据包头中的 Window size 指定。
特点: - 可靠有序的传输:使用发送缓存和接收缓存,只有被确认窗口才滑动。
流量控制:接收端根据可用剩余缓冲区来指定窗口大小。
采用延迟确认:将确认和窗口更新消息延迟发送,希望通过捎带技术节约带宽。
eg. 假设接收端有一个 4096 字节的缓冲区。 - 发送端传送一个 2048
字节的数据段,SEQ = 0
。
接收端缓冲区还剩下 2048 字节,发送确认并指定窗口大小,即发送
ACK = 2048, WIN = 2048
。发送端再次传输另一个 2048 字节的数据段,
SEQ = 2048
。接收端发回确认并宣告窗口大小变为 0,即发送
ACK = 4096, WIN = 0
。- 此时发送端被阻塞,停止发送数据。
- 但是紧急数据仍可以发送。
- 发送端可以发送一个 1 字节的段,以便接收端重新宣告下一个期望字节和窗口大小,称为窗口探测。
低窗口综合征/傻瓜窗口症状:
- 当数据以大块形式被传递给发送端 TCP
实体,但接收端的交互式应用每次仅读取 1 个字节。
于是当接收端缓冲区满时,接收端的应用进程每读取一个字节,发送窗口大小为 1
字节的修正信息。
发送方发送 1 字节数据,窗口很快又满了。
- 解决方案:
- 禁止接收方发送 1 个字节大小的窗口修正信息,而是等到有了一定数量的可用空间后再通知对方。
- 发送方不发送太小的数据段。
计时器管理
TCP 维护多个定时器:包括建立、释放连接,确认、重传,滑动窗口三个方面。
连接建立,连接释放: - 连接建立定时器 (Connection Establishment Timer)
当 SYN 数据段发出时,建立连接计时器就开始计时,如果在一定时间内未收到响应,则连接建立失败。
保持存活定时器 (Keep-Alive Timer)
当一个连接长时间闲置时,该定时器会超时,从而使一方去检测另一方是否存在。
如果它没有得到响应,则种植终止该连接。闲置定时器 (Quiet Timer)
设置该定时器以防止刚刚断开连接的端口号被立即重新使用。
即TIME WAIT
阶段所使用的定时器。
确认、重传: - 重传定时器 (RTO)
当 TCP 实体发送一个段时,它同时启动一个重传计时器。
如果在该计时器超时前该段被确认,则计时器被停止;如果在确认到来前计时器超时,则该段被重传,并且该计时器被重新启动。
使用一个动态算法,根据网络性能的连续测量情况,不断地调整超时间隔。 - RTT(往返时间):代表到达接收方往返时间的当前最佳估计值。
发送一个段后,如果在计时器超时前确认返回,则 TCP 测量这次确认所花的时间 R。
第一次测量时:$RTT = R$。
之后每次测量:根据以下**指数加权移动平均**公式更新 RTT 的值。
$$
RTT = \alpha RTT + (1 - \alpha) R
$$
其中 $\alpha$ 是平滑因子,一般为 $\dfrac{7}{8}$
RTTVAR(往返时间变化)
同样的,发送一个段后,如果在计时器超时前确认返回,则 TCP 测量这次确认所花的时间 R。
第一次测量时:\(RTTVAR = R / 2\)。
之后每次测量,根据以下公式更新:
\[ RTTVAR = \beta RTTVAR + (1 - \beta) |RTT - R| \]
其中 \(\beta\) 一般为 \(\dfrac{3}{4}\)RTO(重传超时值)
\[ RTO = RTT + 4 \times RTTVAR \]
Karn 算法:对已经重传的数据段无需修改 RTT 和
RTTVAR,而是在每次传输失败时将超时时间 RTO
加倍,直到该数据段被成功传输。
因为发送端无法区分 ACK 是对哪次发送的 TCP
数据段的确认,所以重发数据段后收到的 ACK 不会更新 RTT 值。
ACK 延时定时器 (Delayed ACK Timer)
当 TCP 实体收到数据段时,它启动 ACK 延时定时器。
在定时器超时前如果有数据段要发送,则在数据段中携带确认信息,并暂停该定时器。
如果定时器超时时还没有数据段要发送,则单独发送一条确认消息。
滑动窗口: - 持续定时器 (The Persistence Timer)
防止死锁情况:
为了让发送端暂停发送数据,接收端发送一个窗口大小为 0
的确认。之后,接收端又发送了更新了窗口大小的数据段。
如果该数据段丢失,则双方均处于等待。
发送端在收到接收端发来一个窗口大小为 0
的数据段时,启动持续定时器。
如果定时器超时时还没有收到对方修改篡改看大小的数据段,则发送端发送一个 1
字节的探测数据段。
对该探测数据段的响应包含了窗口大小,如果仍为
0,则定时器清零,重新开始计时;否则则可以发送数据。
拥塞控制
每个 TCP 连接需要维护两个窗口: - 接收窗口
(rwnd):接收端根据目前可用接收缓存大小所许诺的最新窗口值。
在滑动窗口中有所涉及。
- 拥塞窗口
(cwnd):发送端根据自己估计的网络拥塞程度而设置的窗口值。
窗口大小时任何时候发送端可以向网络发送的字节数。
TCP 根据
发送端发送窗口的上限值应该去接收窗口和拥塞窗口中的较小值。
TCP 拥塞控制算法过程: - 慢速启动 - 当一个连接初始化时,将拥塞窗口置为一个最大数据段长度 (MSS),并设置慢启动阈值 (ssthresh)。
发送端的发送窗口不能超过 rwnd 和 cwnd 中的最小值,在这里假定接收端不进行流量控制。
发送端收到一个确定,就将拥塞窗口加 1 (MSS)。
即每经过一个 RTT,拥塞窗口就增加为前一个 RTT 时的两倍,即拥塞窗口从 1 开始按指数规律增长。拥塞控制
采用加法递增乘法递减 (AIMD)
当拥塞窗口增长到 ssthresh 时,TCP 就从慢速启动切换到加法递增。
即每轮将拥塞窗口加 1,使拥塞窗口按线性规律增大。如果出现数据包丢失(RTO 超时),就将当前拥塞窗口值减半,作为新的 ssthresh(乘法递减),同时将拥塞串口变为 1。
TCP 重新开始慢速启动过程。
快速重传
发送端只要连续收到三个重复的 ACK 即可断定有数据包丢失。
因为丢失数据包后续的数据包触发接收端返回的确认携带相同的确认号(期望收到的数据包没变)。上述情况下立即重传丢失的数据包而不必等待 RTO 超时。
同时像超时情况下一样处理拥塞窗口(乘法递减)。
快速恢复 (Reno 算法,是 TCP 推荐算法,不是必须)
当发送端收到连续三个重复 ACK 时,重新设置 ssthresh 为当前拥塞窗口的一半。
但拥塞窗口不是设置为 1,而是设置成 ssthresh + 3。如果收到了重复的 ACK(说明又有丢失数据包之后的数据包到达了接收端,网络里的数据包减少了一个),则每收到一个重复 ACK,就将拥塞窗口加 1。
若收到了重传数据包的 ACK(新的 ACK),则将拥塞窗口设置到 ssthresh,再次执行 AIMD。
TCP 的传输速率: - 设 TCP 的发送窗口为 w,往返时间为 RTT,则 TCP
瞬时最大发送速率为 \(\dfrac{w}{RTT}\)。
- 由于 w 是变化的,设丢包或收到三个重复 ACK 时的窗口为 W 且 W 不变,则 w
在 W/2 到 W 之间变化。
忽略慢速启动阶段,则一条连接的平均吞吐量为 \(0.75 \times \dfrac{W}{RTT}\)。