前言
在实际运用中,服务器和客户端都需要遵从某种协议,浏览器里有http协议,各路由器也有自己的协议,我们自己编写的服务器和客户端,也往往加入自己某种理解方式的协议,而NetworkSocket的TcpServerEx是使用DataEventExArgs来描述它的协议,如果我们写客户端来连接其它已有的服务器,可能需要扩展NetworkSocket了。假设现有的服务器用的协议是包长[4byte] + 实体数据[N个byte],那么我们就可以如下来实现类似的服务器和和可以与之正常通讯的客户端。
编写协议描述
NetworkSocket任何协议的描述都是从DataEventArgs类派生,下面我们来实现这个协议描述MyDataEventArgs,新建类库工程MyTcpLib,引用NetworkSocket.dll,添加MyDataEventArgs类,并派生于DataEvnetArgs,我们如下来实现这个协议。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NetWorkSocket; namespace MyTcpLib { /// <summary> /// Tcp数据包 /// </summary> public class MyDataEventArgs : DataEventArgs { /// <summary> /// 重写ToByteArray方法 /// </summary> /// <returns></returns> public override byte[] ToByteArray() { // 包长 int totalLenth = 4; if (this.Binary != null && this.Binary.Buffer != null) { // 4字节 + 实体的长度 totalLenth = totalLenth + this.Binary.Buffer.Length; } ByteBuilder builder = new ByteBuilder(totalLenth); // 添加包长度 builder.Add(BitConverter.GetBytes(totalLenth)); if (this.Binary != null) { // 添加实体数据 builder.Add(this.Binary.Buffer); } return builder.GetBaseBuffer(); } } }
编写服务器
在MyTcpLib工程添加MyTcpServer类,派生于TcpServerBase<MyDataEventArgs>,重写基类的AnalysisRecvData方法,这个方法是用来描述怎么解析数据包的。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NetWorkSocket; namespace MyTcpLib { /// <summary> /// Tcp服务器 /// </summary> public class MyTcpServer : TcpServerBase<MyDataEventArgs> { // 重写解析数据的方法 protected override MyDataEventArgs AnalysisRecvData(ByteBuilder recvBuilder) { // 包长至少为4 里面才可能有实体数据,否则就是未完整的包,等待下回再解析 if (recvBuilder.Count >= 4) { // 前4四字为包长 int packetLength = recvBuilder.GetInt32(0); // 包长小于等于缓冲区数据 才是完整的包 if (packetLength <= recvBuilder.Count) { // 清除前面的4字节 packetLength = recvBuilder.ReadInt32(); // 实体数据的长度 int entityDataLenth = packetLength - 4; // 取出实体数据 byte[] entityData = recvBuilder.ReadRange(entityDataLenth); MyDataEventArgs e = new MyDataEventArgs() { Binary = new Binary(entityData) }; return e; } } // 返回null 表示没有数据 return null; } } }
编写客户端
同里,添加MyTcpClient类,派生于TcpClientBase<MyDataEvnetArgs>,重写基类的AnalysisRecvData方法,里面的代码和服务的重写代码完成一样,复制过来就可以
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NetWorkSocket; namespace MyTcpLib { /// <summary> /// Tcp客户端 /// </summary> public class MyTcpClient : TcpClientBase<MyDataEventArgs> { // 重写解析数据的方法 protected override MyDataEventArgs AnalysisRecvData(ByteBuilder recvBuilder) { // 包长至少为4 里面才可能有实体数据,否则就是未完整的包,等待下回再解析 if (recvBuilder.Count >= 4) { // 前4四字为包长 int packetLength = recvBuilder.GetInt32(0); // 包长小于等于缓冲区数据 才是完整的包 if (packetLength <= recvBuilder.Count) { // 清除前面的4字节 packetLength = recvBuilder.ReadInt32(); // 实体数据的长度 int entityDataLenth = packetLength - 4; // 取出实体数据 byte[] entityDaata = recvBuilder.ReadRange(entityDataLenth); MyDataEventArgs e = new MyDataEventArgs() { Binary = new Binary(entityDaata) }; return e; } } // 返回null 表示没有数据 return null; } } }
测试结果
以上就可以得到属于我们自己协议的MyTcpLib.dll类库,新建服务器工程和客户端工程,分别调用MyTcpServer和MyTcpClient,测试一下成果
服务器代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NetWorkSocket; using System.Net; using MyTcpLib; namespace ServerBase { class Program { // 服务实例 static MyTcpServer server = new MyTcpServer(); static void Main(string[] args) { // 绑定接收数据事件 server.OnRecvComplete += new EventHandler<MyDataEventArgs>(server_OnRecvComplete); // 绑定客户端连接事件 server.OnConnect += new EventHandler(server_OnConnect); // 启动服务 server.StartListen(new IPEndPoint(IPAddress.Any, 8181)); while (true) { Console.ReadLine(); } } /// <summary> /// 客户端连接事件 /// </summary> /// <param name="sender">客户端</param> /// <param name="e">事件</param> static void server_OnConnect(object sender, EventArgs e) { var client = sender as SocketAsync<MyDataEventArgs>; Console.WriteLine("{0}连接进来...当前连接数:{1}", client.RemoteEndPoint, server.AliveClients.Count); } /// <summary> /// 接收数据完成事件 /// </summary> /// <param name="sender">客户端</param> /// <param name="e"></param> static void server_OnRecvComplete(object sender, MyDataEventArgs e) { // sender就是当前发来数据的客户端 var client = sender as SocketAsync<MyDataEventArgs>; // 将数据转为文本 string msg = Encoding.UTF8.GetString(e.Binary.Buffer); // 输出消息 Console.WriteLine("{0}: {1}", client.RemoteEndPoint, msg); msg = "这是来自服务器的消息..."; var data = Encoding.UTF8.GetBytes(msg); e.Binary = new Binary(data); client.Send(e.ToByteArray()); } } }
客户端代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NetWorkSocket; using System.Net; using MyTcpLib; namespace ClientBase { class Program { // 客户端实例 static MyTcpClient client = new MyTcpClient(); static void Main(string[] args) { // 绑定接收数据事件 client.OnRecvComplete += new EventHandler<MyDataEventArgs>(client_OnRecvComplete); // 连接到服务器 bool state = client.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8181)); if (state == true) { while (true) { string msg = Console.ReadLine(); var data = Encoding.UTF8.GetBytes(msg); MyDataEventArgs e = new MyDataEventArgs() { Binary = new Binary(data) }; client.Send(e.ToByteArray()); } } } /// <summary> /// 收到服务器发来数据事件 /// </summary> /// <param name="sender">本客户端</param> /// <param name="e">事件</param> static void client_OnRecvComplete(object sender, MyDataEventArgs e) { // 将数据转为文本 string msg = Encoding.UTF8.GetString(e.Binary.Buffer); // 输出消息 Console.WriteLine("收到: {0}", msg); } } }