1. 介绍
随着网络带宽时延产品
(BDP)
的增加,通常的
TCP
协议开始变的低效。这是因为它的
AIMD
(
additive increase multiplicative decrease
)算法彻底减少了
TCP
拥塞窗口,但不能快速的恢复可用带宽。理论上的流量分析表明
TCP
在
BDP
增加到很高的时候比较容易受包损失攻击。
另外,继承自
TCP
拥塞控制的不公平的
RTT
也成为在分布式数据密集程序中的严重问题。拥有不同
RTT
的并发
TCP
流将不公平地分享带宽。尽管在小的
BDP
网络中使用通常的
TCP
实现来相对平等的共享带宽,但在拥有大量
BDP
的网络中,通常的基于
TCP
的程序就必须承受严重的不公平的问题。这个
RTT
基于的算法严重的限制了其在广域网分布式计算的效率,例如:
internet
上的网格计算。
一直到今天,对标准的
TCP
的提高一直都不能在高
BDP
环境中效率和公平性方面达到满意的程度(特别是基于
RTT
的问题)。例如:
TCP
的修改,
RFC1423
(高性能扩展),
RFC2018
(
SACK
)、
RFC2582
(
New Reno
)、
RFC2883
(
D-SACK
)、和
RFC2988
(
RTO
计算)都或多或少的提高了点效率,但最根本的
AIMD
算法没有解决。
HS TCP
(
RFC 3649
)通过根本上改变
TCP
拥塞控制算法来在高
BDP
网络中获得高带宽利用率,但公平性问题仍然存在。
考虑到上面的背景,需要一种在高
BDP
网络支持高性能数据传输的传输协议。我们推荐一个应用程序级别的传输协议,叫
UDT
或基于
UDP
的数据传输协议并拥有用塞控制算法。
本文描述两个正交的部分,
UDP
协议和
UDT
拥塞控制算法。一个应用层级别的协议,位于
UDP
之上,使用其他的拥塞算法,然而这些本文中描述的算法也可以在其他协议中实现,例如:
TCP
。
一个协议的参考实现叫
[UDT]
;详细的拥塞控制算法的性能分析在
[GHG04]
中可以找到。
2. 设计目标
UDT
主要用在小数量的
bulk
源共享富裕带宽的情况下,最典型的例子就是建立在光纤广域网上的网格计算,一些研究所在这样的网络上运行他们的分布式的数据密集程序,例如,远程访问仪器、分布式数据挖掘和高分辨率的多媒体流。
UDT
的主要目标是效率、公平、稳定。单个的或少量的
UDT
流应该
利用所有高速连接提供的可用带宽,即使带宽变化的很剧烈。同时,所有并发的流必须公平地共享带宽,不依赖于不同的带宽瓶劲、起始时间、
RTT
。稳定性要求包发送速率应该一直会聚可用带宽非常快,并且必须避免拥塞碰撞。
UDT
并不是在瓶劲带宽相对较小的和大量多元短文件流的情况下用来取代
TCP
的。
UDT
主要作为
TCP
的朋友,和
TCP
并存,
UDT
分配的带宽不应该超过根据
MAX-MIN
规则的最大最小公平共享原则。(备注,最大最小规则允许
UDT
在高
BDP
连接下分配
TCP
不能使用的可用带宽)。我们
3. 协议说明
3.1.概述
UDT
是双工的,每个
UDT
实体有两个部分:发送和接收。发送者根据流量控制和速率控制来发送(和重传)应用程序数据。接收者接收数据包和控制包,并根据接收到的包发送控制包。发送和接收程序共享同一个
UDP
端口来发送和接收。
接收者也负责触发和处理所有的控制事件,包括拥塞控制和可靠性控制和他们的相对机制,例如
RTT
估计、带宽估计、应答和重传。
UDT
总是试着将应用层数据打包成固定的大小,除非数据不够这么大。和
TCP
相似的是,这个固定的包大小叫做
MSS
(最大包大小)。由于期望
UDT
用来传输大块数据流,我们假定只有很小的一部分不规则的大小的包在
UDT session
中。
MSS
可以通过应用程序来安装,
MTU
是其最优值(包括所有包头)。
UDT
拥塞控制算法将速率控制和窗口(流量控制)合并起来,前者调整包的发送周期,后者限制最大的位被应答的包。在速率控制中使用的参数通过带宽估计技术来更新,它继承来自基于接收的包方法。同时,速率控制周期是估计
RTT
的常量,流控制参数依赖于对方的数据到达速度,另外接收端释放的缓冲区的大小。
3.2.包结构
UDT
有两种包:数据包和控制包。他们通过包头的第一位来区分(标志位)。如果是
0
,表示是数据包,
1
表示是控制包。
3.2.1. 数据包
数据包结构如下显示:
0 1 3 4
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
0
|
包序号
|
应用数据
|
包序号是
UDT
数据包头中唯一的内容。它是一个无符号整数,使用标志位后的
31
位,
UDT
使用包基础的需要,例如,每个非重传的包都增加序号
1
。序号在到达最大值
2^31-1
的时候覆盖。紧跟在这些数据后面的是应用程序数据。
3.2.2. 控制包
控制包结构如下:
0 1 3 4
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1
|
类型
|
保留
|
ACK
序号
|
控制信息字段
|
有
6
种类型的控制包在
UDT
中,
bit1-3
表示这些信息。前
32
位在包头中必须存在。控制信息字段包括
0
(例如,它不存在)或者多个
32
位无符号整数,这由包类型决定。
UDT
使用应答子序号的方法。每个
ACK/ACK2
包有一个无符号的
16
位序号,它独立于数据包需要。它使用位
16-31
。应答需要从
0
到(
2^16-1
)。位
16-31
在其他控制包中没有定义。
类型
|
说明
|
控制信息
|
000
|
协议连接握手
|
1
.
32
位
UDT
版本
2
.
32
位
内部顺序号
3
.
32
位
MSS
(字节)
4
.
32
位
最大流量窗口大小(字节)
|
001
|
保活
|
没有
|
010
|
应答,位
16-31
是应答序号
|
1
.
32
位包序号,先前接收到的包序号
2
.
32
位,
RTT
(微秒)
3
.
32
位,
RTT
变量或者
RTTVar (
微秒
)
4
.
32
位,流量窗口大小(包的数量)
5
.
32
位,连接容量估计(每秒包的数量)
|
011
|
Negative
应答(
NAK
)
|
丢失信息的
32
位整数数组,见
3.9
节
|
100
|
保留
|
这种类型的控制信息保留作为拥塞警告使用,从接收到发送端。一个拥塞警告能被
ECN
或包延迟增加趋势的度量方法触发。
|
101
|
关闭
|
|
110
|
应答一个应答(
ACK2
)
|
16-31
位,应答序号。
|
111
|
4-15
的解释
|
保留将来使用
|
注意,对于数据和控制包来说,可以从
UDP
协议头中得到实际的包大小。包大小信息能被用来得到有效的数据负载和
NAK
包中的控制信息字段大小。
3.3.定时器
UDT
在接收端使用
4
个定时器来触发不同的周期事件,包括速率控制、应答、丢失报告(
negative
应答)和重传
/
连接维护。
UDT
中的定时器使用系统时间作为源。
UDT
接收端主动查询系统时间来检查一个定时器是否过期。对于某个定时器
T
来说,其拥有周期
TP
,将定变量
t
用来记录最近
T
被设置或复位的时间。如果
T
在系统时间
t0
(
t= t0
)被复位,那么任何
t1
(
t1-t>=TP
)是
T
过期的条件。
四个定时器是:
RC
定时器、
ACK
定时器、
NAK
定时器、
EXP
定时器。他们的周期分别是:
RCTP
、
ATP
、
NTP
、
ETP
。
RC
定时器用来触发周期性的速率控制。
ACK
定时器用来触发周期性的有选择的应答(应答包)。
RCTP
和
ATP
是常量值,值为:
RCTP=ATP=0.01
秒。
NAK
被用来触发
negative
应答(
NAK
包)。重传定时器被用来触发一个数据包的重传和维护连接状态。他们周期依赖于对于
RTT
的估计。
ETP
值也依赖于连续
EXP
时间溢出的次数。推荐的
RTT
初始值是
0.1
秒,而
NTP
和
ETP
的初始值是:
NTP=3*RTT
,
ETP=3*RTT+ATP
。
在每次
bounded UDP
接收操作(如果收到一个
UDP
包,一些额外的必须的数据处理时间)时查询系统时间来检查四个定时器是否已经过期。推荐的周期粒度是微秒。
UDP
接收时间溢出值是实现的一个选择,这依赖于循环查询的负担和事件周期精确度之间的权衡。
速率控制事件更新包发送周期,
UDT
发送端使用
STP
来安排数据包的发送。假定一个在时间
t0
被发送,那么下一次包发送时间是(
t0+ STP
)。换句话说,如果前面的包发送花费了
t’
时间,发送端将等待(
STP-t’
)来发送下一个数据包(如果
STP-t’ <0
,就不需要等待了)。这个等待间隔需要一个高精确度的实现,推荐使用
CPU
时钟周期粒度。
3.4.发送端算法
3.4.1. 数据结构和变量
A.
SND PKT
历史窗口:一个循环数组记录每个数据包的开始时间
B.
发送端丢失链表:发送段丢失列表是一个连接链表,用来存储被接收方
NAK
包中返回的丢失包序号。这些数字以增加的顺序存储。
3.4.2. 数据发送算法
A.
如果发送端的丢失链表是非空的,重传第一个在
list
中的包,并删除该成员,到
5
。
B.
等待有应用程序数据需要发送
C.
如果未应答的包数量超过了两量窗口的大小,转到
1
。如果不是包装一个新的包并发送它。
D.
如果当前包的序号是
16n
,
n
是一个整数,转第
2
步。
E.
在
SND PKT
历史窗口中记录包的发送时间
F.
如果这是自上次发送速率降低之后的第一个包,等外
SYN
时间。
G.
等外(
STP
–
t
)时间,
t
是第
1
到第
4
步之间的总时间,然后转到
1
。
3.5.接收端算法
3.5.1. 数据结构和变量
A.
接收端丢失链表:是一个
duple
连接链表,元素的值包括:丢失数据包的序号、最近丢失包的反馈时间和包已经被反馈的次数。值以包序号增序的方式存储。
B.
应答历史窗口:每个发送
ACK
的和时间一个循环数组;由于其循环的特性,意味着如果数组中没有更多空间的时候新的值将覆盖老的值。
C.
RCV PKT
历史窗口:一个用来记录每个包到达时间的循环数组。
D.
对包窗口:一个用来记录每个探测包对之间的时间间隔。
E.
LRSN
:一个用来记录最大接收数据包需要的变量。
LRSN
被初始化为初始序号减
1
。
3.5.2. 数据接收算法
A.
查询系统时间来检查
RC
、
ACK
、
NAK
、或
EXP
定时器是否过期。如果任一定时器过期,处理事件(本节下面介绍)并复位过期的定时器。
B.
启动一个时间
bounded UDP
接收。如果每个包到,转
1
。
C.
设置
exp-count
为
1
,并更新
ETP
为:
ETP=RTT+4*RTTVar + ATP
。
D.
如果所有的发送数据包已经被应答,复位
EXP
时间变量。
E.
检查包头的标志位。如果是一个控制包,根据类型处理它,然后转
1
。
F.
如果当前数据包的需要是
16n+1
,
n
是一个整数,记录当前包和上个在对包窗口中数据包的时间间隔。
G.
在
PKT
历史窗口中记录包到达时间
H.
如果当前数据包的序号大于
LRSN+1
,将所有在(但不包括)这两个值之间的序号放入接收丢失链表,并在一个
NAK
包中将这些序号发送给发送端。如果序号小于
LRSN
,从接收丢失链表中删除它。
I.
更新
LRSN
,转
1
。
3.5.3. RC定时器到
通过速率控制算法来更新
STP
(见
3.6
节)。
过程如下:
A.
按照下面的原则查找接收端所接收到的所有包之前的序号:如果接收者丢失链表是空的,
ACK
号码是
LRSN+1
,否则是在接收丢失队列中的最小序号。
B.
如果应答号不大于曾经被
ACK2
应答的最大应答号,或等于上次应答的应答号并且两次应答之间的时间间隔小于
RTT+4*RTTVar
,停止(不发送应答)。
C.
分配这个应答一个唯一增加的
ACK
序列号,推荐采用
ACK
序列号按步骤
1
增加,并且重叠在达到最大值之后。
D.
根据下面的算法来计算包的抵达速度:使用
PKT
历史窗口中的值计算最近
16
个包抵达间隔(
AI
)中值。在这
16
个值中,删除那些大于
AI*8
或小于
AI*8
的包,如果最后剩余
8
个值,计算他们的平均值
(AI
’
)
,包抵达速度是
1/AI
’
(每秒包的数量),否则是
0
。
E.
根据
3.7
节中的内容为每端(
W
)计算流量窗口。然后计算有效的流量窗口大小为:最大(
W
,可用接收方缓冲大小),
2
)。
F.
根据下面的算法来计算连接容量估计。如果流量控制快启动阶段(
3.7
)一直继续,返回
0
,否则计算最近
16
个对包间隔(
PI
),这些值在对包窗口中,那么连接容量就是
1/PI
(每秒包的数量)。
G.
打包应答序列号,应答号,
RTT
,
RTT
变量,有效的流量窗口大小并估计连接,将他们放入
ACK
包中,然后发送出去。
H.
记录
ACK
序列号,应答号和这个应答的开始时间,并放入历史窗口中。
3.5.4. 处理NAK定时器到时
Ø
查找接受方的丢失链表,找到所有上次反馈时间是(
k*
(
RTT+4*RTTVar )
)前的包,
k
当前这个包的反馈次数加
1
,如果没有反馈丢失,停止。
Ø
压缩第一步中得到的序号(见
3.9
),然后在一个
NAK
包中发送他们到发送方。
Ø
如果不是停止流量控制快启动阶段。
3.5.5. 处理EXP定时器
A.
如果发送端的丢失链表不是空的,停止
B.
将所有未应答的包放到发送端的丢失链表中
C.
如果
(exp-count>16)
并且自上次从对方接收到一个包以来的总时间超过
3
秒,或者这个时间已经超过
3
分钟了,这被认为是连接已经断开,关闭
UDT
连接。
D.
如果没有数据,也就没有应答,发送一个保活包给对端,否则将所有未应答包的序号放入发送丢失列表中。
E.
更新
exp-count
为:
exp-count
= exp-count+1
F.
更新
ETP
为:
ETP=exp-count*
(
RTT+4*RTTVar
)
+ATP
。
3.5.6. 收到应答包
A.
更新最大的应答序号
B.
更新
RTT
和
RTTVar
为:
RTT = rtt
,
RTTVar = rv
;
rtt
和
rv
是
ACK
包中的
RTT
和
RTTVar
值。
C.
更新
NTP
和
ETP
为:
NTP=RTT+4*RTTVar
;
ETP=exp-count*
(
RTT+4*RTTVar
)
+ATP
。
D.
更新连接容量估计:
B=
(
B*7+b
)
/8
,
b
是
ACK
包带的值。
E.
更新流量窗口大小为
ACK
中的值。
F.
发送
ACK2
包,并设置与
ACK
序号相同的应答号到对端
G.
复位
EXP
定时器
3.5.7. 当收到NAK包的时候
A.
将所有
NAK
包中带的序号放入发送方的丢失列表中
B.
通过速率控制来更新
STP
(见
3.6
)
C.
复位
EXP
定时器
3.5.8. 当收到ACK2包
Ø
在
ACK
历史窗口中根据接收到的
ACK2
序列号查找行营的
ACK
包。
Ø
更新曾经被应答的最大应答号
Ø
根据
ACK2
的到达时间和
ACK
离开时间计算新的
rtt
值,并且更新
RTT
和
RTTVar
值为:
RTTVar = (RTTVar *3 +abs(rtt-RTT)/4
RTT = (RTT *7+rtt)/8
RTT
和
RTTVar
的初始值是
0.1
秒和
0.05
秒。
Ø
更新
NTP
和
ETP
为:
NTP = RTT
;
ETP = (exp-count +1)* RTT+ATP
3.5.9. 当收到保活包的时候
什么也不做
3.5.10. 当收到连接握手和关闭包的时候
见
3.8
节
3.6.速度控制算法
3.6.1. 速率控制快启动
STP
被初始为最小的时间精度(
1
个
CPU
周期或
1
毫秒)。这是在快启动阶段,一般收到一个
ACK
包其携带的估计带宽大于
0
这个阶段就停止了。包的发送周期被设置为
1/W
,
W
是
ACK
携带的流量窗口的大小。
快启动
阶段仅仅在开始一个
UDT
连接的时候发生,且不会在
UDT
连接的以后再出现。在快启动阶段之后,下面的算法就要工作了。
3.6.2. 当RC定时器时间到
1.
如果在上一个
RCTP
时间内,没有收到一个
ACK
,停止
2.
计算在上个
RCTP
时间内的丢失率,计算方法是根据总共发送的包与
NAK
反馈中总共丢失包的数量。如果丢失率大于
0.1%
,停止。
3.
下个
RCTP
时间内发送包的增加数量如下计算:
(inc)
If (B<=C) inc = 1/MSS
Else inc = max (10^(ceil(log10((B-C)*MSS*8)))*Beta/MSS,1/MSS)
B
是连接容量估计,
C
是当前的发送速度。两个都计算为每秒多少个包。
MSS
是以字节计算的;
Beta
是值为
0.0000015
的常量。
4.
更新
STP
:
STP=
(
STP*RCTP
)
/
(
STP*inc + RCTP
)
5.
计算真正的数据发送周期(
rsp
),从
SND PKT
历史窗口中得到,如果(
STP<0.5 *rsp
)设置
STP
为(
0.5 * rsp
)。
6.
如果(
STP<1.0
),设置
STP
为
1.0
。
3.6.3. 当收到NAK包时
3.6.3.1. 数据结构和变量
1.
LSD
:自上次速率降低后发送的最大序号
2.
NumNAK
:自上次
LSD
更新以后的
NAK
数量
3.
AvgNAK
:当最大序号大于
LSD
时两次事件之间的
NAK
移动的平均数。
4.
DR
:在
1
到
AvgNAK
之间的随机平均数。
3.6.3.2. 算法
1.
如果
NAK
中最大的丢失序列号大于
LSD
:
增加
STP
为:
STP=STP*
(
1+1/8
)
更新
AvgNAK
为:
AvgNAK =
(
AvgNAK *7 +NumNAK
)
/8
更新
DR
复位
NumNAK = 0
记录
LSD
2.
否则,增加
NumNAK
按照
1
个步骤增加;如果
NumNAK % DR = 0
;增加
STP
为:
STP=STP*
(
1+1/8
);记录
LSD
。
3.7.流量控制算法
流量控制窗口大小(
W
)初始值是
16
。
3.7.1. 当ACK定时器到的时候
1.
流量控制快启动:如果没有
NAK
产生或者
W
没有到达或超过
15
个包,并且
AS>0
,流量窗口大小更新为应答包的总数量。
2.
否则,如果(
AS>0
),
W
更新为:(
AS
是包的到达速度)
W= ceil (W *0.875+AS* (RTT +ATP) *0.125)
3.
限制
W
到对方最大流量窗口大小。
3.8.连接建立和关闭
一个
UDT
实体首先作为一个
SERVER
启动,当一个客户端需要连接的时候其发送握手包。客户端在从服务端接收到一个握手响应包或时间溢出之前,应该每隔一段时间发送一个握手包(时间间隔由响应时间和系统
overhead
来权衡)。
握手包有如下信息:
1.
UDT
版本:这个值是兼容的目的。当前的版本是
2
2.
初始序号:这是发送这个
UDT
实体将来用于发送数据包的起始序号。它必须是一个在
1
到(
2^31-1
)之间的随机值。另外,建议这个值在合理的时间历史窗口中不应该重复。
3.
MSS
:数据包的大小(通过
IP
有效负载来度量)
4.
最大的流量窗口大小:这是接收到握手信息的
UDT
实体允许的最大流量窗口大小,窗口大小通常限制为接收端的数据结构大小。
服务器接收到一个握手包之后,比较
MSS
值和他自己的值并设置它自己的值为较小的值。结果值也在握手响应中被发送到客户端,另外还有服务器的版本信息,初始序列号,最大流量窗口大小。
版本字段用来检查两端的兼容性。初始序列号和最大流量窗口大小用于初始化接收到这个握手包的
UDT
实体参数。
服务器在第一步完成以后就准备发送或接收数据。然而,只要从同一个客户端接收任何握手包,其应该发送响应包。
客户端一旦得到服务器的一个握手响应其就进入发送和接收数据状态。设置它自己的
MSS
为握手响应包中的值并初始化相应的参数为包中的值(序列号、最大流量窗口)。如果收到任何其他的握手信息,丢掉它。
如果其中的
UDT
实体要关闭,它将发送一个关闭信息到对端;对方收到这个信息以后将自己关闭。这个关闭信息通过
UDP
传输,仅仅发送一次,并不保证一定收到。如果消息没有收到,对方将根据时间溢出机制来关闭连接。
3.9.丢失信息的压缩方案
NAK
包中携带的丢失信息是一个
32-bit
整数的数组。如果数组的中数字是一个正常的序号(第
1
位是
0
),这意味着这个序号的包丢失了,如果第
1
位是
1
,意味着从这个号码开始(包括该号码)到下一个数组中的元素(包括这个元素值)之间的包(它的第
1
位必须是
0
)都丢失。
例如,下面的
NAK
中携带的信息:
0x00000002, 0x80000006, 0x0000000B, 0x0000000E
上面的信息表明序号为:
2
,
6
,
7
,
8
,
9
,
10
,
11
,
14
的包都丢了。
4. 效率和公平性
UDT
能够充分利用当前有线网络的独立于连接容量的可用带宽
、
RTT
、后台共存流、给定的连接比特错误率。
UDT
在没有数据包丢失的情况下从
0bits/s
到
90%
带宽需要一个常量时间,这个时间是
7.5
秒。
UDT
并不适合无线网络。
UDT
的确满足单瓶劲网络拓扑的最大
-
最小公平性。在多个瓶劲情况下,根据最大最小原则它能保证较小瓶劲连接或者至少一半的平等共享
(
it guarantees that flows over smaller bottleneck links obtain at least half of their fair share according to max-min rule
)
。
RTT
对公平性都一点影响。
当和大块的
TCP
流共存的时候,
TCP
能占用比
UDT
更多的带宽,除了三种情况:
1.
网络
BDP
非常大,
TCP
不能利用他们的公平共享带宽。这种情况下,
UDT
将占用
TCP
不能利用的带宽。
2.
连接容量是如此的小,从而导致
UDT
的带宽估计技术不能最有的工作;模拟显示这个极限连接容量大约是
100kb/s
。
3.
在使用
FIFO
队列作为网络路径的网络中,如果队列大小大于
BDP
,
TCP
的共享带宽随着队列大小的增加而降低。然而,抵达
UDT
的共享带宽是,队列大小通常超过实际路由器
/
交换机提供的数量。
当短(
timewise
)类似
web
的
TCP
流和小的并发
UDT
流共存的时候,
UDT
在
TCP
流上的效果非常小。
更多的分析在
[GHG03]
。
5. 安全考虑
UDT
并没有使用特定的安全机制,相反,它依赖于应用程序提供的授权和底层提供的安全机制。
然而,由于
UDP
是无连接的,
UDT
实现应该检查所有达到的包是否是预期的来源。这是从
socket
的
API
连接概念中继承而来,其连接只是接收指定来源的数据。
- 6.UDT SOURCE CODE LINK