感觉这篇文章不错,转过来跟大家分享
原文作者:薛飞
原文链接:http://www.manew.com/thread-134935-1-1.html
原文出处:蛮牛
项目托管在码云上https://gitee.com/awnuxcvbn/Ganghood,如有未涉及到的代码,请下载或直接浏览
消息协议使用Protobuf,这里简单说一下Protobuf C#版使用,写类似如下的消息协议,然后生成C#代码
package Message;
message CToSChat
{
required int32 fromuid = 1;
required string fromuname = 2;
required int32 touid =3;
required string touname =4;
required string content = 5;
}
message SToCChat
{
required int32 fromuid = 1;
required string fromuname = 2;
required int32 touid =3;
required string touname =4;
required string content = 5;
}
生成proto的C#代码的批处理
@echo off
rem 查找文件
for /f "delims=" %%i in ('dir /b ".\*.proto"') do echo %%i
rem 转cpp for /f "delims=" %%i in ('dir /b/a "*.proto"') do protoc -I=. --cpp_out=. %%i
for /f "delims=" %%i in ('dir /b/a "*.proto"') do protogen -i:%%i -o:%%~ni.cs
pause
消息处理过程:
1、Socket接受数据缓存到队列
private List<byte> receiveCache = new List<byte>();
2、反序列化byte数组为protobuf消息体
Type protoType = ProtoDic.GetProtoTypeByProtoId(protoId);
object tos = ProtoBuf.Serializer.Deserialize(protoType, new MemoryStream(data));
3、分发消息体给监听者
object[] args = new object[1];
args[0] = tos;
MsgMgr.Instance.SendMsg(protoType.ToString(), args);
接下来主要看一下第2步中数据包处理的代码,把发送的消息按照 消息id+消息长度+消息内容 的格式进行拼接,用于处理粘包问题
这里仅粘贴主要代码NetCode
using System;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// 编码和解码
/// </summary>
public class NetCode
{
/// <summary>
/// 将数据编码 消息id+消息长度+消息内容
/// </summary>
/// <param name="protoId"></param>
/// <param name="proto"></param>
/// <returns></returns>
public static byte[] Encode(object proto)
{
Type type = proto.GetType();
int protoId = ProtoDic.GetProtoIdByProtoType(type);
MemoryStream ms = new MemoryStream();
ProtoBuf.Serializer.Serialize(ms, proto);
byte[] data = ms.ToArray();
ms.Dispose();
ms = new MemoryStream();
//整形占四个字节,所以声明一个+4+4+消息长度的数组
byte[] result = new byte[data.Length + 8];
//使用流将编码写二进制
BinaryWriter br = new BinaryWriter(ms);
br.Write(protoId);
br.Write(data.Length);
br.Write(data);
//将流中的内容复制到数组中
Buffer.BlockCopy(ms.ToArray(), 0, result, 0, (int)ms.Length);
br.Close();
ms.Close();
//Debug.LogWarning("编码消息id " + protoId + " 长度 " + result.Length + " 内容 " + GetDesc(result));
return result;
}
/// <summary>
/// 将数据解码
/// </summary>
/// <param name="cache">消息队列</param>
public static byte[] Decode(ref int protoId, ref List<byte> cache)
{
//首先要获取消息长度,整形4个字节,如果字节数不足4个字节
if (cache.Count < 4)
{
return null;
}
//读取数据
MemoryStream ms = new MemoryStream(cache.ToArray());
BinaryReader br = new BinaryReader(ms);
protoId = br.ReadInt32();
//消息长度
int len = br.ReadInt32();
//根据长度,判断内容是否传递完毕
if (len > ms.Length - ms.Position)
{
return null;
}
//获取数据
byte[] result = br.ReadBytes(len);
//Debug.LogWarning("解码消息id " + protoId + " 长度 " + len + " 内容 " + GetDesc(result));
//清空消息池
cache.Clear();
//将剩余没处理的消息存入消息池
cache.AddRange(br.ReadBytes((int)ms.Length - (int)ms.Position));
return result;
}
public static string GetDesc(byte[] bytes)
{
string str = "";
if (bytes == null)
return str;
for (int i = 0; i < bytes.Length; i++)
{
int b = (int)bytes[i];
str += b.ToString() + " ";
}
return str;
}
}
然后是第3步,消息的分发,贴一下主要代码
using System.Collections.Generic;
using UnityEngine;
//委托类型
public delegate void MsgDelegate(object[] args);
/// <summary>
/// 消息分发
/// </summary>
public class MsgMgr : MonoBehaviour
{
public static MsgMgr Instance;
//消息列表
public Dictionary< string, MsgDelegate> msgDic;
public MsgMgr()
{
Instance = this;
msgDic = new Dictionary<string, MsgDelegate>();
}
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="name"></param>
/// <param name="cb"></param>
public void AddListener(string name, MsgDelegate cb)
{
if (!msgDic.ContainsKey(name))
{
msgDic.Add(name, cb);
}
else
{
msgDic[name] += cb;
}
}
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="name"></param>
/// <param name="cb"></param>
public void RemoveListener(string name, MsgDelegate cb)
{
if (msgDic.ContainsKey(name))
{
msgDic[name] -= cb;
}
else
{
msgDic.Remove(name);
}
}
/// <summary>
/// 清除所有的监听者
/// </summary>
public void ClearAllListeners()
{
msgDic.Clear();
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="name"></param>
/// <param name="args"></param>
public void SendMsg(string name, params object[] args)
{
MsgDelegate cb;
if (msgDic.TryGetValue(name, out cb))
{
if (cb != null)
{
cb(args);
}
}
}
}