69.Protobuf协议使用

69.消息处理-第三方协议生成工具Protobuf-协议使用


69.1 知识点

序列化存储为本地文件

  • 主要使用
    • 生成的类中的 WriteTo 方法
    • 文件流 FileStream 对象
TestMsg testMsg1 = new TestMsg();
testMsg1.ListInt.Add(1);
testMsg1.TestBool = false;
testMsg1.TestD = 5.5;
testMsg1.TestInt32 = 99;
testMsg1.TestMap.Add(1, "韬老狮");
testMsg1.TestMsg2 = new TestMsg2();
testMsg1.TestMsg2.TestInt32 = 88;
testMsg1.TestMsg3 = new TestMsg.Types.TestMsg3();
testMsg1.TestMsg3.TestInt32 = 66;

testMsg1.TestHeart = new GameSystemTest.HeartMsg();
testMsg1.TestHeart.Time = 7777;

print(Application.persistentDataPath);
using (FileStream fileStream = File.Create(Application.persistentDataPath + "/TestMsg.tao"))
{
    testMsg1.WriteTo(fileStream);
}

反序列化本地文件

  • 主要使用
    • 生成的类中的 Parser.ParseFrom 方法
    • 文件流 FileStream 对象
using (FileStream fileStream = File.OpenRead(Application.persistentDataPath + "/TestMsg.tao"))
{
    TestMsg testMsg2 = null;
    testMsg2 = TestMsg.Parser.ParseFrom(fileStream);
    print(testMsg2.TestMap[1]);
    print(testMsg2.ListInt[0]);
    print(testMsg2.TestD);
    print(testMsg2.TestMsg2.TestInt32);
    print(testMsg2.TestMsg3.TestInt32);
    print(testMsg2.TestHeart.Time);
}

得到序列化后的字节数组

  • 主要使用
    • 生成的类中的 WriteTo 方法
    • 内存流 MemoryStream 对象
byte[] bytes = null;
using (MemoryStream memoryStream = new MemoryStream())
{
    testMsg1.WriteTo(memoryStream);
    bytes = memoryStream.ToArray();
    print("字节数组长度" + bytes.Length);
}

从字节数组反序列化

  • 主要使用
    • 生成的类中的 Parser.ParseFrom 方法
    • 内存流 MemoryStream 对象
using (MemoryStream memoryStream = new MemoryStream(bytes))
{
    print("内存流当中反序列化的内容");
    TestMsg testMsg2 = TestMsg.Parser.ParseFrom(memoryStream);
    print(testMsg2.TestMap[1]);
    print(testMsg2.ListInt[0]);
    print(testMsg2.TestD);
    print(testMsg2.TestMsg2.TestInt32);
    print(testMsg2.TestMsg3.TestInt32);
    print(testMsg2.TestHeart.Time);
}

总结

  • Protobuf 的序列化和反序列化都要通过流对象来进行处理。
  • 如果是进行本地存储,则可以使用文件流。
  • 如果是进行网络传输,则可以使用内存流获取字节数组。

69.2 知识点代码

using GamePlayerTest;
using Google.Protobuf;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class Lesson69_消息处理_第三方协议生成工具Protobuf_协议使用 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 序列化存储为本地文件

        //主要使用
        //1.生成的类中的 WriteTo方法
        //2.文件流FileStream对象

        TestMsg testMsg1 = new TestMsg();
        testMsg1.ListInt.Add(1);
        testMsg1.TestBool = false;
        testMsg1.TestD = 5.5;
        testMsg1.TestInt32 = 99;
        testMsg1.TestMap.Add(1, "韬老狮");
        testMsg1.TestMsg2 = new TestMsg2();
        testMsg1.TestMsg2.TestInt32 = 88;
        testMsg1.TestMsg3 = new TestMsg.Types.TestMsg3();
        testMsg1.TestMsg3.TestInt32 = 66;

        testMsg1.TestHeart = new GameSystemTest.HeartMsg();
        testMsg1.TestHeart.Time = 7777;

        print(Application.persistentDataPath);
        using (FileStream fileStream = File.Create(Application.persistentDataPath + "/TestMsg.tao"))
        {
            testMsg1.WriteTo(fileStream);
        }

        #endregion

        #region 知识点二 反序列化本地文件

        //主要使用
        //1.生成的类中的 Parser.ParseFrom方法
        //2.文件流FileStream对象

        using (FileStream fileStream = File.OpenRead(Application.persistentDataPath + "/TestMsg.tao"))
        {
            TestMsg testMsg2 = null;
            testMsg2 = TestMsg.Parser.ParseFrom(fileStream);
            print(testMsg2.TestMap[1]);
            print(testMsg2.ListInt[0]);
            print(testMsg2.TestD);
            print(testMsg2.TestMsg2.TestInt32);
            print(testMsg2.TestMsg3.TestInt32);
            print(testMsg2.TestHeart.Time);
        }

        #endregion

        #region 知识点三 得到序列化后的字节数组

        //主要使用
        //1.生成的类中的 WriteTo方法
        //2.内存流MemoryStream对象

        byte[] bytes = null;
        using (MemoryStream memoryStream = new MemoryStream())
        {
            testMsg1.WriteTo(memoryStream);
            bytes = memoryStream.ToArray();
            print("字节数组长度" + bytes.Length);
        }

        #endregion

        #region 知识点四 从字节数组反序列化

        //主要使用
        //1.生成的类中的 Parser.ParseFrom方法
        //2.内存流MemoryStream对象

        using (MemoryStream memoryStream = new MemoryStream(bytes))
        {
            print("内存流当中反序列化的内容");
            TestMsg testMsg2 = TestMsg.Parser.ParseFrom(memoryStream);
            print(testMsg2.TestMap[1]);
            print(testMsg2.ListInt[0]);
            print(testMsg2.TestD);
            print(testMsg2.TestMsg2.TestInt32);
            print(testMsg2.TestMsg3.TestInt32);
            print(testMsg2.TestHeart.Time);
        }

        #endregion

        #region 总结

        //Protobuf的 序列化和反序列化都要通过
        //流对象来进行处理
        //如果是进行本地存储 则可以使用文件流
        //如果是进行网络传输 则可以使用内存流获取字节数组

        #endregion
    }
}

69.3 练习题

请封装一个工具类,用于快速的对Protobuf生成的内容,进行序列化和反序列化

创建静态网络工具类

public static class NetTool
{
}

序列化消息对象,可以自己写流,也可以用封装好的方法

//序列化Protobuf生成的对象
public static byte[] GetProtoBytes(IMessage msg)
{
    //拓展方法、里氏替换、接口 这些知识点 都在 C#相关的内容当中

    //基础写法 基于上节课学习的知识点
    //byte[] bytes = null;
    //using (MemoryStream ms = new MemoryStream())
    //{
    //    msg.WriteTo(ms);
    //    bytes = ms.ToArray();
    //}
    //return bytes;

    //通过该拓展方法 就可以直接获取对应对象的 字节数组了
    return msg.ToByteArray();
}

反序列化消息对象,使用反射

/// <summary>
/// 反序列化字节数组为Protobuf相关的对象
/// </summary>
/// <typeparam name="T">想要获取的消息类型</typeparam>
/// <param name="bytes">对应的字节数组 用于反序列化</param>
/// <returns></returns>
public static T GetProtoMsg<T>(byte[] bytes) where T : class, IMessage
{
    //泛型 C#进阶
    //反射 C#进阶
    //得到对应消息的类型 通过反射得到内部的静态成员 然后得到其中的 对应方法
    //进行反序列化
    Type type = typeof(T);
    //通过反射 得到对应的 静态成员属性对象
    PropertyInfo propertyInfo = type.GetProperty("Parser");
    object parserObj = propertyInfo.GetValue(null, null);
    //已经得到了对象 那么可以得到该对象中的 对应方法 
    Type parserType = parserObj.GetType();
    //这是指定得到某一个重载函数
    MethodInfo methodInfo = parserType.GetMethod("ParseFrom", new Type[] { typeof(byte[]) });
    //调用对应的方法 反序列化为指定的对象
    object msg = methodInfo.Invoke(parserObj, new object[] { bytes });
    return msg as T;
}

进行测试

TestMsg testMsg1 = new TestMsg();
testMsg1.ListInt.Add(1);
testMsg1.TestBool = false;
testMsg1.TestD = 5.5;
testMsg1.TestInt32 = 99;
testMsg1.TestMap.Add(1, "韬老狮");
testMsg1.TestMsg2 = new TestMsg2();
testMsg1.TestMsg2.TestInt32 = 88;
testMsg1.TestMsg3 = new TestMsg.Types.TestMsg3();
testMsg1.TestMsg3.TestInt32 = 66;

testMsg1.TestHeart = new GameSystemTest.HeartMsg();
testMsg1.TestHeart.Time = 7777;


print("练习题打印相关");
//封装的 序列化方法 
byte[] bytes = NetTool.GetProtoBytes(testMsg1);
//封装的 反序列化方法
TestMsg testMsg2 = NetTool.GetProtoMsg<TestMsg>(bytes);
print(testMsg2.TestMap[1]);
print(testMsg2.ListInt[0]);
print(testMsg2.TestD);
print(testMsg2.TestMsg2.TestInt32);
print(testMsg2.TestMsg3.TestInt32);
print(testMsg2.TestHeart.Time);

69.4 练习题代码

NetTool

using Google.Protobuf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;

public static class NetTool
{
    //序列化Protobuf生成的对象
    public static byte[] GetProtoBytes(IMessage msg)
    {
        //拓展方法、里氏替换、接口 这些知识点 都在 C#相关的内容当中

        //基础写法 基于上节课学习的知识点
        //byte[] bytes = null;
        //using (MemoryStream ms = new MemoryStream())
        //{
        //    msg.WriteTo(ms);
        //    bytes = ms.ToArray();
        //}
        //return bytes;

        //通过该拓展方法 就可以直接获取对应对象的 字节数组了
        return msg.ToByteArray();
    }

    /// <summary>
    /// 反序列化字节数组为Protobuf相关的对象
    /// </summary>
    /// <typeparam name="T">想要获取的消息类型</typeparam>
    /// <param name="bytes">对应的字节数组 用于反序列化</param>
    /// <returns></returns>
    public static T GetProtoMsg<T>(byte[] bytes) where T : class, IMessage
    {
        //泛型 C#进阶
        //反射 C#进阶
        //得到对应消息的类型 通过反射得到内部的静态成员 然后得到其中的 对应方法
        //进行反序列化
        Type type = typeof(T);
        //通过反射 得到对应的 静态成员属性对象
        PropertyInfo propertyInfo = type.GetProperty("Parser");
        object parserObj = propertyInfo.GetValue(null, null);
        //已经得到了对象 那么可以得到该对象中的 对应方法 
        Type parserType = parserObj.GetType();
        //这是指定得到某一个重载函数
        MethodInfo methodInfo = parserType.GetMethod("ParseFrom", new Type[] { typeof(byte[]) });
        //调用对应的方法 反序列化为指定的对象
        object msg = methodInfo.Invoke(parserObj, new object[] { bytes });
        return msg as T;
    }
}

Lesson69_练习题

using GamePlayerTest;
using Google.Protobuf;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class Lesson69_练习题 : MonoBehaviour
{
    void Start()
    {
        TestMsg testMsg1 = new TestMsg();
        testMsg1.ListInt.Add(1);
        testMsg1.TestBool = false;
        testMsg1.TestD = 5.5;
        testMsg1.TestInt32 = 99;
        testMsg1.TestMap.Add(1, "韬老狮");
        testMsg1.TestMsg2 = new TestMsg2();
        testMsg1.TestMsg2.TestInt32 = 88;
        testMsg1.TestMsg3 = new TestMsg.Types.TestMsg3();
        testMsg1.TestMsg3.TestInt32 = 66;

        testMsg1.TestHeart = new GameSystemTest.HeartMsg();
        testMsg1.TestHeart.Time = 7777;


        print("练习题打印相关");
        //封装的 序列化方法 
        byte[] bytes = NetTool.GetProtoBytes(testMsg1);
        //封装的 反序列化方法
        TestMsg testMsg2 = NetTool.GetProtoMsg<TestMsg>(bytes);
        print(testMsg2.TestMap[1]);
        print(testMsg2.ListInt[0]);
        print(testMsg2.TestD);
        print(testMsg2.TestMsg2.TestInt32);
        print(testMsg2.TestMsg3.TestInt32);
        print(testMsg2.TestHeart.Time);
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏