반응형
Serialization
public override void OnRecvPacket(ArraySegment<byte> buffer)
{
ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + 2);
Console.WriteLine($"RecvPacketId: {id}, Size: {size}");
}
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
Packet packet = new Packet() { size = 4, packetId = 7 };
//보낸다
for (int i = 0; i < 5; i++)
{
ArraySegment<byte> openSegment = SendBufferHelper.Open(4096); //최조 덩어리 생성
byte[] buffer = BitConverter.GetBytes(packet.size);
byte[] buffer2 = BitConverter.GetBytes(packet.packetId);
Array.Copy(buffer, 0, openSegment.Array, openSegment.Offset, buffer.Length);
Array.Copy(buffer2, 0, openSegment.Array, openSegment.Offset + buffer.Length, buffer2.Length);
ArraySegment<byte> sendBuff = SendBufferHelper.Close(packet.size);
Send(sendBuff);
}
}
- 위와 같이 Byte 배열에 넣고, 풀고 하는 작업
- 이를 분리해보자
Serialization 분리
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
PlayerInfoReq packet = new PlayerInfoReq() { size = 4, packetId = (ushort)PacketID.PlayerInfoReq, playerId = 1001 };
//보낸다
//for (int i = 0; i < 5; i++)
{
ArraySegment<byte> s = SendBufferHelper.Open(4096); //최조 덩어리 생성
byte[] size = BitConverter.GetBytes(packet.size);
byte[] packetId = BitConverter.GetBytes(packet.packetId);
byte[] playerId = BitConverter.GetBytes(packet.playerId);
ushort count = 0;
Array.Copy(size, 0, s.Array, s.Offset + count, 2);
count += 2;
Array.Copy(packetId, 0, s.Array, s.Offset + count, 2);
count += 2;
Array.Copy(playerId, 0, s.Array, s.Offset + count, 8);
count += 8;
ArraySegment<byte> sendBuff = SendBufferHelper.Close(count);
Send(sendBuff);
}
}
- 이렇게 카운터로 바이트 수를 추적하면서 보내는 방법이 있음
- 하지만, 비효율적이고, 뭔가 추가될 때마다 byte배열을 하나씩 더 만드는 것이 굉장히 비효율적
- 해결해보자
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
PlayerInfoReq packet = new PlayerInfoReq() { packetId = (ushort)PacketID.PlayerInfoReq, playerId = 1001 };
//보낸다
//for (int i = 0; i < 5; i++)
{
ArraySegment<byte> s = SendBufferHelper.Open(4096); //최조 덩어리 생성
ushort count = 0;
bool success = true;
//단 한 비트라도 실패했는지 검사
count += 2;
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), packet.packetId);
count += 2;
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), packet.playerId);
count += 8;
//실제 사이즈는 모든 작업이 끝난 후 마지막에 삽입하여야 정확
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset, s.Count), count);
ArraySegment<byte> sendBuff = SendBufferHelper.Close(count);
if(success)
Send(sendBuff);
}
- TryWriteBytes 키워드를 사용하여, 배열을 하나하나 만드는 작업을 대체할 수 있다.
- 배열 문제는 해결 했고, 이제 count += 하는 부분을 처리해야 한다.
- 저렇게 길게 쓰는것보단 함수로 합쳐서 호출하는게 낫지 않나?
public abstract class Packet
{ //최대한 크기를 작게 보내자
public ushort size; //2 Byte
public ushort packetId;
public abstract ArraySegment<byte> Write();
public abstract void Read(ArraySegment<byte> s);
}
class PlayerInfoReq : Packet
{
public long playerId;
public PlayerInfoReq()
{
this.packetId = (ushort)PacketID.PlayerInfoReq;
}
public override void Read(ArraySegment<byte> s)
{
ushort count = 0;
//ushort size = BitConverter.ToUInt16(s.Array, s.Offset);
count += 2;
//ushort id = BitConverter.ToUInt16(s.Array, s.Offset + count);
count += 2;
//패킷 헤더의 사이즈가 적힌 만큼만 파싱을 하기 위함
this.playerId = BitConverter.ToInt64(new ReadOnlySpan<byte>(s.Array, s.Offset + count, s.Count - count));
count += 8;
}
public override ArraySegment<byte> Write()
{
ArraySegment<byte> s = SendBufferHelper.Open(4096); //최조 덩어리 생성
ushort count = 0;
bool success = true;
//단 한 비트라도 실패했는지 검사
count += 2;
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.packetId);
count += 2;
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset + count, s.Count - count), this.playerId);
count += 8;
//실제 사이즈는 모든 작업이 끝난 후 마지막에 삽입하여야 정확
success &= BitConverter.TryWriteBytes(new Span<byte>(s.Array, s.Offset, s.Count), count);
if (success == false)
return null;
return SendBufferHelper.Close(count);
}
}
public enum PacketID
{
PlayerInfoReq = 1,
PlayerInfoOk = 2,
}
class ServerSession : Session
{
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
PlayerInfoReq packet = new PlayerInfoReq() { playerId = 1001 };
//보낸다
//for (int i = 0; i < 5; i++)
{
ArraySegment<byte> s = packet.Write();
if(s != null)
Send(s);
}
}
}
- Read측에서, ReadOnlySpan<byte> 를 활용해, 정확히 넘어온 패킷의 헤더에 있는 size만큼 역직렬화를 하도록 함
- Client 측을 반만 신뢰하여, size를 맞게 보내줬다고 가정, 아니라면 exception에 걸리도록
반응형
'C# > 네트워크' 카테고리의 다른 글
[c#][서버] Serialization (2) (0) | 2024.06.09 |
---|---|
[c#][서버] Unicode & Encoding / UTF8 vs UTF16 (0) | 2024.06.09 |
[c#][서버] PacketSession (1) | 2024.06.07 |
[c#][서버] RecvBuffer, SendBuffer (1) | 2024.06.07 |
[c#][서버] TCP vs UDP (0) | 2024.06.07 |