当前位置:  编程技术>.net/c#/asp.net

如何应用C#实现UDP的分包组包

    来源: 互联网  发布时间:2014-10-18

    本文导语:  场景介绍如果需要使用UDP传输较大数据,例如传输10M的图片,这突破了UDP的设计原则。UDP的设计是基于"datagram",也就是它假设你发送的每个数据包都能包含在单一的包内。并且设定UDP数据包的最大长度受基础网络协议的限制。...

场景介绍
如果需要使用UDP传输较大数据,例如传输10M的图片,这突破了UDP的设计原则。UDP的设计是基于"datagram",也就是它假设你发送的每个数据包都能包含在单一的包内。并且设定UDP数据包的最大长度受基础网络协议的限制。

UDP数据包的理论最大长度限制是 65535 bytes,这包含 8 bytes 数据包头和 65527 bytes 数据。但如果基于IPv4网络传输,则还需减去 20 bytes 的IP数据包头。
则单一的UDP数据包可传输的数据最大长度为:

则单一的UDP数据包可传输的数据最大长度为:

MaxUdpDataLength = 65535 - 8 - 20 = 65507 bytes

这就需要实现UDP包的分包传输和接收组包功能。

分包功能

代码如下:

///
   /// UDP数据包分割器
   ///
   public static class UdpPacketSplitter
   {
     ///
     /// 分割UDP数据包
     ///
     /// UDP数据包所持有的序号
     /// 被分割的UDP数据包
     /// 分割块的长度
     ///
     /// 分割后的UDP数据包列表
     ///
     public static ICollection Split(long sequence, byte[] datagram, int chunkLength)
     {
       if (datagram == null)
         throw new ArgumentNullException("datagram");

       List packets = new List();

       int chunks = datagram.Length / chunkLength;
       int remainder = datagram.Length % chunkLength;
       int total = chunks;
       if (remainder > 0) total++;

       for (int i = 1; i 0)
       {
         int length = datagram.Length - (chunkLength * chunks);
         byte[] chunk = new byte[length];
         Buffer.BlockCopy(datagram, chunkLength * chunks, chunk, 0, length);
         packets.Add(new UdpPacket(sequence, total, total, chunk, length));
       }

       return packets;
     }
   }

发送分包
代码如下:

private void WorkThread()
 {
   while (IsRunning)
   {
     waiter.WaitOne();
     waiter.Reset();

     while (queue.Count > 0)
     {
       StreamPacket packet = null;
       if (queue.TryDequeue(out packet))
       {
         RtpPacket rtpPacket = RtpPacket.FromImage(
           RtpPayloadType.JPEG,
           packet.SequenceNumber,
           (long)Epoch.GetDateTimeTotalMillisecondsByYesterday(packet.Timestamp),
           packet.Frame);

         // max UDP packet length limited to 65,535 bytes
         byte[] datagram = rtpPacket.ToArray();
         packet.Frame.Dispose();

         // split udp packet to many packets
         // to reduce the size to 65507 limit by underlying IPv4 protocol
         ICollection udpPackets
           = UdpPacketSplitter.Split(
             packet.SequenceNumber,
             datagram,
             65507 - UdpPacket.HeaderSize);
         foreach (var udpPacket in udpPackets)
         {
           byte[] udpPacketDatagram = udpPacket.ToArray();
           // async sending
           udpClient.BeginSend(
             udpPacketDatagram, udpPacketDatagram.Length,
             packet.Destination.Address,
             packet.Destination.Port,
             SendCompleted, udpClient);
         }
       }
     }
   }
 }

接收组包功能
代码如下:

private void OnDatagramReceived(object sender, UdpDatagramReceivedEventArgs e)
     {
       try
       {
         UdpPacket udpPacket = UdpPacket.FromArray(e.Datagram);

         if (udpPacket.Total == 1)
         {
           RtpPacket packet = new RtpPacket(udpPacket.Payload, udpPacket.PayloadSize);
           Bitmap bitmap = packet.ToBitmap();
           RaiseNewFrameEvent(
             bitmap, Epoch.GetDateTimeByYesterdayTotalMilliseconds(packet.Timestamp));
         }
         else
         {
           // rearrange packets to one packet
           if (packetCache.ContainsKey(udpPacket.Sequence))
           {
             List udpPackets = null;
             if (packetCache.TryGetValue(udpPacket.Sequence, out udpPackets))
             {
               udpPackets.Add(udpPacket);

               if (udpPackets.Count == udpPacket.Total)
               {
                 packetCache.TryRemove(udpPacket.Sequence, out udpPackets);

                 udpPackets = udpPackets.OrderBy(u => u.Order).ToList();
                 int rtpPacketLength = udpPackets.Sum(u => u.PayloadSize);
                 int maxPacketLength = udpPackets.Select(u => u.PayloadSize).Max();

                 byte[] rtpPacket = new byte[rtpPacketLength];
                 foreach (var item in udpPackets)
                 {
                   Buffer.BlockCopy(
                     item.Payload, 0, rtpPacket,
                     (item.Order - 1) * maxPacketLength, item.PayloadSize);
                 }

                 RtpPacket packet = new RtpPacket(rtpPacket, rtpPacket.Length);
                 Bitmap bitmap = packet.ToBitmap();
                 RaiseNewFrameEvent(
                   bitmap,
                   Epoch.GetDateTimeByYesterdayTotalMilliseconds(packet.Timestamp));

                 packetCache.Clear();
               }
             }
           }
           else
           {
             List udpPackets = new List();
             udpPackets.Add(udpPacket);
             packetCache.AddOrUpdate(
               udpPacket.Sequence,
               udpPackets, (k, v) => { return udpPackets; });
           }
         }
       }
       catch (Exception ex)
       {
         RaiseVideoSourceExceptionEvent(ex.Message);
       }
     }


    
 
 

您可能感兴趣的文章:

  • C# Split分隔字符串的应用(C#、split、分隔、字符串)
  • c# 应用事务的简单实例
  • 使用C# Winform应用程序获取网页源文件的解决方法
  • 深入C#任务管理器中应用程序选项隐藏程序本身的方法详解
  • C#访问应用程序配置文件的方法
  • C#反射的一些基本应用
  • c#取得控制台应用程序根目录
  • C#可选参数应用举例
  • C# Pointer指针应用实例简述
  • 解决C# X64应用程序中读取WParam溢出的问题
  • 将c#编写的程序打包成应用程序的实现步骤分享(安装,卸载) 图文
  • C#窗口关闭到最小化与应用程序最小化到托盘的实现代码
  • C#加密在实际中的应用
  • C#打包应用程序,与.NETFramework介绍
  • C#数组应用分析第1/2页
  • c# 多维数组应用举例(初级)
  • C#实现只运行单个实例应用程序的方法(使用VB.Net的IsSingleInstance)
  • C#实现点击按钮退出应用程序实例
  • C# 禁止应用程序多次启动的实例
  • C#中分部类和分部方法的应用
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • 不太明白,利用RMI实现JAVA分布式应用 和 EJB实现JAVA分布式应用有什么区别。
  • 新手问几个应用层实现的问题
  • 物联网应用平台实现框架 Pinecone
  • 如何重复运行一个应用程序(shell实现,在线等......)
  • 基本问题:jsp开发的网页能否实现用户控制启动服务器中某应用程序的功能?
  • 请问在编写Java小应用程序的时候,有些功能用JavaScript实现是否可以提高效率呢?
  • 大家一般都是怎么实现应用程序中的动态图表的?
  • 请问能否在linux实现一个应用程序访问另外一个程序的内存数据?
  • Android Intent启动别的应用实现方法
  • j2ee 模式的分布式应用如何实现复杂报表的找印?
  • Android应用开发中模拟按下HOME键的效果(实现代码)
  • linux上到应用程序原子操作的实现问题
  • linux QT应用程序, 如何在代码中获取root权限,实现重启电脑
  • 在java的GUI的应用程序中能否实现对容器(如Frame)中的所有组件遍历?
  • C语言应用程序使用的函数来源于libc库, Libc库中的函数是在哪里实现的??
  • 请教:如何能实现从内核层调用应用层的程序
  • 使用mutex实现应用程序单实例运行代码分享
  • 知道远程机器root帐户的情况下怎么实现远程安装应用程序?
  • Oracle创建主键自增表(sql语句实现)及触发器应用
  • 用applet实现打印,除了弹出系统自带的打印对话框,在自其前后各弹出一个”小应用程序需打印,要继续吗?“的对话框,如何去掉这两个对话
  • 重装服务器后IIS网站错误(应用程序中的服务器错误)
  • 让HTML5应用与原生应用一样运行流畅 Steroids.js
  • 隐藏andriod 应用app启动图标的几种方法
  • 如何将应用程序加到桌面或应用程序组?
  • ​传统应用的docker化迁移
  • 怎样开发在LINUX 上运行的应用程序,像WINDOWS桌面应用程序一样
  • Http协议3XX重定向介绍及301跳转和302跳转应用场景
  • adnroid已安装应用中检测某应用是否安装的代码实例
  • Docker 1.12.4应用容器引擎发布及下载地址
  • linux商业应用或者说开源软件商业应用是否需要付费?
  • Docker v1.13.0 应用容器引擎正式版发布及下载地址


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3