知识点一 利用protoc.exe编译器生成脚本文件
//2.进入protoc.exe所在文件夹(也可以直接将exe文件拖入cmd窗口中)
//protoc.exe -I=配置路径 --csharp_out=输出路径 配置文件名
//注意:路径不要有中文和特殊符号,避免生成失败
知识点二 测试生成对象是否能使用
cs
TestMsg msg = new TestMsg();
msg.TestBool = true;
//对应的和List以及Dictionary使用方式一样的 数组和字典对象
msg.ListInt.Add(1);
print(msg.ListInt[0]);
msg.TestMap.Add(1, "DamnF");
print(msg.TestMap[1]);
//枚举
msg.TestEnum = TestEnum.Boss;
msg.TestEnum2 = TestMsg.Types.TestEnum2.Boss;
//内部枚举
msg.TestMsg2 = new TestMsg2();
msg.TestMsg2.TestInt32 = 99;
//其他内部类对象
msg.TestMsg3 = new TestMsg.Types.TestMsg3();
msg.TestMsg3.TestInt32 = 55;
//在另一个生成的脚本当中的类 如果命名空间不同 需要命名空间点出来使用
msg.TestHeart = new GameSystemTest.HeartMsg();
制作ProtobufTool协议生成工具
cs
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;
public class ProtobufTool
{
//协议配置文件所在路径
private static string PROTO_PATH = "C:\\Users\\HONOR\\Desktop\\TeachNet\\Protobuf\\proto";
//协议生成可执行文件的路径
private static string PROTOC_PATH = "C:\\Users\\HONOR\\Desktop\\TeachNet\\Protobuf\\protoc.exe";
//C#文件的生成路径
private static string CSHARP_PATH = "C:\\Users\\HONOR\\Desktop\\TeachNet\\Protobuf\\csharp";
//C++文件的生成路径
private static string CPP_PATH = "C:\\Users\\HONOR\\Desktop\\TeachNet\\Protobuf\\cpp";
//Java文件的生成路径
private static string JAVA_PATH = "C:\\Users\\HONOR\\Desktop\\TeachNet\\Protobuf\\java";
[MenuItem("ProtobufTool/生成C#代码")]
private static void GenerateCSharp()
{
Generate("csharp_out",CSHARP_PATH);
}
[MenuItem("ProtobufTool/生成C++代码")]
private static void GenerateCpp()
{
Generate("cpp_out",CPP_PATH);
}
[MenuItem("ProtobufTool/生成Java代码")]
private static void GenerateJava()
{
Generate("java_out", JAVA_PATH);
}
private static void Generate(string outCmd,string filePath)
{
//第一步:遍历对应协议配置文件夹 得到所有的配置文件
DirectoryInfo directoryInfo = Directory.CreateDirectory(PROTO_PATH);
//获取对应文件夹下所有文件的信息
FileInfo[] files = directoryInfo.GetFiles();
//遍历所有文件 为其生成协议脚本
for (int i = 0; i < files.Length; i++)
{
//后缀的判断 只有是配置文件才能用于生成
if (files[i].Extension == ".proto")
{
//第二步:根据文件内容 来生成对应C#脚本(需要使用C#当中的Process类)
Process cmd = new Process();
//protoc.exe的路径
cmd.StartInfo.FileName = PROTOC_PATH;
//命令
cmd.StartInfo.Arguments = $"-I={PROTO_PATH} --{outCmd}={filePath} {files[i]}";
//执行
cmd.Start();
//告诉外部 某一个文件生成结束
UnityEngine.Debug.Log(files[i] + "生成结束");
}
}
UnityEngine.Debug.Log("所有内容生成结束");
}
}
Protobuf-Net知识点
//Protobuf的序列化和反序列化都要通过
//流对象来进行处理
//如果是进行本地存储,则可以使用文件流
//如果是进行网络传输,则可以使用内存流获取字节数组
知识点一 序列化存储为本地文件
cs
//主要使用
//1.生成的类中的 WriteTo方法
//2.文件流FileStream对象
TestMsg msg = new TestMsg();
msg.ListInt.Add(1);
msg.TestBool = false;
msg.TestD = 5.5;
msg.TestInt32 = 99;
msg.TestMap.Add(1, "DamnF");
msg.TestMsg2 = new TestMsg2();
msg.TestMsg2.TestInt32 = 66;
msg.TestHeart = new GameSystemTest.HeartMsg();
msg.TestHeart.Time = 7777;
using (FileStream fs = File.Create(Application.persistentDataPath + "/TestMsg.df"))
{
msg.WriteTo(fs);
}
知识点二 反序列化本地文件
cs
//主要使用
//1.生成的类中的Parser.ParseFrom方法
//2.文件流FileStream对象
using (FileStream fs=File.OpenRead (Application.persistentDataPath + "/TestMsg.df"))
{
TestMsg msg2 = null;
msg2 = TestMsg.Parser.ParseFrom(fs);
print(msg2.TestMap[1]);
print(msg2.ListInt[0]);
print(msg2.TestD);
print(msg2.TestMsg2.TestInt32);
print(msg2.TestHeart.Time);
}
知识点三 得到序列化后的字节数组
cs
//主要使用
//1.生成的类中的WriteTo方法
//2.内存流MemoryStream对象
byte[] bytes = null;
using (MemoryStream ms=new MemoryStream ())
{
msg.WriteTo(ms);
bytes = ms.ToArray();
print("字节数组的长度" + bytes.Length);
}
知识点四 从字节数组反序列化
cs
//主要使用
//1.生成的类中的 Parser.ParserFrom方法
//2.内存流MemoryStream对象
using (MemoryStream ms=new MemoryStream (bytes))
{
print("内存流当中反序列化的内容");
TestMsg msg2 = TestMsg.Parser.ParseFrom(ms);
print(msg2.TestMap[1]);
print(msg2.ListInt[0]);
print(msg2.TestD);
print(msg2.TestMsg2.TestInt32);
print(msg2.TestHeart.Time);
}
封装NetTool
cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Google.Protobuf;
using UnityEngine;
public static class NetTool
{
public static byte[] GetProtoBytes(IMessage msg)
{
//上节课学习的基础写法
//byte[] bytes = null;
//using (MemoryStream ms=new MemoryStream())
//{
// msg.WriteTo(ms);
// bytes = ms.ToArray();
//}
//return bytes;
//通过该拓展方法,就可以直接获取字节流对象
return msg.ToByteArray();
}
public static T GetProtoMsg<T>(byte[]bytes)where T:class,IMessage
{
//得到对应消息的类型 通过反射得到内部的静态成员 然后得到其中对应的方法
//进行反序列化
Type type = typeof(T);
//通过反射 得到对应的 静态成员属性对象
PropertyInfo pInfp = type.GetProperty("Parser");
object parseObj= pInfp.GetValue(null, null);
//已经得到了该对象,那么可以得到该对象中 对应方法
Type parserType = parseObj.GetType();
//这是指定得到某一个重载方法
MethodInfo mInfo = parserType.GetMethod("ParseFrom", new Type[] { typeof(byte[]) });
//调用对应的方法 反序列化为指定的对象
object msg= mInfo.Invoke(parseObj, new object[] { bytes });
return msg as T;
}
}