UN2L4——区分消息类型

不同类型的消息辨识问题

当将序列化的2进制数据发送给对象时,对方如何区分?举例:

  • PlayerInfo​:玩家信息
  • ChatInfo​:聊天信息
  • LoginInfo​:登录信息

等等

这些数据对象序列化后是长度不同的字节数组,将它们发送给对象后,对方如何区分出他们分别是什么消息
如何选择对应的数据类反序列化它们?

区分消息思路

解决方案:为发送的信息添加标识,比如添加消息ID
在所有发送的消息的头部加上消息ID(int​、short​、byte​、long​都可以,根据实际情况选择)

举例说明:
消息构成,如果选用int​类型作为消息ID的类型,前4个字节为消息ID,而后面的字节为数据类的内容
这样每次收到消息时,先把前4个字节取出来解析为消息ID,再根据ID进行消息反序列化即可

区分消息的关键点,是在数据字节数组头部加上消息ID,只要前后端定义好统一的规则
那么我们可以通过ID来决定如何反序列化消息,并且可以决定我们应该如何处理该消息

实现

实践步骤

  1. 创建消息基类,基类继承BaseData​,基类添加获取消息ID的方法或者属性
  2. 让想要被发送的消息继承该类,实现序列化反序列化方法
  3. 修改客户端和服务端收发消息的逻辑

消息基类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class BaseMessage : BaseData
{
public override int GetBytesNum()
{
throw new System.NotImplementedException();
}

public override int Reading(byte[] bytes, int BeginIndex = 0)
{
throw new System.NotImplementedException();
}

public override byte[] Writeing()
{
throw new System.NotImplementedException();
}

public virtual int GetID()
{
return 0;
}
}

继承消息基类的示例:

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
//玩家数据类
public class PlayerData : BaseData
{
public string name;
public int atk;
public int lev;

public override int GetBytesNum()
{
return 4 + 4 + 4 + Encoding.UTF8.GetBytes(name).Length;
}

public override int Reading(byte[] bytes, int beginIndex = 0)
{
int index = beginIndex;
name = ReadString(bytes, ref index);
atk = ReadInt(bytes, ref index);
lev = ReadInt(bytes, ref index);
return index - beginIndex;
}

public override byte[] Writeing()
{
int index = 0;
byte[] bytes = new byte[GetBytesNum()];
WriteString(bytes, name, ref index);
WriteInt(bytes, atk, ref index);
WriteInt(bytes, lev, ref index);
return bytes;
}
}

public class PlayerMessage : BaseMessage
{
public int playerID;
public PlayerData playerData;

public override byte[] Writeing()
{
int index = 0;
byte[] bytes = new byte[GetBytesNum()];
WriteInt(bytes, GetID(), ref index); //先写消息ID
WriteInt(bytes, playerID, ref index); //再写消息的成员变量
WriteData(bytes, playerData, ref index);
return bytes;
}

public override int Reading(byte[] bytes, int beginIndex = 0)
{
//反序列化不需要去解析ID 因为在这一步之前 就应该把ID反序列化出来
//用来判断到底使用哪一个自定义类来反序化
int index = beginIndex;
playerID = ReadInt(bytes, ref index);
playerData = ReadData<PlayerData>(bytes, ref index);
return index - beginIndex;
}

public override int GetBytesNum()
{
return 4 + //消息ID的长度
4 + //playerID的字节数组长度
playerData.GetBytesNum(); //playerData
}

//自定义的消息ID 主要是用于区分是哪一个消息类
public override int GetID()
{
return 1001;
}
}

服务端部分代码:

1
2
3
4
5
6
7
PlayerMessage msg = new PlayerMessage();
msg.playerID = 666;
msg.playerData = new PlayerData();
msg.playerData.name = "我是唐老狮的服务端";
msg.playerData.atk = 99;
msg.playerData.lev = 50;
socketClient.Send(msg.Writeing());

客户端部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//用Receive方法接收数据
byte[] receiveBytes = new byte[1024];
int receiveNum = socket.Receive(receiveBytes);
//首先解析消息的ID,使用字节数组中的前四个字节,得到ID
int msgID = BitConverter.ToInt32(receiveBytes, 0);
switch (msgID)
{
case 1001:
PlayerMessage msg = new PlayerMessage();
msg.Reading(receiveBytes, 4);
print(msg.playerID);
print(msg.playerData.name);
print(msg.playerData.atk);
print(msg.playerData.lev);
break;
}

image