c#基于事件模型的UDP通讯框架(适用于网络包编解码)

时间:2022-02-25 04:49:44

之前写过一篇关于c#udp分包发送的文章

这篇文章里面介绍的方法是一种实现,可是存在一个缺点就是一个对象序列化后会增大非常多。不利于在网络中的传输。

我们在网络中的传输是须要尽可能的减小传送的数据包的大小。于是我參考了网上一些资料和一些开源的项目()这个上面的那个开源的飞鸽传输的框架。

事实上也就是把要传送的数据依照某种规定放在一个byte数组中,然后接收到后依照对应的格式把数据解析出来,为了减小数据还使用了GZipStream的压缩,之前出的问题就是在解压缩时,只是如今已经攻克了。

首先我们要定义一种能表示我们传送数据的包的格式

public class PacketNetWorkMsg : IComparable<PacketNetWorkMsg> { /// <summary> /// 封包版本号 /// </summary> public int Version { get; set; } /// <summary> /// 要发送的数据包 /// </summary> public byte[] Data { get; set; } /// <summary> /// 数据包所含数据长度 /// </summary> public int DataLength { get; set; } /// <summary> /// 分包后最后一个剩余长度 /// </summary> public int Remainder { get; set; } /// <summary> /// 远程地址 /// </summary> public IPEndPoint RemoteIP { get; set; } /// <summary> /// 发送次数 /// </summary> public int SendTimes { get; set; } /// <summary> /// 包编号 /// </summary> public long PackageNo { get; set; } /// <summary> /// 分包索引 /// </summary> public int PackageIndex { get; set; } /// <summary> /// 分包总数 /// </summary> public int PackageCount { get; set; } /// <summary> /// 获得或设置是否须要返回已收到标志 /// </summary> public bool IsRequireReceiveCheck { get; set; } public PacketNetWorkMsg() { Version = 1; CreationTime = DateTime.Now; } public PacketNetWorkMsg(long packageNo, int Count, int index, byte[] data, int dataLength, int remainder, IPEndPoint desip, bool IsRequireReceive) { this.PackageNo = packageNo; this.PackageCount = Count; this.PackageIndex = index; this.Data = data; this.DataLength = dataLength; this.Remainder = remainder; this.IsRequireReceiveCheck = IsRequireReceive;//默认都须要确认包 this.RemoteIP = desip; } #region IComparable<PackedNetworkMessage> 成员 public int CompareTo(PacketNetWorkMsg other) { return PackageIndex < other.PackageIndex ? -1 : 1; } #endregion /// <summary> /// 获得生成数据包的时间 /// </summary> public DateTime CreationTime { get; private set; } }这个类是我们在网络中须要传输的详细最小的包

然后另一个就是我们用来表示我们传输的数据的格式类Msg类。一个Msg类可能被分为多个PacketNetWorkMsg 来传送。分包的方法是为了突破udp方式数据传输的限制(64k),,可以使用udp传输大数据。非常多人就在问,为什么不用TCP,,尽管TCP是非常好的方式。可是我也不想说出个所以然来,我就喜欢这么干。

public class Msg { /// <summary> /// 是否已经被处理.在挂钩过程中,假设为true,则底层代码不会再对信息进行处理 /// </summary> public bool Handled { get; set; } /// <summary> /// 获得或设置当前的消息编号 /// </summary> /// <value></value> /// <remarks></remarks> public long PackageNo { get; set; } /// <summary> /// 获得或设置当前的消息所属的主机名 /// </summary> public string HostName { get; set; } /// <summary> /// 获得或设置当前的消息所属的username /// </summary> public string UserName { get; set; } /// <summary> /// 获得或设置当前的命令代码 /// </summary> //命令的名称 public Commands Command { get; set; } /// <summary> /// 获得或设置当前的消息的类型 文本消息。或者二进制消息 /// </summary> public Consts Type { get; set; } /// <summary> /// 获得或设置当前的命令消息文本 /// </summary> public string NormalMsg { get; set; } /// <summary> /// 消息文本字节 /// </summary> public byte[] NormalMsgBytes { get; set; } /// <summary> /// 扩展消息文本字节 /// </summary> public byte[] ExtendMessageBytes { get; set; } /// <summary> /// 获得或设置当前命令的扩展文本 /// </summary> public string ExtendMessage { get; set; } /// <summary> /// 远程地址 /// </summary> public IPEndPoint RemoteAddr { get; set; } /// <summary> /// 主机地址 /// </summary> public IPEndPoint HostAddr { get; set; } /// <summary> /// 获得或设置是否须要返回已收到标志 /// </summary> public bool IsRequireReceive { get; set; } public Msg(IPEndPoint Addr) { RemoteAddr = Addr; Handled = false; Type = Consts.MESSAGE_TEXT; } public Msg(IPEndPoint hostIP,IPEndPoint remoteIP,Commands cmd) { HostAddr = hostIP; RemoteAddr = remoteIP; Command = cmd; Handled = false; Type = Consts.MESSAGE_TEXT; } public Msg(IPEndPoint addr, string hostName, string userName,Commands command, string message, string extendMessage) { RemoteAddr = addr; Handled = false; HostName = hostName; UserName = userName; Command = command; NormalMsg = message; ExtendMessage = extendMessage; Type = Consts.MESSAGE_TEXT; } /// <summary> /// 直接创建一个新的Message对象 /// </summary> /// <param>主机对象</param> /// <param>远程地址</param> /// <param>主机名</param> /// <param>username</param> /// <param>命令</param> /// <param>选项</param> /// <param>信息</param> /// <param>扩展信息</param> /// <returns></returns> public static Msg Create(Host host, IPEndPoint addr, string hostName, string userName, Commands command, string message, string extendMessage) { return new Msg(addr,hostName, userName, command,message, extendMessage); } }
眼下这两个类就是我们基本的数据结构了。