UDP分包重组算法2

// 发送时的操作:

#iclude "packet.h"

const int RTP_SPLIT_PACKSIZE =     1300;//udp一般最大包为1500,一般这里都设置为1400,当然1300也没错

LONGLONG index = rand();                       //帧序数,从一个随机数开始,我机器上生成的是41
//分包发送,逻辑相当简单,按最常规的方法分割发送


void Send(BYTE* pBuff,UINT nLen,ULONG ip,USHORT port)
{
   
    HeadExt head;
    strcpy(head.flag ,"aaaa");
     
    head.m_nSeqNumber = index++; //帧序数

    head.m_nTotalFragment = nLen/RTP_SPLIT_PACKSIZE;
    head.m_nTotalFragment  += nLen%RTP_SPLIT_PACKSIZE?1:0; //整除的话就是0,否则多出一个尾数据
    head.m_nFragmentIndex = 0;//第一个分段从0开始
    head.m_nTotalSize = nLen;
    head.m_bLastFragment = 0;
    head.m_usFragOffset = 0;

    char tem[RTP_SPLIT_PACKSIZE+sizeof(HeadExt)];

    if(head.m_nTotalFragment == 1)
    {
        memcpy(tem,&head,sizeof(HeadExt));
        memcpy(tem+sizeof(HeadExt),pBuff,nLen);
        head.m_usPayloadSize = nLen;

       //用UDP发送,常规做法,先对UDP做一个封装的类,这里调用类对象。本代码只说明原理,类似伪代友

         sendto(tem,nLen+sizeof(HeadExt),ip,port);


        CString str;
        str.Format("发送数据:m_nSeqNumber%d ,m_nTotalFragment %d,m_nFragmentIndex %d,m_usFragOffset %d,m_nTotalSize %d,m_nDataLen %d/r/n",
            head.m_nSeqNumber,head.m_nTotalFragment,head.m_nFragmentIndex,head.m_usFragOffset,head.m_nTotalSize,head.m_usPayloadSize);
        OutputDebugString(str);


        return;
    }


    int i = 0;
    for( i = 0; i < head.m_nTotalFragment-1; i++)//开始分割,最后一个单独处理
    {
        head.m_bLastFragment = 0;
        head.m_nFragmentIndex = i;
        head.m_usFragOffset = i*RTP_SPLIT_PACKSIZE;
        head.m_usPayloadSize = RTP_SPLIT_PACKSIZE;
        memcpy(tem,&head,sizeof(HeadExt));
        memcpy(tem+sizeof(HeadExt),pBuff+i*RTP_SPLIT_PACKSIZE,RTP_SPLIT_PACKSIZE);
       sendto(tem,nLen+sizeof(HeadExt),ip,port);

        CString str;
        str.Format("发送数据:m_nSeqNumber%d ,m_nTotalFragment %d,m_nFragmentIndex %d,m_usFragOffset %d,m_nTotalSize %d,m_nDataLen %d/r/n",
            head.m_nSeqNumber,head.m_nTotalFragment,head.m_nFragmentIndex,head.m_usFragOffset,head.m_nTotalSize,head.m_usPayloadSize);
        OutputDebugString(str);

    }

    head.m_bLastFragment = 1;
    head.m_nFragmentIndex = i;
    head.m_usFragOffset = i*RTP_SPLIT_PACKSIZE;
    head.m_usPayloadSize = nLen - (i*RTP_SPLIT_PACKSIZE);

    memcpy(tem,&head,sizeof(HeadExt));
    memcpy(tem+sizeof(HeadExt),pBuff+i*RTP_SPLIT_PACKSIZE,nLen - (i*RTP_SPLIT_PACKSIZE));

 

    sendto(tem,nLen+sizeof(HeadExt),ip,port);

    CString str;
        str.Format("发送数据:m_nSeqNumber%d ,m_nTotalFragment %d,m_nFragmentIndex %d,m_usFragOffset %d,m_nTotalSize %d,m_nDataLen %d/r/n",
            head.m_nSeqNumber,head.m_nTotalFragment,head.m_nFragmentIndex,head.m_usFragOffset,head.m_nTotalSize,head.m_usPayloadSize);
        OutputDebugString(str);
}

//接收数据的回调

//代码很简单,收到数据后,形成PacketIn,然后找一个链表,插进去

//因为收到的数据的顺序是不定的,所以先创建一个链表,每帧首数据到来时,分配一个节点,其他分片到来的时候,都加到//这个节点里,等数据都到齐了,这个节点就是一个完整的包,回调出去。

void RecvCallback(BYTE* pBuff,UINT nLen,ULONG ip,USHORT port)
{
    if(nLen < sizeof(HeadExt))
        return;
   
   
    HeadExt* pHead = (HeadExt*)pBuff;
    CString str = pHead->flag;
    if(str != "aaaa")
    {
        return;
    }
    if(pHead->m_nTotalFragment == 1)//只有一帧,不分片
    {
        //回调,上层处理
        if(m_pfDispatch)
            m_pfDispatch(pBuff+sizeof(HeadExt),nLen-sizeof(HeadExt),ip,port,m_lpParam);

        return;
    }

    PacketIn data( (char*)pBuff, 64000, nLen );
    if ( !data.Normalize() )
        return;
   
 
    if ( data.head.m_nTotalFragment>1 )
    {
        Packet* pTemp = GetPartialPacket( iaRemote, data.head.m_nSeqNumber );
        if ( pTemp )
        {
            if ( pTemp ->InsertFragment( &data ) )
            {
                m_pfDispatch(pTemp ->m_pBuffer,
                    pTemp ->recvedbytes,ip,port,m_lpParam);
            }
        }//end of if
    }
}

//上面用到的一些变量可以在一个.h里面定义,比如:

Packet TempPacket[16];//分配一个数组,每个节点是一个重组后临时缓冲

Packet* GetPartialPacket(const CVLInetAddr& iaFrom, UINT nSeqNumber);//从数组里取出一个缓冲节点

Packet* GetPartialPacket(const ULONG ip,USHORT port, UINT nSeqNumber)//根据这几个关键字查找,不过只用//到了nSeqNumber,要是3人以上的视频聊天,ip和port是必要的
{
    Packet* tmp = NULL;
    int i=0;
    while(tmp==NULL && i<16)
    {
        //该包所属的帧已有其它数据包到达
        if(TempPacket[i].seqnum==nSeqNumber && TempPacket[i].recvedpacks>0)
        {
            tmp = &TempPacket[i];
            break;
        }
        i++;
    }
    if(tmp == NULL)        //新的帧
    {
        //查找空闲元素
        for(i=0;i<16;i++)
        {
            if(!TempPacket[i].m_bUsed)
                break;
        }

        if(i>=16)
        {
            //没有空闲的元素,丢掉一个最早
            tmp = &TempPacket[0];
            int j = 0;
            for(i=1;i<16;i++)
            {
                if(TempPacket[i].seqnum < tmp->seqnum)
                {
                    tmp = &TempPacket[i];
                    j = i;
                }
            }
            //找到最早的一帧
            if(tmp->m_pBuffer)
            {
                delete []tmp->m_pBuffer;
                tmp->reset();
            }
           
           
        }
        else
            tmp = &TempPacket[i];
    }

    InsertFragment
    tmp->m_bUsed = true;
    tmp->seqnum = nSeqNumber;

    return tmp;
}

整个示例最重要的是两个函数:

GetPartialPacket:取到一个临时缓冲节点

InsertFragment:把收到的每个数据段插入临时缓冲组成一个完整帧

当然发送方也要按一定规则分割发送。

 

更多技术文章请参见我的个人网站:http://www.joyvc.cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值