最近研究Unity3D,同时需要给游戏制定一套通信协议。因为本人是后端出生,对C#的 Socket相关通信框架不太熟悉,经过几天的学习,终于搞定了。在这里公布出来,大家可以共同学习,少走弯路。
本文重点:演示怎么解析和发送协议。
Protobuf 相关的使用,请自行Gooogle,后面的代码会展示相关API, Goole打不开买一个代理一个月20RMB
协议解析,无论任何语言协议解析在通信中使用中都是必须的。要成功的解析协议,必须先搞清楚协议是如何制定的。
Protobuf 是基于 变长消息头(length) + 消息体(body)
message Request { required string command = 1; required string data = 2;}message Response { required string command = 1; required string data = 2;}
至于怎么生成,我这里就不给出详细方式了,通过Protobuf资料,有详细说明。
public static DataEventArgs buffer = new DataEventArgs(); public static int count = 0; public static void Main (string[] args) { buffer.Data = new byte[8192]; //8 KB IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8001); //supersocket clientengine AsyncTcpSession client = new AsyncTcpSession (endPoint); client.Connected += OnConnected; client.DataReceived += OnDataReceive; //重点解析在这里 //连接服务器 client.Connect (); //构造Message,属性Protobuf的人应该都能看懂 Request.Builder builder = Request.CreateBuilder (); builder.SetCommand ("110"); builder.SetData ("1231231232131"); Request request = builder.BuildPartial (); sendMessage (client, request); Thread.Sleep (30000); } public static void OnConnected(Object sender, EventArgs e) { Console.WriteLine ("connect to server finish."); } /** * 这里 C# 的实现和Protobuf 官方给的Java实现是一样的 */ public static void sendMessage(AsyncTcpSession client, Request request) { using(MemoryStream stream = new MemoryStream()) { CodedOutputStream os = CodedOutputStream.CreateInstance(stream); //一定要去看它的代码实现, os.WriteMessageNoTag(request); /** * WriteMessageNoTag 等价于 WriteVarint32, WriteByte(byte[]) * 也就是:变长消息头 + 消息体 */ os.Flush(); byte[] data = stream.ToArray(); client.Send ( new ArraySegment<byte>(data) ); } } /** * 协议解析,把这里搞明白了,就没白看 */ public static void OnDataReceive(Object sender, DataEventArgs e) { //DataEventArgs 里面有 byte[] Data是从协议层接收上来的字节数组,需要程序端进行缓存 Console.WriteLine ("buff length: {0}, offset: {1}", e.Length, e.Offset); if( e.Length <= 0 ) { return; } //把收取上来的自己全部缓存到本地 buffer 中 Array.Copy (e.Data, 0, buffer.Data, buffer.Length, e.Length); buffer.Length += e.Length; CodedInputStream stream = CodedInputStream.CreateInstance (buffer.Data); while ( !stream.IsAtEnd ) { //标记读取的Position, 在长度不够时进行数组拷贝,到下一次在进行解析 int markReadIndex = (int)stream.Position; //Protobuf 变长头, 也就是消息长度 int varint32 = (int)stream.ReadRawVarint32(); if( varint32 <= (buffer.Length - (int)stream.Position) ) { try { byte[] body = stream.ReadRawBytes (varint32); Response response = Response.ParseFrom (body); Console.WriteLine("Response: " + response.ToString() + ", count: " + (++count)); //dispatcher message, 这里就可以用多线程进行协议分发 }catch(Exception exception) { Console.WriteLine(exception.Message); } } else { /** * 本次数据不够长度,缓存进行下一次解析 */ byte[] dest = new byte[8192]; int remainSize = buffer.Length - markReadIndex; Array.Copy(buffer.Data, markReadIndex, dest, 0, remainSize); /** * 缓存未处理完的字节 */ buffer.Data = dest; buffer.Offset = 0; buffer.Length = remainSize; break; } } }
客户端完整代码打包:http://download.csdn.net/detail/zeus_9i/8748899
联系客服