c#有关udp可靠传输(包传输数据包)我们讨论,UDP包的发送,可是上一个程序有一个问题。就是数据比較大。一个Message类序列化后都有2048B,而实际的数据量也就只是 50B罢了,这就说明当中数据有效的非常少。这样当传送的数据包过多后,效率会极大的降低。

因此我们仅仅有想办法降低冗余数据。

此项目中借用了飞鸽传书中的一个《FSLib.IPMessager》项目中的思想,并加以改善。感谢此项目作者,让我对此有了深刻的理解

我们须要自定义数据的传输结构    我们能够定义一个数据头  当中包括一些主要的必要信息。然后再把要传送的数据写入尾部。

我把这部分代码贴出来。要详细完整的项目请到我的资源区下载,因为LZ原创希望给点分哈!

项目下载地址

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Net;
  6.  
  7. namespace Netframe.Model
  8. {
  9. /// <summary>
  10. /// 消息封包类
  11. /// </summary>
  12. public class MessagePacker
  13. {
  14. /*
  15. * 消息包注意:
  16. * 1.第一位始终是2(ASCII码50)
  17. * 2.第二位到第九位是一个long类型的整数,代表消息编号
  18. * 3.第十位到第十三位是一个int类型的整数,代表消息内容总长度
  19. * 4.第十四位到第十七位是一个int类型的整数,代表分包的总数
  20. * 5.第十八位到第二十一位是一个int类型的整数。代表当前的分包编号
  21. * 6.第二十二位表示是否须要返回一个确认标识(1/0)
  22. * 7.第二十三到第三十一位是保留的(Reserved)
  23. * 8.第三十二字节以后是数据包
  24. * */
  25.  
  26. /// <summary>
  27. /// 消息版本
  28. /// </summary>
  29. public static byte VersionHeader { get { return 50; } }
  30. /// <summary>
  31. /// 返回当前消息封包的头字节数
  32. /// </summary>
  33. public static int PackageHeaderLength { get { return 32; } }
  34.  
  35. /// <summary>
  36. /// 获得消息包的字节流
  37. /// </summary>
  38. /// <param name="message">要打包的消息对象</param>
  39. /// <returns></returns>
  40. public static UdpPacketMsg[] BuildNetworkMessage(Msg message)
  41. {
  42. if (message.ExtendMessageBytes != null)
  43. {
  44. return BuildNetworkMessage(
  45. message.RemoteAddr,
  46. message.PackageNo,
  47. message.Command,
  48. message.UserName,
  49. message.HostName,
  50. message.Type,
  51. message.NormalMsgBytes,
  52. message.ExtendMessageBytes,
  53. message.IsRequireReceive
  54. );
  55. }
  56. else
  57. {
  58. return BuildNetworkMessage(
  59. message.RemoteAddr,
  60. message.PackageNo,
  61. message.Command,
  62. message.UserName,
  63. message.HostName,
  64. message.Type,
  65. System.Text.Encoding.Unicode.GetBytes(message.NormalMsg),
  66. System.Text.Encoding.Unicode.GetBytes(message.ExtendMessage),
  67. message.IsRequireReceive
  68. );
  69. }
  70. }
  71.  
  72. /// <summary>
  73. /// 获得消息包的字节流
  74. /// </summary>
  75. /// <param name="remoteIp">远程主机地址</param>
  76. /// <param name="packageNo">包编号</param>
  77. /// <param name="command">命令</param>
  78. /// <param name="options">參数</param>
  79. /// <param name="userName">username</param>
  80. /// <param name="hostName">主机名</param>
  81. /// <param name="content">正文消息</param>
  82. /// <param name="extendContents">扩展消息</param>
  83. /// <returns></returns>
  84. public static UdpPacketMsg[] BuildNetworkMessage(IPEndPoint remoteIp, long packageNo, Commands command, string userName, string hostName,Consts type ,byte[] content, byte[] extendContents, bool RequireReceiveCheck)
  85. {
  86.  
  87. //每次发送所能容下的数据量
  88. int maxBytesPerPackage = (int)Consts.MAX_UDP_PACKAGE_LENGTH - PackageHeaderLength;
  89. //压缩数据流
  90. System.IO.MemoryStream ms = new System.IO.MemoryStream();
  91. System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress);
  92. System.IO.BinaryWriter bw = new System.IO.BinaryWriter(zip, System.Text.Encoding.Unicode);
  93. //写入头部数据
  94. bw.Write(packageNo); //包编号
  95. bw.Write(userName); //username
  96. bw.Write(hostName); //主机名
  97. bw.Write((long)command); //命令
  98. bw.Write((long)type); //数据类型
  99. bw.Write(content == null ? 0 : content.Length);//数据长度
  100.  
  101. //写入消息数据
  102. if (content != null)
  103. bw.Write(content);
  104. bw.Write(extendContents == null ? 0 : extendContents.Length);//补充数据长度
  105. if (extendContents != null)
  106. bw.Write(extendContents);
  107. //bw.Close();
  108. //zip.Close();
  109. ms.Flush();
  110. ms.Seek(0, System.IO.SeekOrigin.Begin);
  111.  
  112. //打包数据总量
  113. int dataLength = (int)ms.Length;
  114.  
  115. int packageCount = (int)Math.Ceiling(dataLength * 1.0 / maxBytesPerPackage);
  116. UdpPacketMsg[] pnma = new UdpPacketMsg[packageCount];
  117. for (int i = 0; i < packageCount; i++)
  118. {
  119. int count = i == packageCount - 1 ? dataLength - maxBytesPerPackage * (packageCount - 1) : maxBytesPerPackage;
  120.  
  121. byte[] buf = new byte[count + PackageHeaderLength];
  122. buf[0] = VersionHeader;//版本 第1位
  123. BitConverter.GetBytes(packageNo).CopyTo(buf, 1);//消息编号 第2到9位 long类型的整数
  124. BitConverter.GetBytes(dataLength).CopyTo(buf, 9);//消息内容长度 第10到13位 int类型的整数
  125. BitConverter.GetBytes(packageCount).CopyTo(buf, 13);//分包总数 第14位到第17位 int类型的整数
  126. BitConverter.GetBytes(i).CopyTo(buf, 17);//分包编号 第18位到第21位 int类型的整数
  127. buf[21] = RequireReceiveCheck ? (byte)1 : (byte)0;//是否回确认包 第22位
  128. //第23到第31位是保留的(Reserved)
  129. ms.Read(buf, 32, buf.Length - 32);//第32字节以后是,详细的数据包
  130.  
  131. pnma[i] = new UdpPacketMsg()
  132. {
  133. Data = buf,
  134. PackageCount = packageCount,
  135. PackageIndex = i,
  136. PackageNo = packageNo,
  137. RemoteIP = remoteIp,
  138. SendTimes = 0,
  139. Version = 2,
  140. IsRequireReceiveCheck = buf[21] == 1
  141. };
  142. }
  143. bw.Close();
  144. zip.Close();
  145. ms.Close();
  146.  
  147. return pnma;
  148. }
  149.  
  150. /// <summary>
  151. /// 检測确认是否是这个类型的消息包
  152. /// </summary>
  153. /// <param name="buffer"></param>
  154. /// <returns></returns>
  155. public static bool Test(byte[] buffer)
  156. {
  157. return buffer != null && buffer.Length > PackageHeaderLength && buffer[0] == VersionHeader;
  158. }
  159.  
  160. /// <summary>
  161. /// 缓存接收到的片段
  162. /// </summary>
  163. static Dictionary<long, UdpPacketMsg[]> packageCache = new Dictionary<long, UdpPacketMsg[]>();
  164.  
  165. /// <summary>
  166. /// 分析网络数据包并进行转换为信息对象
  167. /// </summary>
  168. /// <param name="packs">接收到的封包对象</param>
  169. /// <returns></returns>
  170. /// <remarks>
  171. /// 对于分包消息,假设收到的仅仅是片段而且尚未接收全然。则不会进行解析
  172. /// </remarks>
  173. public static Msg ParseToMessage(params UdpPacketMsg[] packs)
  174. {
  175. if (packs.Length == 0 || (packs[0].PackageCount > 1 && packs.Length != packs[0].PackageCount))
  176. return null;
  177.  
  178. //尝试解压缩,先排序
  179. Array.Sort(packs);
  180. //尝试解压缩
  181. System.IO.MemoryStream ms = new System.IO.MemoryStream();
  182. System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress);
  183. //System.IO.BinaryWriter bw = new System.IO.BinaryWriter(zip, System.Text.Encoding.Unicode);
  184. try
  185. {
  186. foreach (var s in packs)
  187. {
  188. if (zip.CanWrite)
  189. {
  190. zip.Write(s.Data, 0, s.Data.Length);
  191. }
  192. }
  193.  
  194. //Array.ForEach(packs, s => zip.Write(s.Data, 0, s.Data.Length));
  195. }
  196. catch (Exception e)
  197. {
  198. //触发事件
  199. return null;
  200. }
  201.  
  202. zip.Close();
  203. ms.Flush();
  204. ms.Seek(0, System.IO.SeekOrigin.Begin);
  205.  
  206. //构造读取流
  207. System.IO.BinaryReader br = new System.IO.BinaryReader(ms, System.Text.Encoding.Unicode);
  208.  
  209. //開始读出数据
  210. Msg m = new Msg(packs[0].RemoteIP);
  211. m.PackageNo = br.ReadInt64();//包编号
  212.  
  213. m.UserName = br.ReadString();//username
  214. m.HostName = br.ReadString();//主机名
  215. m.Command = (Commands)br.ReadInt64(); //命令
  216. m.Type = (Consts)br.ReadInt64();//数据类型
  217. int length = br.ReadInt32(); //数据长度
  218. m.NormalMsgBytes = new byte[length];
  219. br.Read(m.NormalMsgBytes, 0, length);//读取内容
  220.  
  221. length = br.ReadInt32(); //附加数据长度
  222. m.ExtendMessageBytes = new byte[length];
  223. br.Read(m.ExtendMessageBytes, 0, length);//读取附加数据
  224.  
  225. if (m.Type == Consts.MESSAGE_TEXT)
  226. {
  227. m.NormalMsg = System.Text.Encoding.Unicode.GetString(m.NormalMsgBytes, 0, length); //正文
  228. m.ExtendMessage = System.Text.Encoding.Unicode.GetString(m.ExtendMessageBytes, 0, length); //扩展消息
  229. m.ExtendMessageBytes = null;
  230. m.NormalMsgBytes = null;
  231.  
  232. }
  233. return m;
  234. }
  235.  
  236. /// <summary>
  237. /// 尝试将收到的网络包解析为实体
  238. /// </summary>
  239. /// <param name="pack">收到的网络包</param>
  240. /// <returns></returns>
  241. /// <remarks>假设收到的包是分片包,且其全部子包尚未接受全然,则会返回空值</remarks>
  242. public static Msg TryToTranslateMessage(UdpPacketMsg pack)
  243. {
  244. if (pack == null || pack.PackageIndex >= pack.PackageCount - 1) return null;
  245. else if (pack.PackageCount == 1) return ParseToMessage(pack);
  246. else
  247. {
  248. if (packageCache.ContainsKey(pack.PackageNo))
  249. {
  250. UdpPacketMsg[] array = packageCache[pack.PackageNo];
  251. array[pack.PackageIndex] = pack;
  252.  
  253. //检測是否完整
  254. if (Array.FindIndex(array, s => s == null) == -1)
  255. {
  256. packageCache.Remove(pack.PackageNo);
  257. return ParseToMessage(array);
  258. }
  259. else
  260. {
  261. return null;
  262. }
  263. }
  264. else
  265. {
  266. UdpPacketMsg[] array = new UdpPacketMsg[pack.PackageCount];
  267. array[pack.PackageIndex] = pack;
  268. packageCache.Add(pack.PackageNo, array);
  269. return null;
  270. }
  271. }
  272.  
  273. }
  274.  
  275. /// <summary>
  276. /// 将网络信息解析为封包
  277. /// </summary>
  278. /// <param name="buffer"></param>
  279. /// <returns></returns>
  280. public static UdpPacketMsg Parse(byte[] buffer, IPEndPoint clientAddress)
  281. {
  282. if (!Test(buffer)) return null;
  283.  
  284. UdpPacketMsg p = new UdpPacketMsg()
  285. {
  286. RemoteIP = clientAddress,
  287. SendTimes = 0
  288. };
  289. p.PackageNo = BitConverter.ToInt64(buffer, 1);//包编号
  290. p.DataLength = (int)BitConverter.ToInt64(buffer, 9); //内容长度
  291. p.PackageCount = BitConverter.ToInt32(buffer, 17);//分包总数
  292. p.PackageIndex = BitConverter.ToInt32(buffer, 21);//索引
  293. p.IsRequireReceiveCheck = buffer[21] == 1;//是否须要回包
  294. p.Data = new byte[buffer.Length - PackageHeaderLength];
  295. Array.Copy(buffer, PackageHeaderLength, p.Data, 0, p.Data.Length);
  296.  
  297. return p;
  298. }
  299.  
  300. }
  301. }

c#有关udp可靠传输(包传输数据包) 升级的更多相关文章

  1. UDP可靠传输那些事

    有空来论坛走走,发现讨论udp可靠传输又热了起来,有人认为udp高效率,有人认为udp丢包重传机制容易控制,还有朋友搞极限测试,当然也有人推销自己的东西,这里写一点我个人的看法. udp可靠传输其实非 ...

  2. UDP可靠传输简易设计

    UDP,鉴于其丢包和乱序(后发先至)问题,为保证其可靠性设计如下报头协议,供大家参考 数据包设计 数据包总大小按照MTU设计设置,小于1500字节 数据包示意图 包头类型说明 1.类型(1字节) 数值 ...

  3. 用wireshark抓包分析TCP三次握手、四次挥手以及TCP实现可靠传输的机制

    关于TCP三次握手和四次挥手大家都在<计算机网络>课程里学过,还记得当时高超老师耐心地讲解.大学里我遇到的最好的老师大概就是这位了,虽然他只给我讲过<java程序设计>和< ...

  4. UDP如何实现可靠传输

    概述 UDP不属于连接协议,具有资源消耗少,处理速度快的优点,所以通常音频,视频和普通数据在传送时,使用UDP较多,因为即使丢失少量的包,也不会对接受结果产生较大的影响. 传输层无法保证数据的可靠传输 ...

  5. 如何用 UDP 实现可靠传输?

    作者:小林coding 计算机八股文刷题网站:https://xiaolincoding.com 大家好,我是小林. 我记得之前在群里看到,有位读者字节一面的时候被问到:「如何基于 UDP 协议实现可 ...

  6. TCP、UDP详解与抓包工具使用

    参考:https://www.cnblogs.com/HPAHPA/p/7737641.html TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间 ...

  7. 记录一个UDP收包丢包的问题

    这几天写GB28181平台接入层代码,对收到的PS包进行解包时,总是出现误码,最终导致rtsp点播服务中画面花屏. 分析了码流抓包数据之后,发现网络上没有丢包,遂认为PS流解包代码有bug,于是埋头分 ...

  8. TCP传输小数据包效率问题(译自MSDN)

    TCP传输小数据包效率问题(译自MSDN) http://www.ftpff.com/blog/?q=node/16 摘要:当使用TCP传输小型数据包时,程序的设计是相当重要的.如果在设计方案中不对T ...

  9. iptables传输数据包的过程

    IPTABLES传输数据包的过程 大概过程如图所示: 1. 数据包进入网卡时,首先进入PREROUTING链,linux内核会判断数据包的目的IP是否为本地主机 2. 如果数据包的目的IP是本地主机, ...

随机推荐

  1. iOS8推送消息的回复处理速度

    iOS8我们有一个新的通知中心,我们有一个新的通报机制.当在屏幕的顶部仅需要接收一个推拉向下,你可以看到高速接口,天赋并不需要输入应用程序的操作.锁定屏幕,用于高速处理可以推动项目. 推送信息,再次提 ...

  2. 透过浏览器看HTTP缓存(转)

    作为前端开发人员,对于我们的站点或应用的缓存机制我们能做的似乎不多,但这些却是与我们关注的性能息息相关的部分,站点没有做任何缓存机制,我们的页面可能会因为资源的下载和渲染变得很慢,但大家都知道去找前端 ...

  3. TinyXml高速入口(一)

    笔者:朱金灿 来源:http://blog.csdn.net/clever101 对于xml文件,眼下我的工作仅仅是集中在配置文件和作为简单的信息文件来用.因此我不太喜欢使用msxml这样的重量级的x ...

  4. 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)

    原文:乐在其中设计模式(C#) - 代理模式(Proxy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 代理模式(Proxy Pattern) 作者:webabcd 介绍 为 ...

  5. HDU 1069 Monkey and Banana(DP 长方体堆放问题)

    Monkey and Banana Problem Description A group of researchers are designing an experiment to test the ...

  6. 懒与馋的平衡:餐饮O2O市场广阔,发展不易

    餐饮行业是众多行业中O2O起步较早的,现在方兴未艾的团购站点中最先涉足的领域就有餐饮版块.长时间的合作推广,很多餐饮商家已经从中尝到甜头,可以说餐饮行业市场基础培育的比較好,所以餐饮O2O 已经是大势 ...

  7. Android - 和其他APP交互 - 让其他app启动你的activity

    前面的两篇文章主要讲了一个方面:从app中启动其他app.但是如果你的app可以处理对其他app有用的操作,你的app也应该响应其他app的操作请求.例如,如果你创建了一个社交app可以分享信息和图片 ...

  8. 电商指尖---(6)solrconfig.xml配置具体解释

    solrconfig.xml配置文件主要定义SOLR理规则,包含索引数据的存放位置,更新,删除,查询的一些规则配置. 能够在tomcat的安装路径下找到这个文件C:\Program Files\Apa ...

  9. Android4.0 Design之UI设计缺陷1

    我想成为Android卓越发展project联赛,不知道Android它如何设计规则,Android4.0谷歌公司的问世后Android一系列的设计原则,程序猿规范,不要盲目模仿IOS它的设计,由于A ...

  10. HDU 1541 Stars (树状数组)

    Problem Description Astronomers often examine star maps where stars are represented by points on a p ...