消息类
前篇所写的CS模型只能传输字符串,在本篇文章中,尝试在服务端一次传输多种类型的数据,并在客户端接收到信息之后进行解析显示
为了实现对不同类型的数据传输,需要将不同类型的数据都转换成二进制的形式在网络中进行传输。所以要实现一个消息基类,提供各种类型数据的序列化与反序列化方法,再让需要发送的数据类继承该基类,实现其中信息的序列化等操作
using System;
using System.Text;
public abstract class BaseData
{
/// <summary>
/// 获取字节数
/// </summary>
/// <returns></returns>
public abstract int GetByteNum();
/// <summary>
/// 写入
/// </summary>
/// <returns></returns>
public abstract byte[] Writing();
/// <summary>
/// 读取
/// </summary>
/// <param name="bytes"></param>
/// <param name="index"></param>
/// <returns></returns>
public abstract int Reading(byte[] bytes, int index = 0);
protected void WriteInt(byte[] bytes, int value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, index);
index += sizeof(int);
}
protected void WriteFloat(byte[] bytes, float value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, index);
index += sizeof(float);
}
protected void WriteString(byte[] bytes, string value, ref int index)
{
byte[] b1 = Encoding.UTF8.GetBytes(value);
BitConverter.GetBytes(b1.Length).CopyTo(bytes, index);
index += sizeof(int);
b1.CopyTo(bytes, index);
index += b1.Length;
}
protected void WriteBool(byte[] bytes, bool value, ref int index)
{
BitConverter.GetBytes(value).CopyTo(bytes, index);
index += sizeof(bool);
}
protected void WriteData(byte[] bytes, BaseData data, ref int index)
{
data.Writing().CopyTo(bytes, index);
index += data.GetByteNum();
}
protected int ReadInt(byte[] bytes, ref int index)
{
int v = BitConverter.ToInt32(bytes, index);
index += sizeof(int);
return v;
}
protected string ReadString(byte[] bytes, ref int index)
{
int num = BitConverter.ToInt32(bytes, index);
index += sizeof(int);
string v = Encoding.UTF8.GetString(bytes, index, num);
index += num;
return v;
}
protected float ReadFloat(byte[] bytes, ref int index)
{
float v = BitConverter.ToSingle(bytes, index);
index += sizeof(float);
return v;
}
protected bool ReadBool(byte[] bytes, ref int index)
{
bool v = BitConverter.ToBoolean(bytes, index);
index += sizeof(bool);
return v;
}
protected T ReadData<T>(byte[] bytes, ref int index) where T : BaseData, new()
{
T data = new T();
index += data.Reading(bytes, index);
return data;
}
}
创建一个数据类,继承自消息基类,实现一下写入和读取的方法
using System;
using System.Text;
public class PlayerData : BaseData
{
public int atk;
public bool sex;
public string name;
public override int GetByteNum()
{
//int bool int(string转换成字节数组后的字节数) 字符串转字符数组后的长度 总共所占的字节数
return sizeof(int) + sizeof(bool) + sizeof(int) + Encoding.UTF8.GetBytes(name).Length;
}
public override int Reading(byte[] bytes, int beginIndex = 0)
{
int index = beginIndex;
atk = ReadInt(bytes, ref index);
sex = ReadBool(bytes, ref index);
name = ReadString(bytes, ref index);
return index - beginIndex;
}
public override byte[] Writing()
{
int index = 0;
byte[] bytes = new byte[GetByteNum()];
WriteInt(bytes, atk, ref index);
WriteBool(bytes, sex, ref index);
WriteString(bytes, name, ref index);
return bytes;
}
}
为了区别不同的消息类型,需要添加一个消息头,其中有标识类型的ID
public class BaseMsg : BaseData
{
public override int GetByteNum()
{
throw new System.NotImplementedException();
}
public override int Reading(byte[] bytes, int index = 0)
{
throw new System.NotImplementedException();
}
public override byte[] Writing()
{
throw new System.NotImplementedException();
}
public virtual int GetID()
{
return 0;//默认id为0
}
}
创建一个最终的玩家信息类,实现读取写入方法,并设置一个标识id
public class PlayerMsg : BaseMsg
{
public int playerID;
public PlayerData data;
public override int GetByteNum()
{
//消息头长度,int,PlayerData
return sizeof(int) + sizeof(int) + data.GetByteNum();
}
public override int Reading(byte[] bytes, int index = 0)
{
int _index = index;
playerID = ReadInt(bytes, ref _index);
data = ReadData<PlayerData>(bytes, ref _index);
return _index - index;
}
public override byte[] Writing()
{
int index = 0;
byte[] bytes = new byte[GetByteNum()];
WriteInt(bytes, GetID(), ref index);
WriteInt(bytes, playerID, ref index);
WriteData(bytes, data, ref index);
return bytes;
}
public override int GetID()
{
return 10086;
}
}
使用例
在服务端调用Writing方法进行序列化,然后Send。在客户端Receive后调用Reading进行反序列化
public void Send(string info)
{
if (socket != null)
{
try
{
//socket.Send(Encoding.UTF8.GetBytes(info));
PlayerMsg msg = new PlayerMsg();
msg.playerID = 114;
msg.data = new PlayerData();
msg.data.name = "123";
msg.data.atk = 122;
msg.data.sex = true;
socket.Send(msg.Writing());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
先解析消息头,获得消息类型,然后根据类型进行对应解析
private void ReceiveMsg(object obj)
{
if (socket is null) return;
while(isConnteced)
{
if (socket.Available > 0)
{
num = socket.Receive(reveiveBuffer);
int id = BitConverter.ToInt32(reveiveBuffer, 0);
switch (id)
{
case 10086:
PlayerMsg msg = new PlayerMsg();
msg.Reading(reveiveBuffer, 4);
print(msg.playerID);
print(msg.data.atk);
print(msg.data.name);
print(msg.data.sex);
break;
default:
break;
}
//receiveQue.Enqueue(Encoding.UTF8.GetString(reveiveBuffer, 0, num));
}
}
}
先启动服务端,然后在unity中运行客户端,可以看到信息解析成功了
实现了最基本的序列化反序列化通信信息的操作