.Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--类设计2
2012-03-28 09:15
911 查看
本案例使用.Net Socket的Tcp、Udp实现字串、文件、各种序列化对象的网络传输,同时封装了Tcp的粘包、半包处理细节,定义了网络封包格式,在发送端和接收端无需考虑内部传输细节。以下是类设计:
网络封包服务类设计抽象类提供Tcp、Udp共有的行为和特征,Tcp、Udp发包和收包的细节不同,所以发包方法和收包方法定义为抽象方法去子类实现
提供网络封包传输服务的核心类代码:
时间关系我没有编写Udp异步传输子类
网络封包服务类设计抽象类提供Tcp、Udp共有的行为和特征,Tcp、Udp发包和收包的细节不同,所以发包方法和收包方法定义为抽象方法去子类实现
提供网络封包传输服务的核心类代码:
示范代码using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Runtime.InteropServices; using System.IO; namespace TcpLabCommon { public delegate void TcpAsynHandler1(NetPacket packet); public delegate void TcpAsynHandler2(NetPacketHead packetHead); /// <summary> /// 网络封包Tcp异步服务类 /// </summary> public class NetPacketTcpAsynService : NetPacketService { /// <summary> /// 发包前事件 /// </summary> public event TcpAsynHandler1 OnBeforeSendPacket; /// <summary> /// 发包后事件 /// </summary> public event TcpAsynHandler2 OnAfterSendPacket; /// <summary> /// 收到网络封包后的事件 /// </summary> public event TcpAsynHandler1 OnReceivedPacket; public NetPacketTcpAsynService(NetworkStream netStream) : base(netStream) { } /// <summary> /// 该方法的返回值总是为空.提取一个完整网络包然后返回,返回的封包需要通过注册OnReceivedPacket事件的函数获取 /// 如果收到的包是STRING类型,NetPacket.Data是发送的字符串 /// 如果收到的包是BINARY类型,NetPacket.Data是发送的文件名长度+文件名+文件内容,请调用ParseFile函数进行解析 /// 如果收到的包是COMPLEX类型,NetPacket.Data是发序列化的对象,可以直接转型使用 /// </summary> /// <returns></returns> public override NetPacket ReceivePacket() { //判断是否满足一个完整封包大小 if (IsFullNetPacket())//如果有完整封包就返回 { NetPacket packet = PickNetPacket(); if (OnReceivedPacket != null) //判断事件是否注册,如果注册调用回调函数传递收到的封包 { OnReceivedPacket(packet); } //return null;//提取到一个封包后应该及时返回 } //【缓冲区不满足一个完整封包大小则继续从网络流读取数据,异步读取】 _netStream.BeginRead(_tempBuffer, 0, BUFFER_SIZE, new AsyncCallback(AsyncCallbackReadFromNetStream),_netStream); return null; } /// <summary> /// 从网络流异步读取数据回调函数 /// </summary> /// <param name="result"></param> private void AsyncCallbackReadFromNetStream(IAsyncResult result) { NetworkStream netStream = (NetworkStream)result.AsyncState; int readLen = netStream.EndRead(result); //判断读取的字节数+缓冲区已有字节数是否超过缓冲区总大小 if (readLen + _netDataOffset > _netDataBuffer.Length) { if (IsFullNetPacketHead())//如果缓冲区数据满足一个包头数据大小,则可以计算出本次接收的包需要的缓冲区大小,从而实现一次调整大小 { Array.Resize<Byte>(ref _netDataBuffer, FullNetPacketSize); } else //不满足一个完整的网络封包的大小 { Array.Resize<Byte>(ref _netDataBuffer, _netDataBuffer.Length + BUFFER_SIZE * 2); } } //将新读取的数据拷贝到缓冲区 Array.Copy(_tempBuffer, 0, _netDataBuffer, _netDataOffset, readLen); //修改"网络数据实际长度" _netDataOffset += readLen; ReceivePacket(); } /// <summary> /// 发包[发送Tcp包/发送Udp数据报] /// </summary> /// <param name="packet"></param> protected override void SendPacket(NetPacket packet) { if (packet == null || packet.Data == null || packet.PacketHead == null) return; if (OnBeforeSendPacket != null) OnBeforeSendPacket(packet); MemoryStream mStream = new MemoryStream(); #region【计算包体长度】 if (packet.PacketHead.Len == 0) { switch (packet.PacketHead.PType) { case PacketType.STRING: packet.PacketHead.Len = Encoding.Default.GetBytes(Convert.ToString(packet.Data)).Length; break; case PacketType.BINARY: packet.PacketHead.Len = ((Byte[])packet.Data).Length; break; case PacketType.COMPLEX: packet.PacketHead.Len = GetCanSerializableObjectSize(packet.Data); break; default: break; } } #endregion #region【写入包头】 mStream.Write(BitConverter.GetBytes(packet.PacketHead.Version), 0, Marshal.SizeOf(packet.PacketHead.Version)); mStream.Write(BitConverter.GetBytes((Int32)packet.PacketHead.PType), 0, sizeof(Int32)); mStream.Write(BitConverter.GetBytes(packet.PacketHead.Len), 0, Marshal.SizeOf(packet.PacketHead.Len)); #endregion #region【写入包体】 byte[] buffer = null; switch (packet.PacketHead.PType) { case PacketType.STRING: buffer = Encoding.Default.GetBytes(Convert.ToString(packet.Data)); break; case PacketType.BINARY: buffer = (byte[])packet.Data; break; case PacketType.COMPLEX: MemoryStream m = new MemoryStream(); SerializeHelper<BinarySerializeHelper>().Serialize(m, packet.Data); m.Position = 0; buffer = new byte[m.Length]; m.Read(buffer, 0, (Int32)m.Length); break; } if (buffer != null) mStream.Write(buffer, 0, buffer.Length); #endregion #region【将内存流一次写入网络流,异步写入】 mStream.Seek(0, SeekOrigin.Begin); _netStream.BeginWrite(mStream.GetBuffer(), 0, (Int32)mStream.Length, new AsyncCallback(AsyncCallbackWriteToNetStream), new WriteNetStreamASyncCallbackParam{ netStream = _netStream, packetHead = packet.PacketHead }); #endregion } /// <summary> /// 写入网络流异步回调函数 /// </summary> /// <param name="result"></param> private void AsyncCallbackWriteToNetStream(IAsyncResult result) { WriteNetStreamASyncCallbackParam p = (WriteNetStreamASyncCallbackParam)result.AsyncState; p.netStream.EndWrite(result); if (OnAfterSendPacket != null)//事件处理,如果注册了该事件,则执行回调函数 OnAfterSendPacket(p.packetHead); } } /// <summary> /// 写入网络流异步回调参数 /// </summary> sealed class WriteNetStreamASyncCallbackParam { /// <summary> /// 网络流 /// </summary> internal NetworkStream netStream; /// <summary> /// 包头 /// </summary> internal NetPacketHead packetHead; } }
时间关系我没有编写Udp异步传输子类
相关文章推荐
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--类设计1[使用IE浏览本页]
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--类设计2[使用IE浏览本页]
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--类设计1
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务3[聊天室]
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务3[聊天室][使用IE浏览本页]
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务4[聊天室]
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务1
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务1[使用IE浏览本页]
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务2
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务2[使用IE浏览本页]
- .Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--使用封装的网络服务4[聊天室][使用IE浏览本页]
- Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--前面6篇博文全部源代码下载地址
- Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--前面6篇博文全部源代码下载地址
- .NET(C#)基于Socket编程实现平行主机之间网络通讯有图片传输的Demo演示
- Android网络编程之Socket方式上传对象序列化文件(客户端)
- socket网络编程实现文件从服务器端到客户端的传输
- 23.编程实现序列化的Student(sno,sname)对象在网络上的传输
- .NET通过Socket实现平行主机之间网络通讯(含图片传输的Demo演示)
- java网络编程-利用datagramsocket和datagrampacket实现一台机器向另一台机器传文件
- .NET 插件系统框架设计(二) 使用对象序列化实现自定义配置文件管理