OSI七层模型
- 应用层:为应用程序提供服务
- 表示层:数据格式转化,加密
- 会话层:建立,管理和维护对话
- 传输层:建立,管理和维护端到端的连接
- 网络层:IP选址和路由选择
- 数据链路层:分帧(数据包),确定Mac地址
- 物理层:物理设备传输数据
TCP/IP
将OSI七层模型进行设计,吧互联通信的过程抽象分成了四层
- 应用层
- 传输层
- 网络层
- 数据链路层
TCP/UDP
- TCP:三次握手,四次挥手
- 更可靠,保证了数据的稳定性和有序性
- 适合对信息准确性要求高,效率要求低的场景
- UDP:
- 更效率,传输更快,资源消耗少
- 适合对时效性要求高的场景
IP地址和端口类
- 初始化IP信息IPAddress
- IPAddress ip = IPAddress.Parse(“118.102.111.11”)
- IPAddress ip = new IPAddress(0x79666F0B)
- IPAddress ip = new IPAddress(new byte[]{118, 102, 111, 11})
- 初始化IP和端口号组合IPEndPoint
- IPEndPoint ipPoint = new IPEndPoint(0x79666F0B, 8080)
- IPEndPoint ipPoint = new IPEndPoint(IPAddress, 8080)
- 域名解析IPHostEntry(为保证在查询时不阻塞主线程,通常使用异步操作)
- IPHostEntry entry = Dns.GetHostEntry(“www.baidu.com“);
- entry.AddressList // IP地址
- entry.Aliases // 主机别名
- entry.HostName // 服务器名称
- 异步操作
- public async void FuncAsync()
{
Task<IPHostEntry> task = Dns.GetHostEntryAsync(“www.baidu.com”);
await task;
for(int i = 0;i < task.Result.AddressList.Length;++ i)
{
print(task.Result.AddressList[i]);
}
print(“服务器名称:” + task.Result.HostName);
}
- public async void FuncAsync()
- IPHostEntry entry = Dns.GetHostEntry(“www.baidu.com“);
Socket套接字
- Socket s = new Socket()
- 参数一:AddressFamily 网络寻址 枚举类型,决定寻址方案 常用: 1.InterNetwork IPv4寻址 2.InterNetwork6 IPv6寻址 做了解: 1.UNIX UNIX本地到主机地址 2.ImpLink ARPANETIMP地址 3.Ipx IPX或SPX地址 4.Iso ISO协议的地址 5.Osi OSI协议的地址 7.NetBios NetBios地址 9.Atm 本机ATM服务地址
- 参数二:SocketType 套接字枚举类型,决定使用的套接字类型 常用: 1.Dgram 支持数据报,最大长度固定的无连接、不可靠的消息(主要用于UDP通信) 2.Stream 支持可靠、双向、基于连接的字节流(主要用于TCP通信) 做了解: 1.Raw 支持对基础传输协议的访问 2.Rdm 支持无连接、面向消息、以可靠方式发送的消息 3.Seqpacket 提供排序字节流的面向连接且可靠的双向传输
- 参数三:ProtocolType 协议类型枚举类型,决定套接字使用的通信协议 常用: 1.TCP TCP传输控制协议 2.UDP UDP用户数据报协议 做了解: 1.IP IP网际协议 2.Icmp Icmp网际消息控制协议 3.Igmp Igmp网际组管理协议 4.Ggp 网关到网关协议 5.IPv4 Internet协议版本4 6.Pup PARC通用数据包协议 7.Idp Internet数据报协议 8.Raw 原始IP数据包协议 9.Ipx Internet数据包交换协议 10.Spx 顺序包交换协议 11.IcmpV6 用于IPv6的Internet控制消息协议
- 2、3参数的常用搭配: SocketType.Dgram + ProtocolType.Udp = UDP协议通信(常用,主要学习) SocketType.Stream + ProtocolType.Tcp = TCP协议通信(常用,主要学习) SocketType.Raw + ProtocolType.Icmp = Internet控制报文协议(了解) SocketType.Raw + ProtocolType.Raw = 简单的IP包通信(了解)
TCP通信
- 服务端1.创建Socket对象
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2.绑定IP端口
socket.Bind(new IPEndPoint(IPAddress.Parse(IP), port));
3.设置最大监听客户端数量
socket.Listen(maxListener);
4.连接接收客户端
Socket nowSocket = socket.Accept();
5.向客户端发送消息
client.socket.Send(Encoding.UTF8.GetBytes(info)); - 客户端1.创建Socket对象
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2.连接服务端
socket.Connect(new IPEndPoint(IPAddress.Parse(IP), port));
3.接收服务端消息
int byteNum = socket.Receive(result);
4.向服务端发送消息
socket.Send(data.Writing());
TCP异步
- Acceptpublic void FuncAcceptAsync()
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += (_socket, args) =>
{
if(e.SocketError == SocketError.Success)
{
Socket clientSocket = args.AcceptSocket;
ClientSocket client = new ClientSocket(clientSocket);
clientDic.Add(client.clientID, client);
client.FuncSendAsync(Encoding.UTF8.GetBytes(“欢迎连入服务端”));
}
else
{
Console.WriteLine(“Accept Fault:” + e.SocketError);
}
};
socket.AcceptAsync(e);
} - Receivepublic void FuncReceiveAsync()
{
receiveE.SetBuffer(new byte[1024 * 1024], 0, 1024 * 1024);
receiveE.Completed += (_socket, args) =>
{
if (args.SocketError == SocketError.Success)
{
byte[] result = args.Buffer;
int byteNum = args.BytesTransferred;
// 后续处理信息
int index = 0;
int msgID = BitConverter.ToInt32(result, index);
index += 4;
int length = BitConverter.ToInt32(result, index);
index += 4;
switch (msgID)
{
case 1001:
break;
case 1002:
Console.WriteLine(“收到心跳”);
break;
default:
Console.WriteLine(Encoding.UTF8.GetString(result, index, length));
break;
}
args.SetBuffer(0, args.Buffer.Length);
(_socket as Socket).ReceiveAsync(args);
}
else
{
Console.WriteLine(“Receive Fault:” + args.SocketError);
}
};
socket.ReceiveAsync(receiveE);
} - Sendpublic void FuncSendAsync(byte[] bytes)
{
sendE.SetBuffer(bytes, 0, bytes.Length);
socket.SendAsync(sendE);
}
UDP通信
相较于TCP,UDP服务端不需要设置listener,客户端不需要connect,关键字为ReceiveFrom,SendTo
- 服务端
- 客户端
UDP异步
- 服务端public Socket socket;
public bool isClosed;
public Dictionary<string, UDPClient> clientDic = new Dictionary<string, UDPClient>();
public SocketAsyncEventArgs sendE = new SocketAsyncEventArgs();
public void Init(string IP, int port)
{
Console.WriteLine(“服务端开启”);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(IPAddress.Parse(IP), port));
Console.WriteLine(“正在等待接收消息…”);
FuncReceiveAsync();
}
public void FuncReceiveAsync()
{
SocketAsyncEventArgs receiveE = new SocketAsyncEventArgs();
//IPEndPoint clientIP = new IPEndPoint(IPAddress.Any, 0);
receiveE.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
receiveE.SetBuffer(new byte[500], 0, 500);
receiveE.Completed += (_socket, args) =>
{
IPEndPoint iPEndPoint = args.RemoteEndPoint as IPEndPoint;
string s = iPEndPoint.Address.ToString() + iPEndPoint.Port.ToString();
UDPClient client = null;
if (!clientDic.ContainsKey(s))
{
client = new UDPClient(iPEndPoint);
clientDic.Add(s, client);
}
else client = clientDic[s];
byte[] result = args.Buffer;
int length = args.BytesTransferred;
args.SetBuffer(result, 0, length);
client.HandleReceiveMsg(result);
(_socket as Socket).ReceiveFromAsync(receiveE);
};
socket.ReceiveFromAsync(receiveE);
}
public void BroadCast(byte[] bytes)
{
foreach (UDPClient client in clientDic.Values)
{
socket.SendTo(bytes, client.iPEndPoint);
}
} - 客户端private static AsyncUDPNetMgr instance;
public static AsyncUDPNetMgr Instance => instance;
private IPEndPoint serverIP = new IPEndPoint(IPAddress.Parse(“127.0.0.1”), 8080);
private Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
private void Awake()
{
instance = this;
FuncReceiveAsync();
}
public void SetServerIPEndPoint(string IP, int port)
{
serverIP.Address = IPAddress.Parse(IP);
serverIP.Port = port;
}
public void FuncReceiveAsync()
{
SocketAsyncEventArgs receiveE = new SocketAsyncEventArgs();
receiveE.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
receiveE.SetBuffer(new byte[500], 0, 500);
receiveE.Completed += (_socket, args) =>
{
if(args.SocketError == SocketError.Success)
{
args.SetBuffer(args.Buffer, 0, args.BytesTransferred);
HandleMsg(args.Buffer);
(_socket as Socket).ReceiveFromAsync(args);
}
else
{
print(“接收错误:” + args.SocketError);
}
};
socket.ReceiveFromAsync(receiveE);
}
public void SendMsg(byte[] bytes)
{
socket.SendTo(bytes, serverIP);
}
public void HandleMsg(byte[] result)
{
string info = Encoding.UTF8.GetString(result);
print(“收到服务端发来消息:” + info);
}
文件传输FTP
- 本质是TCP
- 两种传输模式:主动模式Port,被动模式Passive
- 两种传输方式:ASCII和二进制
- 需要用户名和密码登录(服务器允许时也可匿名登录)
- 重要类
- 通信凭证类 NetworkCredential
- 构造参数为string账号密码
- Create创建新的WebRequest
- FtpWebRequest req = FtpWebRequest.Create(new Uri(“ftp://127.0.0.1/test.txt”)) as FtpWebRequest
- GetRequestStream获取上传流
- Stream s = req.GetRequestStream()
- GetResponse 返回FTP服务器响应
- FtpWebResponse res = req.GetResponse () as FtpWebResponse
- 通信凭证类 NetworkCredential
- 重要成员//1.Credentials 通信凭证,设置为NetworkCredential对象
req.Credentials = n;
//2.KeepAlive bool值,当完成请求时是否关闭到FTP服务器的控制连接(默认为true,不关闭)
req.KeepAlive = false;//3.Method 操作命令设置
req.Method
// WebRequestMethods.Ftp类中的操作命令属性
// DeleteFile 删除文件
// DownloadFile 下载文件
// ListDirectory 获取文件简短列表
// ListDirectoryDetails 获取文件详细列表
// MakeDirectory 创建目录
// RemoveDirectory 删除目录
// UploadFile 上传文件
req.Method = WebRequestMethods.Ftp.DownloadFile;
这些方法会在req.GetResponse()的时候执行//4.UseBinary 是否使用2进制传输
req.UseBinary = true;//5.RenameTo 重命名
//req.RenameTo = “myTest.txt”;知识点三 FtpWebResponse类
//命名空间:System.Net
//它是用于封装FTP服务器对请求的响应
//它提供操作状态以及从服务器下载数据
//我们可以通过FtpWebRequest对象中的GetResponse()方法获取
//当使用完毕时,要使用Close释放
//通过它来真正的从服务器获取内容
FtpWebResponse res = req.GetResponse() as FtpWebResponse;//重要方法:
//1.Close:释放所有资源
res.Close();
//2.GetResponseStream:返回从FTP服务器下载数据的流
Stream stream = res.GetResponseStream();//重要成员:
//1.ContentLength:接受到数据的长度
print(res.ContentLength);
//2.ContentType:接受数据的类型
print(res.ContentType);
//3.StatusCode:FTP服务器下发的最新状态码
print(res.StatusCode);
//4.StatusDescription:FTP服务器下发的状态代码的文本
print(res.StatusDescription);
//5.BannerMessage:登录前建立连接时FTP服务器发送的消息
print(res.BannerMessage);
//6.ExitMessage:FTP会话结束时服务器发送的消息
//7.LastModified:FTP服务器上的文件的上次修改日期和时间
超文本传输HTTP
- 重要类
- 通信凭证类 NetworkCredential
- 请求类HttpWebRequest
- 响应类HttpWebResponse
- 常用属性
- req.Method = WebRequestMethods.Http.Get; // Get通常为下载,通常Post为上传
- req.Timeout = 2000; // 当2000ms后未响应,则会认为超时,不成功
- req.ContentType = ” ” // Post需要
- Post上传示例知识点二 Post如何携带额外参数
//关键点:将Content-Type设置为 application/x-www-form-urlencoded 键值对类型
HttpWebRequest req = HttpWebRequest.Create(“http://192.168.50.109:8000/Http_Server/”) as HttpWebRequest;
req.Method = WebRequestMethods.Http.Post;
req.Timeout = 2000;
//设置上传的内容的类型
req.ContentType = “application/x-www-form-urlencoded”;//我们要上传的数据
string str = “Name=MrTang&ID=2”;
byte[] bytes = Encoding.UTF8.GetBytes(str);
//我们在上传之前一定要设置内容的长度
req.ContentLength = bytes.Length;
//上传数据
Stream stream = req.GetRequestStream();
stream.Write(bytes, 0, bytes.Length);
stream.Close();
//发送数据 得到响应结果
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
print(res.StatusCode);- 其中ContentType有//ContentType的构成:
//内容类型;charset=编码格式;boundary=边界字符串
//text/html;charset=utf-8;boundary=自定义字符串//其中内容类型有:
//文本类型text:
//text/plain 没有特定子类型就是它(重要)
//text/html
//text/css
//text/javascript//图片类型image:
//image/gif
//image/png
//image/jpeg
//image/bm
//image/webp
//image/x-icon
//image/vnd.microsoft.icon//音频类型audio:
//audio/midi
//audio/mpeg
//audio/webm
//audio/ogg
//audio/wav//视频类型video:
//video/webm
//video/ogg//二进制类型application:
//application/octet-stream 没有特定子类型就是它(重要)
//application/x-www-form-urlencoded 传递参数时使用键值对形式(重要)
//application/pkcs12
//application/xhtml+xml
//application/xml
//application/pdf
//application/vnd.mspowerpoint//复合内容multipart:
//multipart/form-data 复合内容,有多种内容组合(重要)
//multipart/byteranges 特殊的复合文件 - ContentType中对于我们来说重要的类型
- 1.通用2进制类型 application/octet-stream
- 2.通用文本类型 text/plain
- 3.键值对参数 application/x-www-form-urlencoded
- 4.复合类型(传递的信息有多种类型组成,比如有键值对参数,有文件信息等等,上传资源服务器时需要用该类型) multipart/form-data
- 其中ContentType有//ContentType的构成:
- WWW类
- 三种地址
- Http — http://
- Ftp — ftp://
- 本地 — file://
- 创建方式为WWW www = new WWW(“file://” + Path);
- 配合协程使用,yield return www等待创建好后再进行后面逻辑
- 直接通过属性来获得加载好的文件
- www.texture
- www.text
- www.assetBundle
- www.GetAudioClip()
- 三种地址
- WWWForm类
- 可以作为new WWW(path, data)的第二参数,传递数据
- 用法:WWWForm data = new WWWFrom( )
- data.AddField(字段名,字段值)
- data.AddBinaryData(字段名,byte[ ])
- UnityWebRequest类
- 示例获取Text(Get只是配置连接,真正的连接需要SendWebRequest( ))IEnumerator LoadText()
{
UnityWebRequest req = UnityWebRequest.Get(“http://192.168.50.109:8000/Http_Server/test.txt”);
//就会等待 服务器端响应后 断开连接后 再继续执行后面的内容
yield return req.SendWebRequest();//如果处理成功 结果就是成功枚举
if(req.result == UnityWebRequest.Result.Success)
{
//文本 字符串
print(req.downloadHandler.text);
//字节数组
byte[] bytes = req.downloadHandler.data;
print(“字节数组长度” + bytes.Length);
}
else
{
print(“获取失败:” + req.result + req.error + req.responseCode);
}
} - 1.获取文本或二进制数据时 使用UnityWebRequest.Get(path) 以及req.downloadHandler.data
- 2.获取纹理图片数据时 使用UnityWebRequestTexture.GetTexture(path) 以及DownloadHandlerTexture.GetContent(req)
- 3.获取AB包数据时 使用UnityWebRequestAssetBundle.GetAssetBundle(path) 以及DownloadHandlerAssetBundle.GetContent(req)
- 示例获取Text(Get只是配置连接,真正的连接需要SendWebRequest( ))IEnumerator LoadText()
- 上传相关数据类
- 需要将要上传的数据都存储到 List
dataList - Add两种父类MultipartFormDataSection和MultipartFormFileSection
- 其中Add的方式有以下
- //子类数据
//MultipartFormDataSection
//1.二进制字节数组
dataList.Add(new MultipartFormDataSection(Encoding.UTF8.GetBytes(“123123123123123”)));
//2.字符串
dataList.Add(new MultipartFormDataSection(“12312312312312312dsfasdf”));
//3.参数名,参数值(字节数组,字符串),编码类型,资源类型(常用)
dataList.Add(new MultipartFormDataSection(“Name”, “MrTang”, Encoding.UTF8, “application/….”));
dataList.Add(new MultipartFormDataSection(“Msg”, new byte[1024], “appl…..”));//MultipartFormFileSection
//1.字节数组
dataList.Add(new MultipartFormFileSection(File.ReadAllBytes(Application.streamingAssetsPath + “/test.png”)));//2.文件名,字节数组(常用)
dataList.Add(new MultipartFormFileSection(“上传的文件.png”, File.ReadAllBytes(Application.streamingAssetsPath + “/test.png”)));
//3.字符串数据,文件名(常用)
dataList.Add(new MultipartFormFileSection(“12312313212312”, “test.txt”));
//4.字符串数据,编码格式,文件名(常用)
dataList.Add(new MultipartFormFileSection(“12312313212312”, Encoding.UTF8, “test.txt”));//5.表单名,字节数组,文件名,文件类型
dataList.Add(new MultipartFormFileSection(“file”, new byte[1024], “test.txt”, “”));
//6.表单名,字符串数据,编码格式,文件名
dataList.Add(new MultipartFormFileSection(“file”, “123123123”, Encoding.UTF8, “test.txt”)); - 上传的方式有两种(Post,Put)
- UnityWebRequest req = UnityWebRequest.Post(“http://192.168.50.109:8000/Http_Server/“, data); // 常用
- UnityWebRequest req = UnityWebRequest.Put(“http://192.168.50.109:8000/Http_Server/“, File.ReadAllBytes(Application.streamingAssetsPath + “/test.png”)); // 必须资源服务器支持Put请求类型
- 上传示例private IEnumerator UploadResAysnc(string fileName, string localPath, UnityAction<UnityWebRequest.Result> callBack)
{
List<IMultipartFormSection> data = new List<IMultipartFormSection>();
data.Add(new MultipartFormFileSection(fileName, File.ReadAllBytes(localPath)));UnityWebRequest req = UnityWebRequest.Post(“http://10.4.108.124:8080/UServer/”, data);
yield return req.SendWebRequest();callBack?.Invoke(req.result);
}
- 需要将要上传的数据都存储到 List
- UnityWebRequest高级操作
- 在获取对应类对象时不再需要把req的Get替换成对应的方法,例如UnityWebRequestTexture.GetTexture(path)这种,而是手动去创建req.downloadHandler对应的下载器
- new DownloadHandlerTexture();
- new DownloadHandlerAssetBundle(req.url, 0);UnityWebRequest req = UnityWebRequest.Get(path);
req.downloadHandler = new DownloadHandlerTexture(true); //这里灵活性更高几乎等价于
UnityWebRequest req = UnityWebRequestTexture.GetTexture(path)
- 存在自定义下载类,继承DownloadHandlerScriptpublic class CustomDownLoadFileHandler:DownloadHandlerScript
{
//用于保存 本地存储时的路径
private string savePath;//用于缓存收到的数据的容器
private byte[] cacheBytes;
//这是当前已收到的数据长度
private int index = 0;public CustomDownLoadFileHandler():base(){ }
public CustomDownLoadFileHandler(byte[] bytes) :base(bytes){ }
public CustomDownLoadFileHandler(string path) : base()
{
savePath = path;
}protected override byte[] GetData()
{
//返回字节数组
return cacheBytes;
}/// 从网络收到数据后 每帧会调用的方法 会自动调用的方法
protected override bool ReceiveData(byte[] data, int dataLength)
{
Debug.Log(“收到数据长度:” + data.Length);
Debug.Log(“收到数据长度dataLength:” + dataLength);
data.CopyTo(cacheBytes, index);
index += dataLength;
return true;
}/// 从服务器收到 COntent-Length标头时 会自动调用的方法
protected override void ReceiveContentLengthHeader(ulong contentLength)
{
//base.ReceiveContentLengthHeader(contentLength);
Debug.Log(“收到数据长度:” + contentLength);
//根据收到的标头 决定字节数组容器的大小
cacheBytes = new byte[contentLength];
}/// 当消息收完了 会自动调用的方法
protected override void CompleteContent()
{
Debug.Log(“消息收完”);
//把收到的字节数组 进行自定义处理 我们在这 处理成 存储到本地
File.WriteAllBytes(savePath, cacheBytes);
}}
- 还有上传的高级操作,但是不如常用操作好用,故不记叙
第三方协议工具Protobuf
- .proto文件配置规则syntax = “proto3”;//决定了proto文档的版本号
//规则二:版本号//规则一:注释方式
//注释方式一
/*注释方式二*///规则11:导入定义
import “test2.proto”;//规则三:命名空间
package GamePlayerTest;//这决定了命名空间//规则四:消息类
message TestMsg{
//规则五:成员类型 和 唯一编号//浮点数
// = 1 不代表默认值 而是代表唯一编号 方便我们进行序列化和反序列化的处理
//required 必须赋值的字段
required float testF = 1; //C# – float
//optional 可以不赋值的字段
optional double testD = 2; //C# – double//变长编码
//所谓变长 就是会根据 数字的大小 来使用对应的字节数来存储 1 2 4
//Protobuf帮助我们优化的部分 可以尽量少的使用字节数 来存储内容
int32 testInt32 = 3; //C# – int 它不太适用于来表示负数 请使用sint32
//1 2 4 8
int64 testInt64 = 4; //C# – long 它不太适用于来表示负数 请使用sint64//更实用与表示负数类型的整数
sint32 testSInt32 = 5; //C# – int 适用于来表示负数的整数
sint64 testSInt64 = 6; //C# – long 适用于来表示负数的整数//无符号 变长编码
//1 2 4
uint32 testUInt = 7; //C# – uint 变长的编码
uint64 testULong = 8; //C# – ulong 变长的编码//固定字节数的类型
fixed32 testFixed32 = 9; //C# -uint 它通常用来表示大于2的28次方的数 ,比uint32更有效 始终是4个字节
fixed64 testFixed64 = 10; //C# -ulong 它通常用来表示大于2的56次方的数 ,比uint64更有效 始终是8个字节sfixed32 testSFixed32 = 11; //C# – int 始终4个字节
sfixed64 testSFixed64 = 12; //C# – long 始终8个字节//其它类型
bool testBool = 13; //C# – bool
string testStr = 14; //C# – string
bytes testBytes = 15; //C# – BytesString 字节字符串//数组List
repeated int32 listInt = 16; // C# – 类似List<int>的使用
//字典Dictionary
map<int32, string> testMap = 17; // C# – 类似Dictionary<int, string> 的使用//枚举成员变量的声明 需要唯一编码
TestEnum testEnum = 18;//声明自定义类对象 需要唯一编码
//默认值是null
TestMsg2 testMsg2 = 19;//规则9:允许嵌套
//嵌套一个类在另一个类当中 相当于是内部类
message TestMsg3{
int32 testInt32 = 1;
}TestMsg3 testMsg3 = 20;
//规则9:允许嵌套
enum TestEnum2{
NORMAL = 0; //第一个常量必须映射到0
BOSS = 1;
}TestEnum2 testEnum2 = 21;
//int32 testInt3233333 = 22;
bool testBool2123123 = 23;
GameSystemTest.HeartMsg testHeart = 24;
//告诉编译器 22 被占用 不准用户使用
//之所以有这个功能 是为了在版本不匹配时 反序列化时 不会出现结构不统一
//解析错误的问题
reserved 22;
reserved “testInt3233333”;
}//枚举的声明
enum TestEnum{
NORMAL = 0; //第一个常量必须映射到0
BOSS = 5;
}message TestMsg2{
int32 testInt32 = 1;
} - 将Proto转化为C#文件
- protoc.exe -I=配置路径 –csharp_out=输出路径 配置文件名
- Proto生成的CS类的序列化和反序列化
- 使用FileStream操作文件
- 序列化并生成文件using (FileStream fs = File.Create(Application.persistentDataPath + “/TestMsg.tang”))
{
msg.WriteTo(fs);
} - 反序列化本地文件using (FileStream fs = File.OpenRead(Application.persistentDataPath + “/TestMsg.tang”))
{
TestMsg msg2 = null;
msg2 = TestMsg.Parser.ParseFrom(fs);
}
- 序列化并生成文件using (FileStream fs = File.Create(Application.persistentDataPath + “/TestMsg.tang”))
- 使用MemoryStream操作字节数组
- 序列化为字节数组using (MemoryStream ms = new MemoryStream())
{
msg.WriteTo(ms);
bytes = ms.ToArray();
}
// 推荐使用该类内置的方法
bytes = msg.ToByteArray(); - 反序列化字节数组using (MemoryStream ms = new MemoryStream(bytes))
{
TestMsg msg2 = TestMsg.Parser.ParseFrom(ms);
}
- 序列化为字节数组using (MemoryStream ms = new MemoryStream())
- 生成的CS文件,基类为IMessage,相当于原本的BaseMessage,主要是作为形参
- 自定义生成工具public static class NetTool
{
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();
}public static T GetProtoMsg<T>(byte[] bytes) where T:class, IMessage
{
//得到对应消息的类型 通过反射得到内部的静态成员 然后得到其中的 对应方法
Type type = typeof(T);
//通过反射 得到对应的 静态成员属性对象
PropertyInfo pInfo = type.GetProperty(“Parser”);
object parserObj = pInfo.GetValue(null, null);
//已经得到了对象 那么可以得到该对象中的 对应方法
Type parserType = parserObj.GetType();
//这是指定得到某一个重载函数
MethodInfo mInfo = parserType.GetMethod(“ParseFrom”, new Type[] { typeof(byte[]) });
//调用对应的方法 反序列化为指定的对象
object msg = mInfo.Invoke(parserObj, new object[] { bytes });
return msg as T;
}
}
- 使用FileStream操作文件
大小端模式
- 从左向右读的是大端模式,反之是小端模式
- 小端语言:C#,C++
- 大端语言:Java,Erlang,AS3
- 大小端相互通信时需要对二进制消息进行转化
- Array.Reverse(bytes)
- 使用Protobuf不用考虑大小端,内部已经处理好

评论(0)
暂无评论