前言


通过上两篇内容

在Unity中实现TCP通信(一)

在Unity中实现TCP通信(二)

服务端和客户端的TCP通信已经实现了。

但是,在之前的通信中是直接使用了字符串来进行数据的传输。而在实际开发当中,要传输的数据结构是非常复杂的,仅仅使用字符串势必无法满足基本的开发需求。

于是,本篇将阐述一下TCP网络通信中常用的序列化工具,Protobuf。


安装ProtoBuf


Protobuf是Google研发的一种数据序列化工具,它使用Tag技术使数据在序列化成byte时变得非常的小,解析速度也是非常的快,所以很多软件、游戏的网络通讯部分通常都会采用Protobuf来进行数据的传输。

关于Tag技术及Protobuf原理这里不做过多的阐述,有兴趣可以看这篇文章


安装使用Protobuf以及生成CSharp代码


安装 Python


下载Python27,安装并添加系统变量

安装Python

在命令行执行"python" 命令,出现下图表示安装成功

安装Python


安装Protobuf


先在这里下载Protobuf,这是一个编译好的发布版本,如果想自己编译就下载源码即可,我用得是2.5.0

在目录Protobuf-2.5.0\python进入命令行,执行命令"python setup.py install"(进入到python路径然后shift+右键打开命令行即可)

出现下图则安装成功

安装Python


编译Protobuf


这里下载Protobuf Net的源码,分别编译ProtoGen,precompile,protobuf-net这3个工程。

安装Protobuf

将得到这些文件,把它们拷出来,放到和Protobuf同级目录下,这里放到了proto-gen-cs目录下。

安装Protobuf

由于这个下载地址下载速度堪忧,这里给出编译好的版本,直接下载使用即可。


批量转换.proto文件


在Protobuf同级目录下创建一个空的txt,将其重命名为buildcs.bat(名字随意),如图。

安装Protobuf

然后在bat文件内输入这么一行代码,保存

1
2
3
4
5
6
7
@echo off

set Path=proto-gen-cs\protogen.exe

for /f "delims=" %%i in ('dir  /b Proto "proto/*.proto"') do %Path% -i:proto/%%i -o:cs/%%~ni.cs

pause

proto文件夹下创建测试proto协议,新建一个空txt,重命名为test.proto。

安装Protobuf

使用pad++或者sublime之类的编辑器打开它,输入如下代码

1
2
3
4
5
package client;

message test {
optional string content = 1; //name
}

这里如果不了解proto协议如何编写可以看这篇文章

双击buildcs.bat不出意外,会在cs文件夹下得到一个转换后的csharp代码,长这个样子。

转换Protobuf代码


使用生成的代码序列化数据进行Tcp通信


有了proto的代码,就可以将其导入到项目中使用。


客户端


把protobuf-net.dll拖入到unity的plugins文件夹下,把生成的test.cs拖入到unity的scripts文件夹下。

先封装一个工具类,它用于序列化和反序列化数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using System;
using ProtoBuf;
using System.IO;


public class ProtoBufUtil
{
public static byte[] ObjectToBytes<T>(T instance)
{
try
{
byte[] array;
if (instance == null)
{
array = new byte[0];
}
else
{
MemoryStream memoryStream = new MemoryStream();
Serializer.Serialize(memoryStream, instance);
array = new byte[memoryStream.Length];
memoryStream.Position = 0L;
memoryStream.Read(array, 0, array.Length);
memoryStream.Dispose();
}

return array;

}
catch (Exception ex)
{

return new byte[0];
}
}

public static T BytesToObject<T>(byte[] bytesData, int offset, int length)
{
if (bytesData.Length == 0)
{
return default(T);
}
try
{
MemoryStream memoryStream = new MemoryStream();
memoryStream.Write(bytesData, 0, bytesData.Length);
memoryStream.Position = 0L;
T result = Serializer.Deserialize<T>(memoryStream);
memoryStream.Dispose();
return result;
}
catch (Exception ex)
{
return default(T);
}
}
}

然后把第二篇博客中客户端发送数据的代码改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
private void onClick()
{
if(!SocketMgr.Instance.IsConnected)
{
SocketMgr.Instance.Connect("127.0.0.1", 8888);
return;
}

client.test sendContent = new client.test();//创建protobuf序列化实例
sendContent.content = inputField.text;//对应字段赋值
byte[] buffer = ProtoBufUtil.ObjectToBytes<test>(sendContent);//序列化
SocketMgr.Instance.Send(1, buffer);//发送
}

到这里,只要点击按钮,客户端会把数据序列化成protobuf的格式并发送到服务端。


服务端


添加对protobuf-net.dll(就是上面提到的那个protobuf-net.dll文件)的引用。

服务端

把客户端的ProtobufUtil拖到服务端工程目录下,再把test.cs到也拖到工程目录下。

服务端

然后把第二篇博客中服务端解析数据的代码改一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void CheckReceiveBuffer(object state)
{
lock (m_ReceiveQueue)
{
if (m_ReceiveQueue.Count < 1) return;
byte[] buffer = m_ReceiveQueue.Dequeue();
byte[] msgContent = new byte[buffer.Length - 2];
ushort msgCode = 0;

using (MemoryStream ms = new MemoryStream(buffer))
{
byte[] msgCodeBuffer = new byte[2];
ms.Read(msgCodeBuffer, 0, msgCodeBuffer.Length);
msgCode = BitConverter.ToUInt16(msgCodeBuffer, 0);
ms.Read(msgContent, 0, msgContent.Length);
}

test content = ProtoBufUtil.BytesToObject<test>(msgContent, 0, msgContent.Length);//调用protobuf,把数据反序列化为test对象
Console.WriteLine("消息编号:" + msgCode + ",内容:" + content.content);
}
}

到这里服务端也顺利的使用protobuf来解析数据了,下面开始测试。


测试


还是老套路,先启动服务器,然后客户端连接服务器,成功

服务端

然后客户端输入任意内容点击发送

服务端

服务端成功的使用Protobuf解析了数据,啪啪啪,此处仍然要有掌声雷动。

服务端


结语


到这里protobuf的安装、使用就已经全部写完了,下一篇把最后的心跳机制也写一下,整个TCP网络部分基本就写完了。