Unity3D & Java 基于 Protobuf 通信实现
最近研究Unity3D,同时需要给游戏制定一套通信协议。因为本人是后端出生,对C#的 Socket相关通信框架不太熟悉,经过几天的学习,终于搞定了。在这里公布出来,大家可以共同学习,少走弯路。
本文重点:演示怎么解析和发送协议。
技术选型
- 服务端1
- Java7
- netty 4
- 客户端2
- C#
- SuperSocket.ClientEngine https://clientengine.codeplex.com/
它是从SuperSocket中分离出来的,不人性的是竟然没使用教程
- 通信协议3
- Protobuf https://github.com/google/protobuf/
- 因为Protobuf 官方只支持 Java,C++,Pythone语言,C#需要第三方支持
- protobuf-csharp https://code.google.com/p/protobuf-csharp-port/
Protobuf 相关的使用,请自行Gooogle,后面的代码会展示相关API, Goole打不开买一个代理一个月20RMB
开发前准备
协议解析,无论任何语言协议解析在通信中使用中都是必须的。要成功的解析协议,必须先搞清楚协议是如何制定的。
Protobuf 是基于 变长消息头(length) + 消息体(body)
Proto生成
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
- Unity3D Java 基于 Protobuf 通信实现
- 技术选型
- 开发前准备
- Proto生成
- 客户端代码
- 后记
时间: 2024-10-12 23:34:48