c#有关udp可靠传输(包传输数据包) 升级
在c#有关udp可靠传输(包传输数据包)我们讨论,UDP包的发送,可是上一个程序有一个问题。就是数据比較大。一个Message类序列化后都有2048B,而实际的数据量也就只是 50B罢了,这就说明当中数据有效的非常少。这样当传送的数据包过多后,效率会极大的降低。
因此我们仅仅有想办法降低冗余数据。
此项目中借用了飞鸽传书中的一个《FSLib.IPMessager》项目中的思想,并加以改善。感谢此项目作者,让我对此有了深刻的理解
我们须要自定义数据的传输结构 我们能够定义一个数据头 当中包括一些主要的必要信息。然后再把要传送的数据写入尾部。
我把这部分代码贴出来。要详细完整的项目请到我的资源区下载,因为LZ原创希望给点分哈!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net; namespace Netframe.Model
{
/// <summary>
/// 消息封包类
/// </summary>
public class MessagePacker
{
/*
* 消息包注意:
* 1.第一位始终是2(ASCII码50)
* 2.第二位到第九位是一个long类型的整数,代表消息编号
* 3.第十位到第十三位是一个int类型的整数,代表消息内容总长度
* 4.第十四位到第十七位是一个int类型的整数,代表分包的总数
* 5.第十八位到第二十一位是一个int类型的整数。代表当前的分包编号
* 6.第二十二位表示是否须要返回一个确认标识(1/0)
* 7.第二十三到第三十一位是保留的(Reserved)
* 8.第三十二字节以后是数据包
* */ /// <summary>
/// 消息版本
/// </summary>
public static byte VersionHeader { get { return 50; } }
/// <summary>
/// 返回当前消息封包的头字节数
/// </summary>
public static int PackageHeaderLength { get { return 32; } } /// <summary>
/// 获得消息包的字节流
/// </summary>
/// <param name="message">要打包的消息对象</param>
/// <returns></returns>
public static UdpPacketMsg[] BuildNetworkMessage(Msg message)
{
if (message.ExtendMessageBytes != null)
{
return BuildNetworkMessage(
message.RemoteAddr,
message.PackageNo,
message.Command,
message.UserName,
message.HostName,
message.Type,
message.NormalMsgBytes,
message.ExtendMessageBytes,
message.IsRequireReceive
);
}
else
{
return BuildNetworkMessage(
message.RemoteAddr,
message.PackageNo,
message.Command,
message.UserName,
message.HostName,
message.Type,
System.Text.Encoding.Unicode.GetBytes(message.NormalMsg),
System.Text.Encoding.Unicode.GetBytes(message.ExtendMessage),
message.IsRequireReceive
);
}
} /// <summary>
/// 获得消息包的字节流
/// </summary>
/// <param name="remoteIp">远程主机地址</param>
/// <param name="packageNo">包编号</param>
/// <param name="command">命令</param>
/// <param name="options">參数</param>
/// <param name="userName">username</param>
/// <param name="hostName">主机名</param>
/// <param name="content">正文消息</param>
/// <param name="extendContents">扩展消息</param>
/// <returns></returns>
public static UdpPacketMsg[] BuildNetworkMessage(IPEndPoint remoteIp, long packageNo, Commands command, string userName, string hostName,Consts type ,byte[] content, byte[] extendContents, bool RequireReceiveCheck)
{ //每次发送所能容下的数据量
int maxBytesPerPackage = (int)Consts.MAX_UDP_PACKAGE_LENGTH - PackageHeaderLength;
//压缩数据流
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress);
System.IO.BinaryWriter bw = new System.IO.BinaryWriter(zip, System.Text.Encoding.Unicode);
//写入头部数据
bw.Write(packageNo); //包编号
bw.Write(userName); //username
bw.Write(hostName); //主机名
bw.Write((long)command); //命令
bw.Write((long)type); //数据类型
bw.Write(content == null ? 0 : content.Length);//数据长度 //写入消息数据
if (content != null)
bw.Write(content);
bw.Write(extendContents == null ? 0 : extendContents.Length);//补充数据长度
if (extendContents != null)
bw.Write(extendContents);
//bw.Close();
//zip.Close();
ms.Flush();
ms.Seek(0, System.IO.SeekOrigin.Begin); //打包数据总量
int dataLength = (int)ms.Length; int packageCount = (int)Math.Ceiling(dataLength * 1.0 / maxBytesPerPackage);
UdpPacketMsg[] pnma = new UdpPacketMsg[packageCount];
for (int i = 0; i < packageCount; i++)
{
int count = i == packageCount - 1 ? dataLength - maxBytesPerPackage * (packageCount - 1) : maxBytesPerPackage; byte[] buf = new byte[count + PackageHeaderLength];
buf[0] = VersionHeader;//版本 第1位
BitConverter.GetBytes(packageNo).CopyTo(buf, 1);//消息编号 第2到9位 long类型的整数
BitConverter.GetBytes(dataLength).CopyTo(buf, 9);//消息内容长度 第10到13位 int类型的整数
BitConverter.GetBytes(packageCount).CopyTo(buf, 13);//分包总数 第14位到第17位 int类型的整数
BitConverter.GetBytes(i).CopyTo(buf, 17);//分包编号 第18位到第21位 int类型的整数
buf[21] = RequireReceiveCheck ? (byte)1 : (byte)0;//是否回确认包 第22位
//第23到第31位是保留的(Reserved)
ms.Read(buf, 32, buf.Length - 32);//第32字节以后是,详细的数据包 pnma[i] = new UdpPacketMsg()
{
Data = buf,
PackageCount = packageCount,
PackageIndex = i,
PackageNo = packageNo,
RemoteIP = remoteIp,
SendTimes = 0,
Version = 2,
IsRequireReceiveCheck = buf[21] == 1
};
}
bw.Close();
zip.Close();
ms.Close(); return pnma;
} /// <summary>
/// 检測确认是否是这个类型的消息包
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static bool Test(byte[] buffer)
{
return buffer != null && buffer.Length > PackageHeaderLength && buffer[0] == VersionHeader;
} /// <summary>
/// 缓存接收到的片段
/// </summary>
static Dictionary<long, UdpPacketMsg[]> packageCache = new Dictionary<long, UdpPacketMsg[]>(); /// <summary>
/// 分析网络数据包并进行转换为信息对象
/// </summary>
/// <param name="packs">接收到的封包对象</param>
/// <returns></returns>
/// <remarks>
/// 对于分包消息,假设收到的仅仅是片段而且尚未接收全然。则不会进行解析
/// </remarks>
public static Msg ParseToMessage(params UdpPacketMsg[] packs)
{
if (packs.Length == 0 || (packs[0].PackageCount > 1 && packs.Length != packs[0].PackageCount))
return null; //尝试解压缩,先排序
Array.Sort(packs);
//尝试解压缩
System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress);
//System.IO.BinaryWriter bw = new System.IO.BinaryWriter(zip, System.Text.Encoding.Unicode);
try
{
foreach (var s in packs)
{
if (zip.CanWrite)
{
zip.Write(s.Data, 0, s.Data.Length);
}
} //Array.ForEach(packs, s => zip.Write(s.Data, 0, s.Data.Length));
}
catch (Exception e)
{
//触发事件
return null;
} zip.Close();
ms.Flush();
ms.Seek(0, System.IO.SeekOrigin.Begin); //构造读取流
System.IO.BinaryReader br = new System.IO.BinaryReader(ms, System.Text.Encoding.Unicode); //開始读出数据
Msg m = new Msg(packs[0].RemoteIP);
m.PackageNo = br.ReadInt64();//包编号 m.UserName = br.ReadString();//username
m.HostName = br.ReadString();//主机名
m.Command = (Commands)br.ReadInt64(); //命令
m.Type = (Consts)br.ReadInt64();//数据类型
int length = br.ReadInt32(); //数据长度
m.NormalMsgBytes = new byte[length];
br.Read(m.NormalMsgBytes, 0, length);//读取内容 length = br.ReadInt32(); //附加数据长度
m.ExtendMessageBytes = new byte[length];
br.Read(m.ExtendMessageBytes, 0, length);//读取附加数据 if (m.Type == Consts.MESSAGE_TEXT)
{
m.NormalMsg = System.Text.Encoding.Unicode.GetString(m.NormalMsgBytes, 0, length); //正文
m.ExtendMessage = System.Text.Encoding.Unicode.GetString(m.ExtendMessageBytes, 0, length); //扩展消息
m.ExtendMessageBytes = null;
m.NormalMsgBytes = null; }
return m;
} /// <summary>
/// 尝试将收到的网络包解析为实体
/// </summary>
/// <param name="pack">收到的网络包</param>
/// <returns></returns>
/// <remarks>假设收到的包是分片包,且其全部子包尚未接受全然,则会返回空值</remarks>
public static Msg TryToTranslateMessage(UdpPacketMsg pack)
{
if (pack == null || pack.PackageIndex >= pack.PackageCount - 1) return null;
else if (pack.PackageCount == 1) return ParseToMessage(pack);
else
{
if (packageCache.ContainsKey(pack.PackageNo))
{
UdpPacketMsg[] array = packageCache[pack.PackageNo];
array[pack.PackageIndex] = pack; //检測是否完整
if (Array.FindIndex(array, s => s == null) == -1)
{
packageCache.Remove(pack.PackageNo);
return ParseToMessage(array);
}
else
{
return null;
}
}
else
{
UdpPacketMsg[] array = new UdpPacketMsg[pack.PackageCount];
array[pack.PackageIndex] = pack;
packageCache.Add(pack.PackageNo, array);
return null;
}
} } /// <summary>
/// 将网络信息解析为封包
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static UdpPacketMsg Parse(byte[] buffer, IPEndPoint clientAddress)
{
if (!Test(buffer)) return null; UdpPacketMsg p = new UdpPacketMsg()
{
RemoteIP = clientAddress,
SendTimes = 0
};
p.PackageNo = BitConverter.ToInt64(buffer, 1);//包编号
p.DataLength = (int)BitConverter.ToInt64(buffer, 9); //内容长度
p.PackageCount = BitConverter.ToInt32(buffer, 17);//分包总数
p.PackageIndex = BitConverter.ToInt32(buffer, 21);//索引
p.IsRequireReceiveCheck = buffer[21] == 1;//是否须要回包
p.Data = new byte[buffer.Length - PackageHeaderLength];
Array.Copy(buffer, PackageHeaderLength, p.Data, 0, p.Data.Length); return p;
} }
}
c#有关udp可靠传输(包传输数据包) 升级的更多相关文章
- UDP可靠传输那些事
有空来论坛走走,发现讨论udp可靠传输又热了起来,有人认为udp高效率,有人认为udp丢包重传机制容易控制,还有朋友搞极限测试,当然也有人推销自己的东西,这里写一点我个人的看法. udp可靠传输其实非 ...
- UDP可靠传输简易设计
UDP,鉴于其丢包和乱序(后发先至)问题,为保证其可靠性设计如下报头协议,供大家参考 数据包设计 数据包总大小按照MTU设计设置,小于1500字节 数据包示意图 包头类型说明 1.类型(1字节) 数值 ...
- 用wireshark抓包分析TCP三次握手、四次挥手以及TCP实现可靠传输的机制
关于TCP三次握手和四次挥手大家都在<计算机网络>课程里学过,还记得当时高超老师耐心地讲解.大学里我遇到的最好的老师大概就是这位了,虽然他只给我讲过<java程序设计>和< ...
- UDP如何实现可靠传输
概述 UDP不属于连接协议,具有资源消耗少,处理速度快的优点,所以通常音频,视频和普通数据在传送时,使用UDP较多,因为即使丢失少量的包,也不会对接受结果产生较大的影响. 传输层无法保证数据的可靠传输 ...
- 如何用 UDP 实现可靠传输?
作者:小林coding 计算机八股文刷题网站:https://xiaolincoding.com 大家好,我是小林. 我记得之前在群里看到,有位读者字节一面的时候被问到:「如何基于 UDP 协议实现可 ...
- TCP、UDP详解与抓包工具使用
参考:https://www.cnblogs.com/HPAHPA/p/7737641.html TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间 ...
- 记录一个UDP收包丢包的问题
这几天写GB28181平台接入层代码,对收到的PS包进行解包时,总是出现误码,最终导致rtsp点播服务中画面花屏. 分析了码流抓包数据之后,发现网络上没有丢包,遂认为PS流解包代码有bug,于是埋头分 ...
- TCP传输小数据包效率问题(译自MSDN)
TCP传输小数据包效率问题(译自MSDN) http://www.ftpff.com/blog/?q=node/16 摘要:当使用TCP传输小型数据包时,程序的设计是相当重要的.如果在设计方案中不对T ...
- iptables传输数据包的过程
IPTABLES传输数据包的过程 大概过程如图所示: 1. 数据包进入网卡时,首先进入PREROUTING链,linux内核会判断数据包的目的IP是否为本地主机 2. 如果数据包的目的IP是本地主机, ...
随机推荐
- poj 3375 Network Connection
今天在叉姐的群里找点题目做,这题目还是很好的:提意思如下 [有M个可以提供计算机网络的端口和N台计算机(计算机数量少于端口数),每个端口和计算机有一个坐标(一维的)!其中端口与计算机链接的距离 |x ...
- Could Not Connect
今天在写Quartz定时任务web应用时,访问项目页面出现Could Not Connect,后台也没报错. 然后我访问http://127.0.0.1:8080(Apache主页)的时候是正常的. ...
- Android Java 与 C++ 恒调用,路径、文件名、延长的最大长度
/**************************************************************************** ********************* ...
- 伪异步IO理解
伪异步IO实在堵塞IO的基础上将每个client发送过来的请求由新创建的线程来处理改进为用线程池来处理.因此避免了为每个client请求创建一个新线程造成的资源耗尽问题. 来看一下伪异步IO的服务端代 ...
- 在 Ubuntu 12.04 上安装 GitLab7.x
安装环境: 操作系统: Ubuntu 12.4 LTS 英文 数据库: postgresql webserver: nginx 能够说到7.x的时候,GitLab的文档已经相当完好 ...
- 与我一起extjs5(04--MVVM简要说明财产)
与我一起extjs5(04--MVVM简要说明财产) 以下我们来看一下自己主动生成的代码中的MVVM架构的关系. Main是一个可视的控件,MainController是这个控件的控制 ...
- ADN中国队参加微软Kinect他赢得了全国比赛三等奖,我们的创意项目与团队Kinect于Naviswork虚拟之旅
以下是我的英语写了一个简短的总结,直接贴出来. 让我们知道我们在这参加Hackathon That's an exciting Hackathon for me and also China team ...
- 达到HTTP合约Get、Post和文件上传功能——采用WinHttp介面
于<采用WinHttp实现HTTP协议Get.Post和文件上传功能>一文中,我已经比較具体地解说了怎样使用WinHttp接口实现各种协议. 在近期的代码梳理中,我认为Post和文件上传模 ...
- apache tomcat 集群! (转)
公司需要一个内部测试局域网, 要求可以支持3000并发访问!以前也没做过服务器这方面.临时抱佛脚,查看了N多文档,他人经验,布置好之后,又遇到了N多问题,功夫不负有心人.终于还是完成了要求!观他人的布 ...
- nodejs 复制、移动文件
对路径没有做验证 复制文件 var fs = require('fs'); var path = require('path'); var fileName = "coverflow-3.0 ...