Unity3D & Java 基于 Protobuf 通信實現(xiàn)
最近研究Unity3D,,同時需要給游戲制定一套通信協(xié)議。因為本人是后端出生,,對C#的 Socket相關(guān)通信框架不太熟悉,,經(jīng)過幾天的學(xué)習(xí),終于搞定了,。在這里公布出來,,大家可以共同學(xué)習(xí),少走彎路,。
本文重點:演示怎么解析和發(fā)送協(xié)議,。
技術(shù)選型
- 服務(wù)端1
- Java7
- netty 4
- 客戶端2
- C#
- SuperSocket.ClientEngine https://clientengine./
它是從SuperSocket中分離出來的,不人性的是竟然沒使用教程
- 通信協(xié)議3
- Protobuf https://github.com/google/protobuf/
- 因為Protobuf 官方只支持 Java,,C++,,Pythone語言,C#需要第三方支持
- protobuf-csharp https://code.google.com/p/protobuf-csharp-port/
Protobuf 相關(guān)的使用,,請自行Gooogle,,后面的代碼會展示相關(guān)API, Goole打不開買一個代理一個月20RMB
開發(fā)前準備
協(xié)議解析,無論任何語言協(xié)議解析在通信中使用中都是必須的。要成功的解析協(xié)議,,必須先搞清楚協(xié)議是如何制定的,。
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; //重點解析在這里
//連接服務(wù)器
client.Connect ();
//構(gòu)造Message,,屬性Protobuf的人應(yīng)該都能看懂
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# 的實現(xiàn)和Protobuf 官方給的Java實現(xiàn)是一樣的
*/
public static void sendMessage(AsyncTcpSession client, Request request)
{
using(MemoryStream stream = new MemoryStream())
{
CodedOutputStream os = CodedOutputStream.CreateInstance(stream);
//一定要去看它的代碼實現(xiàn),,
os.WriteMessageNoTag(request);
/**
* WriteMessageNoTag 等價于 WriteVarint32, WriteByte(byte[])
* 也就是:變長消息頭 + 消息體
*/
os.Flush();
byte[] data = stream.ToArray();
client.Send ( new ArraySegment<byte>(data) );
}
}
/**
* 協(xié)議解析,把這里搞明白了,,就沒白看
*/
public static void OnDataReceive(Object sender, DataEventArgs e)
{
//DataEventArgs 里面有 byte[] Data是從協(xié)議層接收上來的字節(jié)數(shù)組,,需要程序端進行緩存
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, 在長度不夠時進行數(shù)組拷貝,到下一次在進行解析
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, 這里就可以用多線程進行協(xié)議分發(fā)
}catch(Exception exception)
{
Console.WriteLine(exception.Message);
}
}
else
{
/**
* 本次數(shù)據(jù)不夠長度,緩存進行下一次解析
*/
byte[] dest = new byte[8192];
int remainSize = buffer.Length - markReadIndex;
Array.Copy(buffer.Data, markReadIndex, dest, 0, remainSize);
/**
* 緩存未處理完的字節(jié)
*/
buffer.Data = dest;
buffer.Offset = 0;
buffer.Length = remainSize;
break;
}
}
}
后記
客戶端完整代碼打包:http://download.csdn.net/detail/zeus_9i/8748899
|