Unity3d 极简网络游戏之消息协议和消息处理

感觉这篇文章不错,转过来跟大家分享

原文作者:薛飞

原文链接: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);
            }
        }
    }

}

猜你喜欢

转载自blog.csdn.net/q764424567/article/details/80596748