龙恒
摘 要: 用户数据报协议(UDP)是一种无连接传输层协议,提供的是一种不可靠的数据传输服务,不能保证将数据按顺序、完整地传送到目的地。本文引入了一些TCP的机制并进行了部分调整,在应用层上基于UDP实现了一个具有拥塞控制、重传等机制的新的数据传输协议。测试结果表明使用该协议传输的所有数据都能正确地到达目的地,成功解决了可靠性问题,同时其他方面都能满足设计要求。
关键词: UDP; 可靠性; 重传; 拥塞控制; 流量控制
中图分类号:TP311 文献标识码:A 文章编号:1006-8228(2020)04-33-05
A reliable transmission protocol based on UDP
Long Heng
(Maoming Polytechnic, Maoming, Guangdong 525000, China)
Abstract: User Datagram Protocol (UDP) is a connectionless transport layer protocol, it provides an unreliable data transmission service, and doesn't guarantee that the data transferred to the destination is in order and is integrated. This article introduces some TCP mechanisms and makes some improvement, to realize a UDP based new protocol with the mechanisms of congestion control and retransmission at the application layer. Test results show that all the data transmitted with the protocol can reach the destination correctly. It solved the reliability issues successfully and all aspects can meet the requirements.
Key words: UDP; reliability; retransmission; congestion control; flow control
0 引言
UDP是一种应用广泛的传输层协议,但是傳输过程中容易出现数据包的丢失或失序等问题。而TCP协议则供了一套完善的保证可靠性的机制,可以保证将数据按顺序完整地发送到目的地。本文在UDP的基础上,引入了一些TCP的概念,在应用层上实现了一个可靠的数据传输协议,称为RUTP(Reliable Udp Transmission Protocol)。RUTP实现了接收方流量控制、拥塞控制、重传和往返时间测量这些保证传输可靠性的基本机制[1],并且实现了SACK、Timestamp和Window Scale这三个对提高性能有帮助的功能。
1 协议首部
RUTP使用一个20字节的首部,结构如图1所示,各个字段的含义如表1所示。
其中确认序号(acknowledgment)是一个32位的无符号整数。与RFC793中定义的acknowledgment number作用相同,是对接收到的字节数的确认,取值为被确认的数据包的序号(sequence)加上该数据包所携带的数据字节数,也就是下一个期望接收的字节的序号[2]。
另外还有一些只在特定数据包中才会出现的字段,称为选项字段,其紧跟在RUTP固定首部字段之后。其中与保证可靠性有关的有两个:SACK和PZW。SACK是选择性确认字段,其功能与RFC2018中描述的相同[3],但是组织方式有所不同;PZW选项实现的是RFC1122的Probing Zero Windows[4]功能。在一个数据包中,RUTP首部出现在UDP首部之后。
2 系统变量
RUTP定义了一系列的系统变量,为了更好地讲明问题,把本文中用到的变量做一个简单的说明:
RMSS:接收方能接收的最大报文段。
SMSS:发送方能发送的最大报文段。
MAX_RECV_SEQ:接收到的数据包的最大序号。
MAX_RECV_ACK:接收到的数据包的最大确认序号。
LAST_SEND_SEQ:最后发送出去的序号,为最近一次发送出去的携带数据的数据包的序号加上其数据字节数。
LAST_SEND_ACK:最后发送出去的确认序号,为最近一次接收到的数据包的序号加上所包含的数据字节数。
TS_RECENT:记录接收到的带有数据的有效数据包的时间,当该数据包的序号小于等于LAST_
SEND_ACK的时候更新TS_RECENT的值。
FLIGHT_SIZE:当前发送出去但是还没有被接收方确认的数据的字节数。
3 数据的接收
RUTP是一种全双工协议,通讯的双方可以同时接收和发送数据。其中接收流程如图2所示。
3.1 滑动窗口
接收方使用一个称为“滑动窗口”的机制进行流量控制,这是对接收方的接收缓冲区使用状况的一个形象的描述,也是判断数据包是否合法的重要依据之一,图3显示了一个滑动窗口在某个时间点的状态。
用于描述滑动窗口的重要变量有:窗口大小的最大值(swMax),窗口左边缘(swLeft)、当前窗口大小(swSize)、窗口的右边缘(swRight),需要注意的是swLeft与swSize相加并不总是等于swRight,因为如果出现丢失/失序的情况,滑动窗口的左边缘将会停留在接收到的序号连续的数据的最右的位置,在这种情况下接收到的序号更大的合法数据包,将会被保存起来等待丢失/失序的数据包到达后再提交给应用程序,数据保存后,swSize的值将减小。
3.2 数据包分类
依据接收到的数据包的序号、TSval值、MAX_RECV_SEQ系统变量、MAX_RECV_ACK系统变量和滑动窗口的状态将它们进行分类。
3.2.1 非法数据包
假设数据包的序号为SEQ,携带的数据的字节数为L,符合以下条件之一的数据包被认为是非法数据包:
⑴ 首部ack标志为1,并且符合下列条件之一。
① 携带有数据;
② 确认序号小于MAX_RECV_ACK;
③ 序号不处于swLeft-swMax与swLeft+swMax之间。
⑵ 首部的TSval值小于TS_RECENT。这是RFC1323中的PAWS(Protect Against Wrapped Sequence Numbers)[5]要求。
⑶ 序号处于滑动窗口之外,也就是SEQ
3.2.2 失序数据包
指的是携带有数据,但是不按顺序到达接收方的数据包。这可能是由于延迟、丢失后被重传等原因造成的。其特点是序号不等于swLeft。
3.2.3 重复数据包
指的是合法的携带有数据,但是前面已经成功接收过的数据包,其特点是序号大于等于swLeft-swMax并且序号加上L小于swLeft。重复数据包的作用是为了在接收到后立刻发送一个确认,这与重传的算法有关。
3.2.4 PZW数据包
这种数据包与在RFC1122中定义的Probing Zero Windows数据包类似,但是实现有所不同,在RUTP中PZW数据包指的是首部的ack和opt标志都为1,且在首部指定了PZW选项的数据包。
3.2.5 数据段
指的是除了上面各种类型之外、携带有数据并且按顺序到达的合法数据包。
3.3 数据的保存
RUTP协议使用一个接收队列和一个失序链表来保存接收到的数据。接收队列用于保存接收到的数据包,每个接收到的数据包的序号通过了合法性检验后就会被放入接收队列中,这些数据包按序号的从小到大从队列的首部开始向后排列。
失序鏈表与接收队列一起工作,它的表项指向接收队列的失序位置的下一项,与swLeft一起可以标识出所有的失序情况。
图4显示了swLeft、接收队列和失序表在某一时刻的状态。
为了支持发送重复确认,失序链表的每个表项还会保存其创建时滑动窗口的大小。在发送确认时,如果出现失序的情况就使用第一个表项的滑动窗口的值作为其首部的rwnd字段的值,这样对于发送方就能符合重复确认的rwnd值相同的要求。
上述设计方式对生成选择性确认(SACK)信息也有帮助,RUTP的SACK功能与RFC2018中定义的功能相同,但是实现方式有所不同,RUTP中SACK块是按顺序从小到大在首部选项中排序的,而且RUTP的选项没有TCP的最长40字节的限制。在生成SACK选项的时候只需遍历一次失序链表即可。
3.4 确认的发送
确认指的是接收方发送的、对本方已经接收到的数据的情况起告知作用的数据包,RUTP的确认数据包将首部的ack标志设置为1。RUTP协议在如下几种情况下会发送确认:
⑴ 接收到失序数据包、重复数据包或PZW数据包都会立刻发送一个确认;
⑵ 连续接收到的未确认字节数大于等于2×RMSS并且满足式⑴的要求:
swSize>=min(0.5×swMax,RMSS) ⑴
式⑴是为了防止出现RFC1122中定义的糊涂窗口综合症(SWS)。
⑶ 确认定时器超时,确认定时器实现的是RFC1122中定义的delayed ACK功能,RFC6298要求延迟的时间必须少于500毫秒[6],超时的时候如果未确认字节数计数器大于0,就会发送一个确认,并且将计数器设置为0,确认定时器只确认以前没有确认过的数据。创建确认时,其首部各字段的取值如下:
rwnd:如果失序表不为空,就使用第一个表项记录的滑动窗口值,否则rwnd字段值使用swSize。
acknowledgment:滑动窗口左边缘swLeft;
sequence:LAST_SEND_SEQ系统变量的值;
TSval:当前时间值;
TSecr:TS_RECENT系统变量的值。
RUTP不会重传已丢失了的确认,要等到接收到发送方发送的数据包才会继续发送确认。
4 数据的发送
图5显示了数据的发送流程。
4.1 数据包类型
在一个功能完整的发送方,除了可以发送数据段、重传数据包、发送PZW数据包之外,还必须处理接收到的确认和重复确认,根据RFC5681中的定义,当同时满足如下条件,一个确认就被认为是一个重复确认[7]:
⑴ 该确认的接收者有发出但未确认的数据;
⑵ 该确认不携带有数据;
⑶ 首部syn和fin标志都为0;
⑷ 确认序号等于MAX_RECV_ACK;
⑸ 首部的rwnd值与上一个确认的rwnd值相同。
对于接收方而言,并没有重复确认这个概念,其所发送的所有确认都是遵循相同的方法创建的,是否重复确认由接收方进行判断。
4.2 发送过程
从图5可以看到,如果不满足发送条件,发送进程将进入等待状态,在满足等待发送队列不为空或发送缓冲区不为空且RWND>0这两个条件之一,发送进程才被唤醒。
当接收到一个确认后,首先检查该确认是否携带SACK信息,如果有就对未确认队列进行扫描,将那些被确认的数据包打上被确认(SACKed)标志,避免被重传,接着从未确认队列中删除序号小于确认的ack字段的数据包,因为这些被删除的数据包已经确定被接收方接收到了。
4.3 拥塞控制
拥塞控制在RFC5681中定义,是发送方的一项重要功能,由慢启动、拥塞避免、快速重传和快速恢复功能组成。有兩个用于拥塞控制的变量:
⑴ CWND:发送方在接收到一个响应之前可以发送到网络的数据的最大字节数。根据RFC5681的定义CWND的初始值IW如下。
如果SMSS>2190字节:
IW=2×SMSS。
如果SMSS>1095字节并且SMSS<=2190字节:
IW=3×SMSS字节。
如果SMSS<=1095字节:
IW=4×SMSS字节。
⑵ SSTHRESH:一个用于判断是使用慢启动算法还是拥塞避免算法的阀值,当CWND
开始发送数据的时候由于不了解网络的拥塞状况,应该以较慢的速度将数据包发送到网络中,以避免大量的突发数据包对网络造成拥塞,这就是慢启动的目的。
拥塞避免是为了在发现网络出现拥塞状况的时候,降低数据包进入网络的速率。
快速重传指的是连续接到三个重复确认就立刻重传序号与该确认的ack字段的值相同的数据包,而不用等到重传定时器超时。在发生了快速重传后,就进入了快速恢复的过程而不是重新使用慢启动算法发送数据包。快速恢复的CWND初始值更大,可以允许更多的数据包进入网络。
假设N是某个确认数据包确认的字节数,D是接收到的重复确认的个数,接收到确认时拥塞控制的工作流程如图6所示。
CWND=SSTHRESH ⑵
CWND=CWND+min(N,SMSS) ⑶
CWND=CWND+max(1,SMSS×SMSS/CWND) ⑷
CWND=CWND+SMSS ⑸
SSTHRESH=max(FLIGHT_SIZE/2,2×SMSS) ⑹
CWND=SSTHRESH+3×SMSS ⑺
另外,如果在重传定时器超时的时候重传了数据包,就要执行式⑹和式⑻。
CWND=SMSS ⑻
超时重传之后的发送过程从慢启动算法开始执行。
4.4 重传定时器
定时重传是一个更重要的重传机制,快速重传只能根据接收方的提示逐个重传丢失的数据包,而在重传定时器中结合SACK机制,一次可以重传一批数据包,另外PZW数据包也是在重传定时器中发送的。
重传定时器的重传超时时间(Retransmission TimeOut,下称RTO)使用RFC1323和RFC6298定义的机制进行计算,每接收到一个确认了新数据的确认数据包都会重新计算一次RTO,同时也会重置一次定时器。
4.4.1 RTO的计算
RUTP采用RFC1323定义的RTT测量方法,为此定义了一个系统变量TS_RECENT, 它的值出现在每个发送出去的数据包首部的Tsecr字段中。
假设接收到的数据包序号为SEQ,携带的数据字节数为L,如果SEQ<=LAST_SEND_ACK 在接收到一个确认了数据的确认数据包后,将当前时间减去其首部的Tsecr字段值,就可以得到往返时间(round-trip time,下称RTT),然后根据RFC6298中定义的方法就可以计算出RTO。 4.4.2 工作流程 图7显示了重传定时器的工作流程。 根据RFC2018中的定义,在图7中被重传的数据包指的是所有没有设置SACKed标志且序号小于设置了SACKed标志的数据包的最大序号。 5 结束语 RUTP协议可以用于点对点传输数据,也可以用于客户端/服务器结构的应用之间传输数据。RUTP协议目前主要关注的是可靠性、易用性和通用性,应用场景接近于TCP,但是要求能够应用于某些TCP不适用的场合。经过测试,所有传输的数据都能正确到达目的地,没有出现过传输错误的情况。在易用性方面,RUTP为应用程序提供了与标准套接字类似的API接口,熟悉套接字编程的程序员可以轻松掌握RUTP协议的使用方法。从应用情况来看,RUTP协议在各方面都能满足当前需求。 参考文献(References): [1] 福罗赞著.王海译.TCP/IP协议族(第4版)[M].清华大学出版社,2011. [2] Information Sciences Institute University of SouthernCalifornia.RFC793 transmission control protocol[S/OL].(1981-9)[2013-5].http://tools.ietf.org/html/rfc0793.html [3] S.Floyd,A.Romanow, M.Mathis,J.Mahdavi.RFC2018 TCPSelective Acknowledgment Options [S/OL].(1996-10)[2013-5].http://tools.ietf.org/html/rfc2018 [4] Internet Engineering Task Force R. Braden.RFC1122Requirements for Internet Hosts-Communication Layers [S/OL].(1989-10)[2013-5].http://tools.ietf.org/html/rfc1122 [5] V.Jacobson,R.Braden.RFC1323 TCP Extensions for High-Performance[S/OL].(1989-10)[2013-5].http://www.ietf.org/rfc/rfc1323.txt [6] V.Paxson,M.Allman.RFC6298 Computing TCP's Retrans-mission Timer [S/OL]. (2000-9)[2013-5].http://tools.ietf.org/html/rfc6298 [7] M.Allman,V.Paxson,E.Blanton.RFC5681 TCP CongestionControl [S/OL].(2009-9)[2013-5].http://tools.ietf.org/html/rfc5681