如何让UDP实现可靠传输
自定义通讯协议,在应用层定义一些可靠的协议,比如检测包的顺序,重复包等问题,如果没有收到对方的ACK,重新发包
UDP没有Delievery Garuantee,也没有顺序保证,所以如果你要求你的数据发送与接受既要高效,又要保证有序,收包确认等,你就需要在UDP协议上构建自己的协议。比如RTCP,RTP协议就是在UPD协议之上专门为H.323协议簇上的IP电话设计的一种介于传输层和应用层之间的协议。
UDP构建可靠数据传输
简单来讲,要使用UDP来构建可靠的面向连接的数据传输,就要实现类似于TCP协议的超时重传,有序接受,应答确认,滑动窗口流量控制等机制,等于说要在传输层的上一层(或者直接在应用层)实现TCP协议的可靠数据传输机制,比如使用UDP数据包+序列号,UDP数据包+时间戳等方法,在服务器端进行应答确认机制,这样就会保证不可靠的UDP协议进行可靠的数据传输,不过这好像也是一个难题!
(一)可靠性协议
(可靠性协议这部分协议 参考论文《 基于UDP 的可靠文件传输协议设计与实现 》)
首先来设计最为重要的可靠性。在UDP增加报头前,我们先定义8个字节的协议头,为2个字节的数据包标识,2个字节的发送序号,2个字节的文件指针定位和2个字节的数据包中数据大小信息。数据包标志指明该数据包为文件数据包、确认包或者其它控制包,发送序号用来指明数据包的顺序信息,指针定位字节数据用来指明该数据包中数据被填写到文件的哪个位置,最后的大小信息也是用来向文件中读写数据时使用。
协议保证可靠性的大致流程是(先只考虑单对单情况下的单方向发送):
首先发送端发送一个文件信息报文,这个报文就是最简单的UDP报文,但是里面的信息很重要,记录着文件的大小,被分隔成的报文数,文件序号。发完这个信息包,发送端阻塞,等待接收端的回复报文才能继续。文件信息包被接收端接受以后使用确认机制确定是否接受这个文件,并把决定回馈给发送端。此时,发送端如果收到的是“确定接受”的结果,将会把这个文件的整组数据报全部发送过去,这里我们并不像最传统的可靠传输协议TCP协议一样,对于每个每个报文都要确认接受完才会对下一个报文进行处理,太没效率,遵守本协议的接受端在接受这组报文的时候将遵守错序重排机制,来对收到的这组报文进行按序号排序,期间可能序号不联系不过没关系,接受过程只要保证序号从小到大即可。发送端发完所有报文延迟一点时间再发送一个结束报文,延迟时间是为了减少结束报文比数据报文还早被接受的情况,当然即使这种情况出现也不会破坏可靠性,只不过在在结束报文之后的数据报文会被当做丢失的包被要求重发,降低效率。接受端接受到结束报文后按照一开始的文件信息包的信息和序列号做对比,把没有的序列号的报文的信息传回给发送端,要求重新发送这些报文。发送端接到信息以后重发丢失的数据包。直到接收端拿到的报文和信息匹配,接受端就可以发回一个“接受完毕”的报文。这样发送端接受端再进行下一次文件传输。
在这个流程中有几个重要的机制保证流程的可靠性:
确认机制
本系统接收方并非对任意数据包都进行确认,在下面的一些情况下会使用到该确认机制:
1、接收方收到文件信息包时,要对是否接收进行确认。
2、接收方收到结束包时要进行确认,然后检测该分组内数据包是否丢失。
3、接收方收到全部数据包时要进行确认,以便结束文件的传输过程。
重发机制
协议设计了两种重发机制:一种是自动重发机制,另一种是请求重发机制。自动重发机制是消息发送时启动一个定时器,如果在规定的一段时间内未收到接收方的确认消息,则断定这段时间内发送的报文已经丢失并进行重发。请求重发机制则是在接收方收到发送方发来的传输结束消息后,在接收方对收到的所有报文序号进行检测,如果发现某些序号的报文缺失,接收方主动请求重发缺失的序号对应的报文。具体实现设计如下所述:
1、自动重发机制
通信发送方和接收方都维持一个自动重发定时器,在通信开始前会检查自动重发定时器是否启动,如果没有启动,就会启动这个定时器。如果在一个特定时间间隔内发送方没有收到来自接收方的任何确认消息,或者接收方没有到发送方的通道检测报文。这时系统会将这个定时器归零,并将这段时间内发送的消息重发一遍,把记录重发次数变量加1。如果过在规定的时间内依然没有收到对方的任何确认信息,则重新将定时器归0, 执行重发操作并将重发次数加1,如此循环,在重发次数未达到指定数据之前,直到收到对方的一个确认消息,然后停止自动重发定时器,将重发次数清 0;否则证明传输路径出现问题。
2、请求重发机制
接收方记录着已收到数据包的序列以及未收到的数据包序列。当接收到分组结束包时,接收方就会启动定时器,检索该分组内未收到的数据包,如果数据包已全部接收到,则关闭定时器,进行下一个分组的传输。否则查找丢失 数据包的序号并依次发送请求重发数据包,在规定时间内接收发送方重发的数据包,然后定时器归 0,重新检索未收到数据包,并按上述情况做出反应,如此循环往复,直到最终完成该分组的传输过程。
协议的错序重排机制
协议头结构中有2个字节的序号字段,当发送端接收到对端发送的确认接收报文后,开始读取文件数据块内容写入协议数据区,为每一个数据块编制一个序号,序号的最大值要求与接收端维护的一个为了实现错序重排机制的动态表长度一致。序号排满后,后面的报文会在下一个分组中进行发送。这时发送端 会根据当前分组下读取的数据块大小及起始位置填写协议头中的字段,最后将数据包发送出去。接收端起初会生成一个动态数组用来存储接收到的数据包序号,当接收端准备好接收文件后将数组的每一位置为无效,每收到一个数据包,就会读取其序号字段值并将数组相应位置为有效,然后将数据区的内容写入文件。这样即使由于网络状况导致数据包不能按序到达,接收端也能根据数据包位置字段和大小字段将数据写入文件。序号字段在收到结束包后用于检索动态数组启动请求重传机制。
(二)多用户并发访问和文件下载协议
前面我们说UDP是面向无连接的,这样一来就可以打破一对一连接的状态,使得一台服务器可以向多个客户端传输相同信息。
所以我们如何利用它来实现多用户并发访问和文件下载呢?本协议中,首先发送方会开辟一个空间,这个空间储存着发送端有的各个文件的序号和可能出现的接收方希望得到某个序号文件的ip地址端口号等信息。在发送端开始运作之后,就会有一个进程一直在监听是否有对某个文件的请求,如果有就把请求方的信息储存在这个空间里。假如请求某个文件的ip+端口只有一个,那么这个协议是没有实质性作用。然而一旦短时间内有大量的ip请求这个文件,那么在某次我们上边设计的可靠性协议的传输过程中,就可以好好利用这个时间差,下一段具体说明。除此之外还有一个很人性化的设计可以加入到这里,就是可以在监听到某次请求时向对方返回目前的服务器状况,比如对应文件的等待ip有多少,这样让接受方决定状况比较拥堵时候是否等一下再请求。
具体来说,我们每次发送文件信息报文时,在多个接收方请求的情况下将是一组一组发送的,把目前在等待空间的所有(或者限制一个上限个数10个)接收方都发送文件信息报文。收到的同意接受报文后把这些同意接受的信息记录下来,将文件报文组和发送完毕的报文同时发送给这些接受方,再把各个接收方返回的缺失报文记录下来,再重传。
非常重要的一点是,其实对于接受方来说在文件传输过程中并没有什么多余的动作,它要做的和只有他一个接收方的情况没有任何的不同,可能只是整个过程的流畅性会受影响。需要变动的主要是发送方,它在做好可靠性协议要求的几点之外,还要做好一系列的记录操作,保证整个过程不会乱套。
(三)针对下载的文件大小不应有限制设计的协议
回答问题时候其实就把思路讲完全了,对于一份任意大小的文件,我们都是可以通过切分成固定大小的N个报文然后将他们组成一组发送的。在每次组报文传输过程之前传输的文件信息报文也是这个过程之一,它记录了本次文件的大小,被分成的报文数量,每个报文的大小(通常确定的大小)。这种方法下,不管多大的文件都视作一定数量的报文,只要发送方和接受方在信息报文中确定了信息,文件大小就没有限制
所以,利用上边设计的可靠性协议、多用户并发访问和文件下载协议和针对下载的文件大小不应有限制设计的协议,我们就能实现一个可靠的文件传输协议,并满足以下要求:1)下层使用不可靠的UDP服务(即使用数据报方式的套接字);2)能够支持多用户并发访问和文件下载;3)下载的文件大小不应有限制。