转自:http://blog.youkuaiyun.com/panda_bear/article/details/9949751
在移动手机游戏开发中,目前Unity3D已成为比较主流的开发技术。
那么对于客户端服务器协议的打解包,我们有3中常用的处理方式:
1、自定义结构体:在协议中直接传输代码中自定义的结构体;这种方式的坏处是极大的增加了重复性的工作量,并且不能实现协议前后向兼容,可扩展性差;
2、json、xml等文本协议格式: 使用json、xml等文本协议作为协议格式;这种方式的好处是易于开发,方便协议前后向兼容和扩展,缺点是不能序列化,数据量大,浪费带宽;
3、推荐使用的方式: protobuf协议打解包方式;protobuf是google提出的一套开源协议,具有良好的前后向协议兼容性,易于扩展,并且具有很高的序列化和反序列化的效率,能极大的减小传输数据量的大小;
在Unity3D开发中,对于网络部分一般使用C#语言进行开发。一般推荐使用protobuf-net第三方库来进行开发。
但是不幸的是,其中使用到的JIT技术在Unity3D的IOS版本中是不能使用的,在序列化时会导致异常。
经过google网上搜索,找到一种不方便使用的解决方案如下:
但是该方案很复杂,非常不便于操作。
经过笔者自己的实验,探索出下面可用的一种解决方案:
1、从SVN上下载protbuf-net的源码:
http://protobuf-net.googlecode.com/svn/trunk/protobuf-net
2、将该目录中的所有C#源码拷贝到Unity3D中,直接使用源码而不是第三方dll;
3、此时在Unity中编译时,可能会报错说 unsafe不能使用;
4、采用如下方案可以解决: 在Assets目录下面新建 smcs.rsp文件,并在其中写入 -unsafe 字符串,前后不加空格;
5、重新启动unity,此时我们可以发现该工程能够通过编译;
经验证,该方案在IOS设备上也是可用的。从而保证我们的protobuf能够应用在Unity移动开发中。
下面是更为详细的使用方法:
转自 http://my.oschina.net/faint/blog/296785
第一部分 dll
1 下面大多数内容,都是使用c#编译的dll来实现的。
2 编译为dll后,要拖放到unity3d的Assets里面,才能using到。
3 有以下类似错误,就是使用了非.net 2.0编译的dll。注意项目必须是在.net 2.0版本编译的才能正常在unity3d当中使用。
Unhandled Exception: System.TypeLoadException: Could not load type 'System.Runtime.Versioning.TargetFrameworkAttribute' from assembly 'MyModel'
4 应该不能用MonoDevelop编译下面会提到的Serializer部分(编译不出dll,会报错)。需用vs编译。
第二部分 tcp/ip
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
using
System;
using
System.IO;
using
System.Net.Sockets;
namespace
TcpConnector{
public
struct
Msg {
public
int
Type;
public
int
Size;
public
byte
[] Content;
}
public
class
Connector{
const
int
HEAD_SIZE = 4;
private
TcpClient client;
NetworkStream stream;
public
bool
Connect(
string
ip,
int
port){
try
{
client =
new
TcpClient(ip,port);
stream = client.GetStream();
return
true
;
}
catch
{
return
false
;
}
}
public
void
Disconnect(){
stream.Close();
client.Close();
}
private
int
readType(){
byte
[] headData =
new
byte
[HEAD_SIZE];
stream.Read(headData,0,headData.Length);
int
msgType = BitConverter.ToInt32(headData,0);
return
msgType;
}
private
int
readSize(){
byte
[] headData =
new
byte
[HEAD_SIZE];
stream.Read(headData,0,headData.Length);
int
msgSize = BitConverter.ToInt32(headData,0);
return
msgSize;
}
private
byte
[] readContent(
int
leghth){
byte
[] content =
new
byte
[leghth];
stream.Read(content,0,content.Length);
return
content;
}
public
Msg Read(){
Msg msg =
new
Msg();
msg.Type = readType();
msg.Size = readSize();
if
(msg.Size > 0) {
msg.Content = readContent(msg.Size);
}
return
msg;
}
public
void
Write(
int
msgType,
byte
[] msgContent){
byte
[] msgTypeByte = BitConverter.GetBytes(msgType);
int
msgSize = msgContent.Length;
byte
[] msgSizeByte = BitConverter.GetBytes(msgSize);
int
totalSize = HEAD_SIZE + HEAD_SIZE + msgSize;
byte
[] msgByte =
new
byte
[totalSize];
int
index = 0;
for
(
int
i = 0; i < HEAD_SIZE; i++){
// put msg type
if
(msgTypeByte.Length > i){
msgByte[index] = msgTypeByte[i];
}
index++;
}
for
(
int
i = 0; i < HEAD_SIZE; i++){
// put msg size
if
(msgTypeByte.Length > i){
msgByte[index + i] = msgSizeByte[i];
}
index++;
}
for
(
int
i = 0; i < msgSize; i++){
// put msg content
if
(msgTypeByte.Length>i){
msgByte[index + i] = msgContent[i];
}
index++;
}
stream.Write(msgByte,0,msgByte.Length);
stream.Flush();
}
}
}
|
主要用的是TcpClient,NetworkStream,BitConverter.
1
2
3
4
5
6
7
8
9
|
TcpClient client =
new
TcpClient(ip,port);
// 获取与服务器连接
NetworkStream stream = client.GetStream();
// 获取连接的流
stream.Read(buf,0,lenght);
// 读取至buf
stream.Write(buf,0,lenght);
// 写至buf
BitConverter.GetBytes(data);
// 用于将整数转为字节
BitConverter.ToInt32(data,0);
// 用于将字节转为整数
stream.Flush();
// 将流中缓存发出,而不等候
stream.Close();
// 关闭流
client.Close();
// 关闭连接
|
第三部分 protobuf-net
翻墙下载安装:http://code.google.com/p/protobuf-net/
数据结构编译成dll:
先新建解决方案,新建库,添加下载的full/unity/dll。具体代码如下:
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
|
using
System;
using
ProtoBuf;
namespace
CSProtoData
{
[ProtoContract]
public
class
Head
{
[ProtoMember(1)]
public
Int32 DataType {
get
;
set
; }
[ProtoMember(2)]
public
Int64 DataDate {
get
;
set
; }
[ProtoMember(3)]
public
byte
[] DataContent {
get
;
set
; }
}
[ProtoContract]
public
class
Number
{
[ProtoMember(1)]
public
Int32 Index {
get
;
set
; }
[ProtoMember(2)]
public
Int64 Value {
get
;
set
; }
}
public
class
Board
{
[ProtoMember(1)]
public
Int64 Rank {
get
;
set
; }
[ProtoMember(2)]
public
string
TargetName {
get
;
set
; }
[ProtoMember(3)]
public
Int64 Number {
get
;
set
; }
}
public
class
Request
{
[ProtoMember(1)]
public
string
DataType {
get
;
set
; }
[ProtoMember(2)]
public
Int64 DataDate {
get
;
set
; }
[ProtoMember(3)]
public
Int32 Start {
get
;
set
; }
[ProtoMember(4)]
public
Int32 End {
get
;
set
; }
}
}
|
编译完后,生成dll下面马上用到(同时也要拖放到unity/assets下)。
第三部分 下
因为protobuf-net的序列化和反序列化用的是jit,ios不支持jit,所以需采用编译成dll的方式来解决问题:
vs中,新建命令行程序,添加protobuf-net/full/unity/dll,添加刚生成的dll,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using
System;
using
ProtoBuf;
using
ProtoSerializer;
using
CSProtoData;
namespace
ProtoSerializer
{
class
MainClass
{
public
static
void
Main(
string
[] args)
{
var
model = ProtoBuf.Meta.TypeModel.Create();
model.Add(
typeof
(Head),
true
);
model.Add(
typeof
(Number),
true
);
model.Add(
typeof
(Board),
true
);
model.Add(
typeof
(Request),
true
);
model.Compile(
"CSProtoSerializer"
,
"CSProtoSerializer.dll"
);
}
}
}
|
这里按运行后,会在目录下生成:CSProtoSerializer.dll,一样拖放到unity/assets下。
其中typeof()的,就是proto数据类型,在上半部分有定义的内容。
第四部分 unity代码
执行完以上步骤,unity/assets下应该有这么几个dll:
protobuf-net/full/unity/dll
proto的data的dll(第三部分)
data的序列化的dll(第三部分下,运行后生成的那个)
还有用于tcp连接的dll(第二部分)
那么实际在unity当中调用的代码则是:
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
|
using
UnityEngine;
using
System.Collections;
using
TcpConnector;
using
ProtoBuf;
using
CSProtoData;
using
System.IO;
public
class
testTcp : MonoBehaviour {
// Use this for initialization
void
Start () {
Connector conn =
new
Connector();
bool
result = conn.Connect(
"127.0.0.1"
,17093);
Debug.Log(result);
Head head=
new
Head{};
head.DataType = 2;
head.DataDate = 201407312;
MemoryStream memStream =
new
MemoryStream();
ProtoBuf.Serializer.Serialize<CSProtoData.Head>(memStream, head);
byte
[] x = memStream.ToArray();
conn.Write(1,x);
conn.Write(1,x);
}
// Update is called once per frame
void
Update () {
}
}
|
新建个script,随便挂在比如camara的组件里即可。
也不知道这到底行不行,到真正工作中再验证一下了。