我的unity端用的是protobuf-net框架,Java服务端是io-game框架,他是基于netty的。
c#的byte和Java byte不太一样,但是他们的二进制是完全一样的。
不需要担心。
但是我们一般在tcp发送消息的时候,头部会带一个int代表protobuf的序列化长度。但是c#和Java的int二进制是颠倒的,比如:30
java中是00000000 00000000 00000000 00011110
而c#中是00011110 00000000 00000000 00000000
这好像叫大小端问题。
只需要在c#代码中使用Array.Reverse(b);倒转即可
combine是把两个数组结合。一起发去服务端。服务端根据头部的int,判断数据长度。
如果:你想要做一个分布式大型的或者单体的服务端。io-game是一个不错的选择。比之netty要简单许多。
我的客户端代码:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using ProtoBuf;
using System.IO;
using System.Text;
namespace code.socket {
public class SocketClient {
//客户端socket
private Socket clientSocket;
//服务器IP和端口
public string ServerIP;
public int ServerPort;
//收消息的线程
private Thread acceptMsgThread;
//构造函数
public SocketClient(string _serverIP, int _serverPort) {
ServerIP = _serverIP;
ServerPort = _serverPort;
}
/// <summary>
/// 连接服务器
/// </summary>
/// <returns></returns>
public bool ConnectServer() {
if (ServerIP == null || ServerPort == 0) {
return false;
}
try {
//初始化socket对象
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//初始化地址和端口号
IPAddress serverIp = IPAddress.Parse(ServerIP);
IPEndPoint endPoint = new IPEndPoint(serverIp, ServerPort);
//连接服务器
clientSocket.Connect(endPoint);
//新建线程,接受服务端下发的消息
acceptMsgThread = new Thread(AcceptMsgs);
acceptMsgThread.IsBackground = true;
acceptMsgThread.Start();
return true;
} catch (Exception e) {
Debug.Log("出现异常连接失败:" + e.Message);
return false;
}
}
/// <summary>
/// 收数据。
/// </summary>
private void AcceptMsgs() {
try {
while (true) {
byte[] buffer = new byte[1024];
int dataLength = clientSocket.Receive(buffer);
int available = clientSocket.Available;
if (dataLength == 0) {
//长度为0表示没接受到内容。断开。
break;
}
string strMsg = Encoding.UTF8.GetString(buffer, 0, dataLength);
Console.WriteLine("客户端接受到消息:" + strMsg);
}
} catch (Exception e) { }
}
/// <summary>
/// 发送文本消息
/// </summary>
/// <param name="msg"></param>
public bool SubmitMsgToServer(byte[] bytes) {
if (clientSocket == null || !clientSocket.Connected) {
return false;
}
try {
// byte[] buffer = Encoding.UTF8.GetBytes(msg);
//发送给服务端
clientSocket.Send(bytes);
return true;
} catch (Exception e) {
Console.WriteLine("客户端发送数据发生异常:" + e.ToString());
return false;
}
}
}
public class CmdMgr {
//获取cmd
public static int getCmd(int merge) {
return merge >> 16;
}
//获取subCmd
public static int getSubCmd(int merge) {
return merge & 0xFFFF;
}
//获取mergeCmd
public static int getMergeCmd(int cmd, int subCmd) {
return (cmd << 16) + subCmd;
}
}
public class ProtobufHelper {
/// <summary>
/// 将数组a,b进行合并
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
public static byte[] Combine2(byte[] a, byte[] b) {
byte[] c = new byte[a.Length + b.Length];
Array.Copy(a, 0, c, 0, a.Length);
Array.Copy(b, 0, c, a.Length, b.Length);
// Array.Reverse(c);
return c;
}
//对象序列化成序列化成文件
public static string Serialize<T>(T t) {
//MemoryStream继承了IDisposable接口,所以才能被using自动释放
//using 的作用为使用完之后就释放流对象
using (MemoryStream ms = new MemoryStream()) {
//使用protobuf程序集内置方法将对象t序列化到ms中
Serializer.Serialize<T>(ms, t);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
public static byte[] SerializeToByte<T>(T t) {
//MemoryStream继承了IDisposable接口,所以才能被using自动释放
//using 的作用为使用完之后就释放流对象
using (MemoryStream ms = new MemoryStream()) {
//使用protobuf程序集内置方法将对象t序列化到ms中
Serializer.Serialize<T>(ms, t);
return ms.ToArray();
}
}
public static string byteToString(byte[] bytes) {
string strResult = "";
for (var i = 0; i < bytes.Length; i++) {
string strTemp = System.Convert.ToString(bytes[i], 2);
strTemp = strTemp.Insert(0, new string('0', 8 - strTemp.Length));
strResult += strTemp + " ";
}
return strResult;
}
/// <summary>
/// 将字节数组反序列化为对象实例——ProtoBuf
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="arr">字节数组</param>
/// <returns></returns>
public static T DeserializeFromByteAry_PB<T>(byte[] arr) {
using (MemoryStream ms = new MemoryStream(arr))
return ProtoBuf.Serializer.Deserialize<T>(ms);
}
//根据字符串反序列化成对象
public static T DeSerialize<T>(string content) {
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(content))) {
T t = Serializer.Deserialize<T>(ms);
return t;
}
}
}
}
using System.Collections.Generic;
using ProtoBuf;
namespace code.socket.pojo {
[ProtoContract]
public class Message {
[ProtoMember(1)]
public int cmdCode { get; set; }
[ProtoMember(2)]
public int protocolSwitch { get; set; }
[ProtoMember(3)]
public int cmdMerge { get; set; }
[ProtoMember(4)]
public int responseStatus { get; set; }
[ProtoMember(5)]
public string validMsg { get; set; }
[ProtoMember(6)]
public byte[] data { get; set; }
[ProtoMember(7)]
public int msgId { get; set; }
}
[ProtoContract]
public class Test01 {
[ProtoMember(1)]
public string name;
[ProtoMember(2)]
public int age;
[ProtoMember(3)]
public List<string> list;
}
[ProtoContract]
class Person {
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Age { get; set; }
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Text;
using code.socket;
using code.socket.pojo;
using UnityEngine;
public class Socket01 : MonoBehaviour {
void Start() {
SocketClient client = new SocketClient("127.0.0.1",10100);
client.ConnectServer();
Test01 hellorep = new Test01() {
name = "张江南嘿嘿嘿"
};
byte[] toByte = ProtobufHelper.SerializeToByte(hellorep);
string byteToString = ProtobufHelper.byteToString(toByte);
Debug.Log(byteToString);
StringBuilder builder1 = new StringBuilder();
for (var i = 0; i < toByte.Length; i++) {
builder1.Append(toByte[i] + " ");
}
Debug.Log(builder1.ToString());
Message message = new Message() {
cmdCode = 1,
cmdMerge = CmdMgr.getMergeCmd(1,0),
protocolSwitch = 1,
data = toByte
};
byte[] serializeToByte = ProtobufHelper.SerializeToByte(message);
Debug.Log(ProtobufHelper.byteToString(serializeToByte));
StringBuilder builder = new StringBuilder();
for (var i = 0; i < serializeToByte.Length; i++) {
builder.Append(serializeToByte[i] + " ");
}
Debug.Log(builder.ToString());
byte[] b = BitConverter.GetBytes(serializeToByte.Length);
Array.Reverse(b);
byte[] bytes = ProtobufHelper.Combine2(b, serializeToByte);
Debug.Log(ProtobufHelper.byteToString(bytes));
client.SubmitMsgToServer(bytes);
// Person person1 = new Person(){ Name = "小明同学", Age = 18 };
// //序列化
// string testInfo = ProtobufHelper.Serialize<Person>(person1);
// print("testInfo"+ testInfo);
// //反序列化
// Person person2 = ProtobufHelper.DeSerialize<Person>(testInfo);
// print("person2" + person2.Name +" " +person2.Age);
}
void Update() { }
}
代码只是demo,我只测试了发送。接收应该有问题的。
Socket01是挂载在游戏中启动的类。
c#端需要引入protobuf-net的dll文件。需要的话可以找我要。
服务端的话是io-game可以搜一下,去看文档。