解析可变大小的数据包
Parsing variable-sized packets
我正在开发一款多人游戏,但我在解析来自连接的数据包的方式时遇到了问题。当我调试游戏时,它以较低的性能运行并且接收到数据包,当我没有调试时,数据包没有完全接收并且没有调用 ParsePacket 方法。
我的数据包结构是这样的:
2 Bytes Short Command, 2 Bytes Short Payload Size, (Optional) Payload Bytes
IInputStream inputStream = null;
DataReader dataReader = null;
byte[] data = new byte[1024];
IBuffer buffer = data.AsBuffer();
try
{
inputStream = StreamSocket.InputStream;
dataReader = DataReader.FromBuffer(buffer);
dataReader.InputStreamOptions = InputStreamOptions.Partial;
dataReader.ByteOrder = ByteOrder.LittleEndian;
while (connected)
{
await inputStream.ReadAsync(buffer, 1024, InputStreamOptions.Partial);
Debug.WriteLine("Buffer " + buffer.Length);
if (buffer.Length >= PacketHeaderSize)
{
short command = dataReader.ReadInt16();
short payloadSize = dataReader.ReadInt16();
byte[] payload = null;
if (payloadSize == 0)
{
UpdateBuffer(buffer, (uint)(PacketHeaderSize + payloadSize));
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
}
else if (payloadSize > 0)
{
if (buffer.Length >= (PacketHeaderSize + payloadSize))
{
payload = new byte[payloadSize];
dataReader.ReadBytes(payload);
UpdateBuffer(buffer, (uint)(PacketHeaderSize + payloadSize));
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
}
}
}
}
}
catch (Exception e) {
// ...
}
private void UpdateBuffer(IBuffer buffer, uint bytesRead)
{
if (buffer.Length > bytesRead)
{
byte[] bufferBytes = new byte[buffer.Length - bytesRead];
System.Buffer.BlockCopy(buffer.ToArray(), (int)bytesRead, bufferBytes, 0, (int)(buffer.Length - bytesRead));
buffer = bufferBytes.AsBuffer();
}
else
{
byte[] bufferBytes = new byte[1024];
buffer = bufferBytes.AsBuffer();
}
}
我做错了什么?
为完成这项工作而修复的问题:
- 在解析数据包循环之外获取重要变量,因为我们下次需要解析另一个数据包时需要该值。
- 如果收到不完整的应用程序或游戏数据包,请读取数据包 header 一次。
- 只加载我们需要的字节
- 使用 UnconsumedBufferLength.
完全接收后,读取 header 和有效负载
代码:
short command = 0;
short payloadSize = 0;
byte[] payload = null;
bool packetHeaderRead = false;
while (connected)
{
if (!packetHeaderRead)
{
if (dataReader.UnconsumedBufferLength < PacketHeaderSize)
{
int headerBytesLeft = PacketHeaderSize - (int)dataReader.UnconsumedBufferLength;
if (headerBytesLeft > 0)
{
await dataReader.LoadAsync((uint)headerBytesLeft);
continue;
}
}
else
{
command = dataReader.ReadInt16();
payloadSize = dataReader.ReadInt16();
packetHeaderRead = true;
continue;
}
}
else
{
int payloadBytesLeft = payloadSize - (int)dataReader.UnconsumedBufferLength;
if (payloadBytesLeft > 0)
{
await dataReader.LoadAsync((uint)payloadBytesLeft);
}
if (payloadSize == 0)
{
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
packetHeaderRead = false;
}
else if (dataReader.UnconsumedBufferLength >= payloadSize)
{
payload = new byte[payloadSize];
dataReader.ReadBytes(payload);
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
packetHeaderRead = false;
}
}
}
我正在开发一款多人游戏,但我在解析来自连接的数据包的方式时遇到了问题。当我调试游戏时,它以较低的性能运行并且接收到数据包,当我没有调试时,数据包没有完全接收并且没有调用 ParsePacket 方法。
我的数据包结构是这样的:
2 Bytes Short Command, 2 Bytes Short Payload Size, (Optional) Payload Bytes
IInputStream inputStream = null;
DataReader dataReader = null;
byte[] data = new byte[1024];
IBuffer buffer = data.AsBuffer();
try
{
inputStream = StreamSocket.InputStream;
dataReader = DataReader.FromBuffer(buffer);
dataReader.InputStreamOptions = InputStreamOptions.Partial;
dataReader.ByteOrder = ByteOrder.LittleEndian;
while (connected)
{
await inputStream.ReadAsync(buffer, 1024, InputStreamOptions.Partial);
Debug.WriteLine("Buffer " + buffer.Length);
if (buffer.Length >= PacketHeaderSize)
{
short command = dataReader.ReadInt16();
short payloadSize = dataReader.ReadInt16();
byte[] payload = null;
if (payloadSize == 0)
{
UpdateBuffer(buffer, (uint)(PacketHeaderSize + payloadSize));
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
}
else if (payloadSize > 0)
{
if (buffer.Length >= (PacketHeaderSize + payloadSize))
{
payload = new byte[payloadSize];
dataReader.ReadBytes(payload);
UpdateBuffer(buffer, (uint)(PacketHeaderSize + payloadSize));
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
}
}
}
}
}
catch (Exception e) {
// ...
}
private void UpdateBuffer(IBuffer buffer, uint bytesRead)
{
if (buffer.Length > bytesRead)
{
byte[] bufferBytes = new byte[buffer.Length - bytesRead];
System.Buffer.BlockCopy(buffer.ToArray(), (int)bytesRead, bufferBytes, 0, (int)(buffer.Length - bytesRead));
buffer = bufferBytes.AsBuffer();
}
else
{
byte[] bufferBytes = new byte[1024];
buffer = bufferBytes.AsBuffer();
}
}
我做错了什么?
为完成这项工作而修复的问题:
- 在解析数据包循环之外获取重要变量,因为我们下次需要解析另一个数据包时需要该值。
- 如果收到不完整的应用程序或游戏数据包,请读取数据包 header 一次。
- 只加载我们需要的字节
- 使用 UnconsumedBufferLength. 完全接收后,读取 header 和有效负载
代码:
short command = 0;
short payloadSize = 0;
byte[] payload = null;
bool packetHeaderRead = false;
while (connected)
{
if (!packetHeaderRead)
{
if (dataReader.UnconsumedBufferLength < PacketHeaderSize)
{
int headerBytesLeft = PacketHeaderSize - (int)dataReader.UnconsumedBufferLength;
if (headerBytesLeft > 0)
{
await dataReader.LoadAsync((uint)headerBytesLeft);
continue;
}
}
else
{
command = dataReader.ReadInt16();
payloadSize = dataReader.ReadInt16();
packetHeaderRead = true;
continue;
}
}
else
{
int payloadBytesLeft = payloadSize - (int)dataReader.UnconsumedBufferLength;
if (payloadBytesLeft > 0)
{
await dataReader.LoadAsync((uint)payloadBytesLeft);
}
if (payloadSize == 0)
{
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
packetHeaderRead = false;
}
else if (dataReader.UnconsumedBufferLength >= payloadSize)
{
payload = new byte[payloadSize];
dataReader.ReadBytes(payload);
Packet packet = new Packet(command, payloadSize, payload);
ParsePacket(packet);
packetHeaderRead = false;
}
}
}