Unity C# 网络学习(三)
一.区分消息类型
- 之前介绍的发送消息已经可以正常发送了,但是客户端和服务器是如何知道我们发送的和接收的是哪一条消息呢,这个时候就需要我们为协议加上一个编号
- MsgBase是继承之前的序列化基类的一个消息的发送类,里面有协议编号的虚属性,在子类继承这个类时,只需要重写这个属性就可以有不同的对应编号了
public class MsgBase : DataBase
{
public override int GetLength()
{
return 0;
}
public override byte[] GetBytes()
{
return null;
}
public override void Reading(byte[] buffer, int startIndex = 0)
{
}
public virtual int ProtoID => -1;
}
public class PlayerMsg : MsgBase
{
public int playerID;
public PlayerData playerData;
public override int GetLength()
{
return 4 + 4 + playerData.GetLength();
}
public override byte[] GetBytes()
{
var index = 0;
var buffer = new byte[GetLength()];
WriteInt(buffer,ProtoID,ref index);
WriteInt(buffer,playerID,ref index);
WriteData(buffer,playerData,ref index);
return buffer;
}
public override void Reading(byte[] buffer, int startIndex = 0)
{
this.playerID = ReadInt(buffer, ref startIndex);
this.playerData = ReadData<PlayerData>(buffer, ref startIndex);
}
public override int ProtoID => 1001;
}
public class PlayerData : DataBase
{
public string name;
public int lev;
public int atk;
public override int GetLength()
{
return 4 +
Encoding.UTF8.GetBytes(name).Length +
4 +
4;
}
public override byte[] GetBytes()
{
var index = 0;
var buffer = new byte[GetLength()];
WriteString(buffer,this.name,ref index);
WriteInt(buffer,this.lev,ref index);
WriteInt(buffer,this.atk,ref index);
return buffer;
}
public override void Reading(byte[] buffer, int startIndex = 0)
{
this.name = ReadString(buffer, ref startIndex);
this.lev = ReadInt(buffer, ref startIndex);
this.atk = ReadInt(buffer, ref startIndex);
}
}
当我们发送消息时只需要序列化后发送就好,双端共享一份协议
二.分包和黏包
- 为了解决黏包和分包的问题我们需要在协议中添加一个关于纯包体长度的参数进行通讯,用于拆分包和结合包
1.黏包
当客户端向服务器发送10次数据时,服务器接收消息时会发生黏包
当服务器接收的包是玩玩全全的黏包时,如下解析
private void HandleReceiveMsg(byte[] receiveBytes, int receiveLength)
{
int protoID = 0;
int length = 0;
int nowIndex = 0;
while (nowIndex < receiveLength)
{
protoID = BitConverter.ToInt32(receiveBytes, nowIndex);
nowIndex += 4;
length = BitConverter.ToInt32(receiveBytes, nowIndex);
nowIndex += 4;
if (protoID == 1001)
{
PlayerMsg playerMsg = new PlayerMsg();
playerMsg.Reading(receiveBytes, nowIndex);
Console.WriteLine("接收到协议:" + protoID);
}
nowIndex += length;
}
}
2.分包
当服务器接收的数据单纯为分包的数据时
private void HandleReceiveMsg(byte[] receiveBytes, int receiveLength)
{
int protoID = 0;
int length = 0;
int nowIndex = 0;
receiveBytes.CopyTo(cacheBuffer, cacheIndex );
cacheIndex += receiveLength;
while (true)
{
if (receiveLength >=8)
{
protoID = BitConverter.ToInt32(cacheBuffer, nowIndex);
nowIndex += 4;
length = BitConverter.ToInt32(cacheBuffer, nowIndex);
nowIndex += 4;
}
if(receiveLength - nowIndex >= length)
{
if (protoID == 1001)
{
PlayerMsg playerMsg = new PlayerMsg();
playerMsg.Reading(cacheBuffer, nowIndex);
Console.WriteLine("接收到协议:" + protoID);
}
nowIndex += length;
if (nowIndex == cacheIndex)
break;
}
else
{
break;
}
}
}
3.同时是黏包和分包数据
private void HandleReceiveMsg(byte[] receiveBytes, int receiveLength)
{
int protoID = 0;
int length = 0;
int nowIndex = 0;
receiveBytes.CopyTo(cacheBuffer, cacheIndex );
cacheIndex += receiveLength;
while (true)
{
length = -1;
if (cacheIndex - nowIndex >= 8)
{
protoID = BitConverter.ToInt32(cacheBuffer, nowIndex);
nowIndex += 4;
length = BitConverter.ToInt32(cacheBuffer, nowIndex);
nowIndex += 4;
}
if (cacheIndex - nowIndex >= length && length != -1)
{
if (protoID == 1001)
{
PlayerMsg playerMsg = new PlayerMsg();
playerMsg.Reading(cacheBuffer, nowIndex);
Console.WriteLine("接收到协议:" + protoID);
}
nowIndex += length;
if (nowIndex == cacheIndex)
{
cacheIndex = 0;
break;
}
}
else
{
if (length != -1)
{
nowIndex -= 8;
Array.Copy(cacheBuffer,nowIndex,cacheBuffer,0,cacheIndex - nowIndex);
cacheIndex -= nowIndex;
}
break;
}
}
}
4.黏包和分包测试
public class Lesson09 : MonoBehaviour
{
[SerializeField] private Button _button1;
[SerializeField] private Button _button2;
[SerializeField] private Button _button3;
private void Awake()
{
gameObject.AddComponent<NetMgr>();
NetMgr.Instance.Connect("127.0.0.1",8080);
}
private void Start()
{
_button1.onClick.AddListener(() =>
{
//黏包
var playerMsg1 = new PlayerMsg
{
playerID = 1,
playerData = new PlayerData
{
name = "zzs",
atk = 10,
lev = 1
}
};
var playerMsg2 = new PlayerMsg
{
playerID = 2,
playerData = new PlayerData
{
name = "ywj",
atk = 10,
lev = 1
}
};
var byte1 = playerMsg1.GetBytes();
var byte2 = playerMsg2.GetBytes();
var buffer = new byte[byte1.Length+byte2.Length];
byte1.CopyTo(buffer,0);
byte2.CopyTo(buffer,byte1.Length);
NetMgr.Instance.SendTest(buffer);
});
_button2.onClick.AddListener(async () =>
{
//分包
var playerMsg1 = new PlayerMsg
{
playerID = 1,
playerData = new PlayerData
{
name = "zzs",
atk = 10,
lev = 1
}
};
var byte1 = playerMsg1.GetBytes();
var buffer1 = new byte[10];
var buffer2 = new byte[byte1.Length - 10];
Array.Copy(byte1,0,buffer1,0,10);
Array.Copy(byte1,10,buffer2,0,buffer2.Length);
NetMgr.Instance.SendTest(buffer1);
await Task.Delay(500);
NetMgr.Instance.SendTest(buffer2);
});
_button3.onClick.AddListener(async () =>
{
//分包黏包
var playerMsg1 = new PlayerMsg
{
playerID = 1,
playerData = new PlayerData
{
name = "zzs",
atk = 10,
lev = 1
}
};
var playerMsg2 = new PlayerMsg
{
playerID = 2,
playerData = new PlayerData
{
name = "ywj",
atk = 10,
lev = 1
}
};
var byte1 = playerMsg1.GetBytes();
var byte2 = playerMsg2.GetBytes();
var byte2_1 = new byte[10];
var byte2_2 = new byte[byte2.Length - 10];
Array.Copy(byte2,0,byte2_1,0,10);
Array.Copy(byte2,10,byte2_2,0,byte2_2.Length);
var buffer1 = new byte[byte1.Length + byte2_1.Length];
byte1.CopyTo(buffer1,0);
byte2_1.CopyTo(buffer1,byte1.Length);
NetMgr.Instance.SendTest(buffer1);
await Task.Delay(500);
NetMgr.Instance.SendTest(byte2_2);
});
}
}