Netly插件分享
我觉得它简单、高效、全面。能够快速开发,官网也提供了每种通信的样例。
TCP粘包分包处理
思路:
设置缓冲区,将新数据追加到缓冲区,然后再从缓冲区中,根据拟定的数据格式进行解析。
数据格式如下:
数据流格式:数据长度 + 数据包类型 + 数据内容
数据长度:数据包类型 + 数据内容的长度
数据包类型: 定义的数据包标志
数据内容:整个数据内容采用json字符串形式,结尾增加一个识别字节0。
客户端发送消息:
csharp
void sendMessage<T>(uint type, T msg)
{
try
{
// 序列化消息
string json = JsonConvert.SerializeObject(msg);
Debug.Log($"发送的json: {json}");
byte[] msgBytes = Encoding.UTF8.GetBytes(json);
// 计算总长度(消息类型 + 消息内容 + 结尾的0)
int totalLength = sizeof(uint) + msgBytes.Length + 1; // 类型长度 + 消息长度 + 1(结尾的0)
// 使用 MemoryStream 和 BinaryWriter 构建字节数组
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write(totalLength); // 写入总长度
writer.Write(type); // 写入消息类型
writer.Write(msgBytes); // 写入消息内容
writer.Write((byte)0); // 写入结尾的0
// 发送数据
client.To.Data(stream.ToArray());
}
}
catch (Exception ex)
{
Debug.LogError("发送消息失败: " + ex.Message);
}
}
客户端接收消息:
csharp
private Dictionary<int, Action<string>> _messageHandlers = new();//处理消息
int erorrCount;
private Memory<byte> _receiveMemory = Memory<byte>.Empty; //缓冲区
void msgData(byte[] bytes)
{
if (bytes == null || bytes.Length == 0)
{
Debug.LogError("无效数据包:空数据");
return;
}
//string hexResult = "";
//foreach (byte b in bytes)
//{
// hexResult += b.ToString("X2") + " ";
//}
//Debug.Log($"消息字节: {hexResult}");
#region Memory缓冲区
// 将新数据追加到缓冲区
_receiveMemory = CombineMemory(_receiveMemory, bytes.AsMemory());
try
{
while (_receiveMemory.Length >= 8)
{
var bufferSpan = _receiveMemory.Span;
int contentLength = BitConverter.ToInt32(bufferSpan.Slice(0, 4));//数据长度
int type = BitConverter.ToInt32(bufferSpan.Slice(4, 4));// 数据类型
if (type > 100)
{
// 找到第一个 0 字节的位置
int zeroIndex = _receiveMemory.Span.IndexOf((byte)0);
if (zeroIndex >= 0)
{
_receiveMemory = _receiveMemory.Slice(zeroIndex + 1); // 移除到 0 字节之后
Debug.LogError($"数据包类型错误: {type},移除到 0 字节之后");
}
else
{
_receiveMemory = Memory<byte>.Empty; // 如果没有 0 字节,清空缓冲区
Debug.LogError($"数据包类型错误: {type},没有 0 字节,清空缓冲区");
}
break;
}
int totalPacketLength = 4 + contentLength;
int msgLength = contentLength - 5;//(减4减1)
try
{
//检查是否收到完整的数据包
if (_receiveMemory.Length >= totalPacketLength)
{
// 验证结尾字节是否为0
if (bufferSpan[totalPacketLength - 1] != 0)
{
_receiveMemory = Memory<byte>.Empty;
Debug.LogError("清除缓冲区,数据包格式错误: 缺少结尾0字节");
break;
}
var msgMemory = _receiveMemory.Slice(8, msgLength);
string msg = Encoding.UTF8.GetString(msgMemory.Span);
Debug.Log($"收到的消息:{msg}");
if (_messageHandlers.TryGetValue(type, out var handler))
{
try
{
handler(msg);//业务处理
}
catch (Exception e)
{
Debug.LogError($"业务逻辑处理报错:{e.Message}");
}
}
else
{
//Debug.LogError($"未知的消息类型: {type}");
}
//Debug.Log("从缓冲区移除已处理的数据");
_receiveMemory = _receiveMemory.Slice(totalPacketLength);
erorrCount = 0;
}
else
{
Debug.LogError($"数据不完整,类型:{type},预期长度: {totalPacketLength}, 实际长度: {_receiveMemory.Length}");
erorrCount++;
if (erorrCount >= 10)
{
erorrCount = 0;
_receiveMemory = Memory<byte>.Empty;
Debug.LogError("清除缓冲区,数据不完整超过10次");
}
break;
}
}
catch (Exception e)
{
Debug.LogError($"清除缓冲区,处理数据包时出错: {e.Message}");
_receiveMemory = Memory<byte>.Empty;
}
}
}
catch (Exception e)
{
Debug.LogError($"清除缓冲区,消息处理异常: {e.Message}");
_receiveMemory = Memory<byte>.Empty;
}
#endregion
}
// 合并两个 Memory<byte>(模拟 List<byte>.AddRange)
private Memory<byte> CombineMemory(Memory<byte> a, Memory<byte> b)
{
if (a.IsEmpty) return b;
if (b.IsEmpty) return a;
var newBuffer = new byte[a.Length + b.Length];
a.CopyTo(newBuffer.AsMemory(0, a.Length));
b.CopyTo(newBuffer.AsMemory(a.Length, b.Length));
return newBuffer;
}
区区百行代码却思考很多