可靠UDP传输的实现

一直纠结于UDP的可靠传输,在查看了UDT的相关介绍后,发现并不是我需要的,我需要的是一个可以进行多点传输的的可靠UDP。最终,还是自己慢慢把它完成了,虽然还不够好,不过总算出来个框架了,以后改就容易了 。
        在算法上并没有什么特别的,就是不断的采用超时重发机制。在发送端,建立了一个庞大的发送缓冲区队列,每个发送过的发送单元都将自动被添加到这个发送缓冲区中,然后由维护线程来管理。在维护线程中,定时遍历所有的发送缓冲区,检查那些没有被确认且还没达到发送最大值限制的发送单元,然后将它们发送出去,以此类推,直到所有的发送单元都被确认或者都达到发送最大值限制。请看下面的发送单元结构:                                                                                                                                      
<pre name="code" class="cpp">//发送单元
typedef struct _UDT_SND_ITEM_
{
 USHORT           uBufLen;               //chBuf的数据长度
 USHORT           uLimitCnt;             //发送次数限制
 SOCKET           hSock;                 //UDP句柄
 CHAR                chBuf[DATA_BUFSIZE];   //要发送的数据,UDT头 + 数据
 DWORD            dwLastTime;            //最后发送时间(如果为0xFFFFFFFF则表示已经确认)
  SOCKADDR_IN      To;                    //发送的目的地
}UDT_SND_ITEM;


 
 
在CUDTSend类中的变量 DWORD * m_pdwPos是用来维护发送单元的集合,当UDTSend函数将一个要发送的数据包拆分成独立的发送单元并发送时,都会依据这个发送单元的序号将该发送单元添加到m_pdwPos集合中,使该集合中的对应序号的地址就是这个发送单元。目前这个发送缓冲区的最大容量使65536,也就是最大可以存储65536个未确认的发送单元。
        相比之下,接收类的设计就显的比较复杂,因为接收类要考虑来自不同主机所发送过来的数据,还要考虑每个主机发送的不同数据包,并且就算是同一个数据包,也还要按顺序接收每个单元数据,在组合这些单元数据为一个整数据包,针对以上问题,我通过定义以下的数据结构来完成功能:
//接收包单元
typedef struct _UDT_RECV_ITEM_
{
 DWORD              dwSeq;                   //序号
 DWORD              uRecvLen;               //数据长度
 CHAR               chBuf[DATA_SNDSIZE];    //数据部分
}UDT_RECV_ITEM;



//数据包(接收单元的组合)
typedef struct _UDT_RECV_ITEMS_
{
    DWORD                dwSeqSpan;               //包序号区间
 USHORT               uPacketCount;            //包数量来自于包区间序号相减
 USHORT               uRecvCount;              //已经接收的包数量
 DWORD                dwLastTime;              //最后接收到包的时间
 CCriticalSection     cs_item;                 //接收单元同步对象
 DWORD                dwItemPos[1024];         //所有接收包单元集合
 _UDT_RECV_ITEMS_   * ItemsNext;               //指向下一个ITEMS结构
}UDT_RECV_ITEMS;


//主机结点(每个接收的主机信息)
typedef struct _UDT_RECV_PEER_
{
 SOCKADDR_IN         Peer;                    //发送端
 DWORD               dwLastTime;              //最后更新时间
 CCriticalSection    cs_items;                //包表示同步对象
    _UDT_RECV_ITEMS_  * ItemsNext;               //指向下一个ITEMS结构
    _UDT_RECV_PEER_   * PeerNext;                //指向下一个节点
}UDT_RECV_PEER;



以上三个结构通过以下示意图的方式连接起来,实现收包的功能
UDT_RECV_PEER ——> UDT_RECV_PEER ——> UDT_RECV_PEER ——>.......  
              |  
              |  
             V
UDT_RECV_ITEMS——>UDT_RECV_ITEM——>UDT_RECV_ITEM——>......
             |
             |
            V
UDT_RECV_ITEMS
             |
             |
            V
UDT_RECV_ITEMS
             |
             |
            V
          ......

以上示意图的意思是说,如果收到的数据包来自不同的主机节点都会添加一个UDT_RECV_PEER节点形成一个主机节点链表,针对与每个主机可能会收到不同的数据包,采用在主机节点下串联UDT_RECV_ITEMS结构的方法存放数据包,针对每个接收包单元会存放在UDT_RECV_ITEMS结构下的dwItemPos[1024]成员中,该成员的每个元素都可以用来存放UDT_RECV_ITEM接收单元。以这样的结构来完成数据包的可靠接收,并完成从单一的接收单元组合成完整的数据包的过程,这整个过程都通过调用CUDTRecv类的UDTRecv函数来完成,该函数完成了从接收数据,到发送确认,再到存储接收单元,最后实现组包这一系列过程,并通过输出参数将接收到的数据包返回给调用者。

        综合以上的说明,不难看出,虽然实现了可靠传输,但是并没有实现完全的可靠传输,因为受到发送缓冲的序号限制,最大只能同时存在65536个发送包,也就是说如果在整个发送过程中同时存在的未确认包超过这个限制,就会使发送包出现错误,并导致数据无法可靠传输;而且由于是尽可能的可到传输(限次发送),所以如果遇到网络情况恶劣的情况下,可能会导致部分发送数据超过发送最大值限制而被清除;因此这里介绍的可到传输以及相关代码只是实现了一个框架,并可以满足大部分情况,如果要满足所有情况,则需要不断的修改完善。


相关代码:点击下载


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值