在TCP通讯中使用协议分析器和自定义协议对象
在处理TCP数据的时候我们需要考虑一个粘包的问题,所谓的粘包就是本次接收的数据不一定完整对应对方发送的数据.对方发送的一次数据有可能需要接 收多次才能完成,实际要处理的情况要复习一点;为了解决点包问题所以必须要制订数据分析协议来处理,常用的解决方法有两种:一种是基于结束符的方式,而另 一种则是在消息头通过一个4字节存储消息大小.
分包注意细节
虽然制定处理粘包的方法,但这两种方法在处理上还是要注意几种情况,以下通过一个图来表达几种情况的处理.
其实最主要关心的是就是分隔符或头描述的内容分别存放在两次receive的数据中.
实现一个简单的协议分析器
组件提供以上两种分包处理方式,基础类分别是HeadSizeOfPackage和EofDataOfPackage;通过继续以上两个类就可以简 单地实现对象协议的发送和接收;如果以上两者不适合的情况可以从Package派生一个新的协议分析类来满足实际情况的需要. 接下来通过继承 HeadSizeOfPackage实现一个简单的对象协议分析器,相关Package实现如下:
12345678910111213141516171819202122232425262728293031323334publicclassHeadSizePackage:Beetle.HeadSizeOfPackage{publicHeadSizePackage(Beetle.TcpChannel channel) :base(channel) { }privatestaticDictionary<string, Smark.Core.InstanceHandler> mTypes =newDictionary<string, Smark.Core.InstanceHandler>(256);publicstaticvoidLoadAssembly(System.Reflection.Assembly assembly){foreach(Type typeinassembly.GetTypes()){if(type.GetInterface("Beetle.IMessage") !=null&& type.IsClass){mTypes[type.Name] =newSmark.Core.InstanceHandler(type);}}}protectedoverrideBeetle.IMessage ReadMessageByType(Beetle.BufferReader reader,outobjecttypeTag){typeTag = reader.ReadShortString();Smark.Core.InstanceHandler handler;if(mTypes.TryGetValue((string)typeTag,outhandler)){return(Beetle.IMessage)handler.Instance();}returnnull;}protectedoverridevoidWriteMessageType(Beetle.IMessage msg, Beetle.BufferWriter writer){writer.WriteShortString(msg.GetType().Name);}}继承HeadSizeOfPackage后主要重写两个方法,分别是ReadMessageByType从BufferReader中读取对消息名 称并返回具体的消息对象,WriteMessageType则是写入消息名称.两个方法的主要作用是写入消息类型标记和根据标记返回消息对象.制定完成协 议分析后要做的事情就是制定对象协议,以下是一个简单注册协议实现:
123456789101112131415161718classRegister : Beetle.IMessage{publicstringName;publicstringEMail;publicDateTime ResponseTime;publicvoidLoad(Beetle.BufferReader reader){Name = reader.ReadString();EMail = reader.ReadString();ResponseTime = reader.ReadDate();}publicvoidSave(Beetle.BufferWriter writer){writer.Write(Name);writer.Write(EMail);writer.Write(ResponseTime);}}构建对象协义的TCP服务端
在Beetle中构建基于对象协议的TCP服务端也是一件非常简单的事情,只需要Beetle.ServerBase<T>即可,而泛型参则是具体的协议分析器.
1234567891011121314151617181920212223242526classProgram:Beetle.ServerBase<Beetle.Packages.HeadSizePackage>{protectedoverridevoidOnConnected(objectsender, Beetle.ChannelEventArgs e){base.OnConnected(sender, e);Console.WriteLine("{0} connected", e.Channel.EndPoint);}protectedoverridevoidOnDisposed(objectsender, Beetle.ChannelDisposedEventArgs e){base.OnDisposed(sender, e);Console.WriteLine("{0} disposed", e.Channel.EndPoint);}protectedoverridevoidOnError(objectsender, Beetle.ChannelErrorEventArgs e){base.OnError(sender, e);Console.WriteLine("{0} error {1}", e.Channel.EndPoint,e.Exception.Message);}protectedoverridevoidOnMessageReceive(Beetle.PacketRecieveMessagerArgs e){Register reg = (Register)e.Message;reg.ResponseTime = DateTime.Now;Console.WriteLine("Name:{0} EMail:{1}", reg.Name, reg.EMail);e.Channel.Send(reg);}和构建普通TCP服务一样,重写相关处理过程方法即可,不过其中一个方法有所不同就是OnMessageReceive,该对象主要包括接收的消息 和对应的Socket通道TcpChannel.在之前只定义了一个Register对象消息,在这里就获取相关消息并把ResponseTime设置成 当前时间后发还给对应的客户端.
构建客户端进行消息交互
客户端的创建则使用TcpServer.CreateClient<T>方法来构建,泛型参是对应协议分析器,具体代码如下:
1234channel = Beetle.TcpServer.CreateClient<Beetle.Packages.HeadSizePackage>(txtIPAddress.Text, 9450,OnReceive);channel.ChannelDisposed += OnDisposed;channel.ChannelError += OnError;channel.BeginReceive();
123456789101112131415161718192021privatevoidOnReceive(Beetle.PacketRecieveMessagerArgs e){Register reg = (Register)e.Message;Invoke(newAction<Register>(r => {txtREMail.Text = r.EMail;txtRName.Text = r.Name;txtResponseTime.Text = r.ResponseTime.ToString();}), reg);}privatevoidOnDisposed(objectsender, Beetle.ChannelEventArgs e){Invoke(newAction<Beetle.ChannelEventArgs>(s => {txtStatus.Text ="disconnect!";}), e);}privatevoidOnError(objectsender, Beetle.ChannelErrorEventArgs e){Invoke(newAction<Beetle.ChannelErrorEventArgs>(r => {txtStatus.Text = r.Exception.Message;}), e);}构建连接后绑事相关事件,并进入数据接收模式即可.创建连接完成后就可以进行对象协议发送
1234Register reg =newRegister();reg.Name = txtName.Text;reg.EMail = txtEMail.Text;channel.Send(reg);运行效果


浙公网安备 33010602011771号