728x90
반응형
이번 포스트에서는 기존의 Packet에서 Write와 Read에서 패킷을 채워주는 경우에
매번 Span을 새로 생성하였는데, 이 부분을 개선하도록 하겠습니다.
추가적으로 패킷에서 string 타입의 프로퍼티를 사용하고
이를 인코딩하는 부분도 추가하도록 하겠습니다.
이때 string 프로퍼티를 쓰고 읽는 경우에
문자열을 그대로 보내서 그때그때 필요한 배열을 할당하는 것이 아니라
우선 2바이트짜리 배열의 크기를 보내고
배열의 크기만큼 문자열을 저장할 배열을 만들어주도록 하겠습니다.
1) 서버와 클라이언트가 공통적으로 사용하는 패킷
// 패킷의 인자를 설정하는 경우, 최대한 용량을 줄여주는 것이 좋음
public abstract class Packet
{
// 패킷의 용량
public ushort _packetSize;
// 패킷의 아이디
public ushort _packetID;
// 패킷을 읽고, 패킷에 쓰기 위한 함수
public abstract void Read(ArraySegment<byte> segment);
public abstract ArraySegment<byte> Write();
}
class PlayerInfoRequire : Packet
{
public long _playerID;
public string _name;
public PlayerInfoRequire()
{
this._packetID = (ushort)PacketID.PlayerInfoRequire;
}
public override void Read(ArraySegment<byte> segment)
{
ushort count = 0;
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(segment.Array, segment.Offset, segment.Count);
// 패킷을 분해 - 헤더
count += sizeof(ushort);
count += sizeof(ushort);
// 패킷을 분해 - 바디(그중에서도 _playerID)
this._playerID = BitConverter.ToInt64(span.Slice(count, span.Length - count));
count += sizeof(long);
// 패킷을 분해 - 바디(그중에서도 _name)
ushort nameLen = BitConverter.ToUInt16(span.Slice(count, span.Length - count));
count += sizeof(ushort);
this._name = Encoding.Unicode.GetString(span.Slice(count, nameLen));
}
public override ArraySegment<byte> Write()
{
ArraySegment<byte> segment = SendBufferHelper.Open(4096);
bool success = true;
ushort offset = 0;
Span<byte> span = new Span<byte>(segment.Array, segment.Offset, segment.Count);
// 패킷을 조립 - 헤더
offset += sizeof(ushort);
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), this._packetID);
offset += sizeof(ushort);
// 패킷을 조립 - 바디(그중에서 _playerID)
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), this._playerID);
offset += sizeof(long);
// 패킷을 조립 - 바디(그중에서 _name)
ushort nameLen = (ushort)Encoding.Unicode.GetByteCount(this._name);
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), nameLen);
offset += sizeof(ushort);
Array.Copy(Encoding.Unicode.GetBytes(this._name), 0, segment.Array, offset, nameLen);
offset += nameLen;
// 패킷 완성
success &= BitConverter.TryWriteBytes(span, offset);
if (success == false)
return null;
return SendBufferHelper.Close(offset);
}
}
기존의 Read와 Write의 수행 방식을 위와 같이 변경하였습니다.
각 패킷의 프로퍼티마다 Span을 생성하는 방식에서
위와 같이 수정하여 Slice하여 생성한 Span을 나눠 사용하겠습니다.
2) 서버의 ClientSession
class ClientSession : PacketSession
{
.
.
.
public override void OnRecvPacket(ArraySegment<byte> buffer)
{
ushort count = 0;
ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
count += 2;
ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + count);
count += 2;
switch ((PacketID)id)
{
case PacketID.PlayerInfoRequire:
{
PlayerInfoRequire playerInfoRequire = new PlayerInfoRequire();
playerInfoRequire.Read(buffer);
Console.WriteLine($"PlayerInfoReq ID : {playerInfoRequire._playerID}");
Console.WriteLine($"PlayerInfoReq Name : {playerInfoRequire._name}");
}
break;
}
Console.WriteLine($"RecvPacketId : {id}, Size : {size}");
}
}
추가적으로 Server의 ClientSession에서는 클라이언트에서 전달된 패킷을 제대로 확인할 수 있도록
기존의 OnRecvPacket 함수를 수정하겠습니다.
이제 실행결과를 살펴보겠습니다.
잘 실행하는 것을 확인하였습니다.
3) 기존의 패킷 코드를 수정
사실 패킷을 구성하는 방법은 여러가지가 존재합니다.
이번에는 기존의 string 프로퍼티를 쓰기전에 해당 프로퍼티의 길이를 구하는 방식 대신,
먼저 쓰고나서 프로퍼티의 길이를 구하는 방식을 사용해보겠습니다.
// 패킷의 인자를 설정하는 경우, 최대한 용량을 줄여주는 것이 좋음
public abstract class Packet
{
// 패킷의 용량
public ushort _packetSize;
// 패킷의 아이디
public ushort _packetID;
// 패킷을 읽고, 패킷에 쓰기 위한 함수
public abstract void Read(ArraySegment<byte> segment);
public abstract ArraySegment<byte> Write();
}
class PlayerInfoRequire : Packet
{
public long _playerID;
public string _name;
public PlayerInfoRequire()
{
this._packetID = (ushort)PacketID.PlayerInfoRequire;
}
public override void Read(ArraySegment<byte> segment)
{
ushort count = 0;
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(segment.Array, segment.Offset, segment.Count);
// 패킷을 분해 - 헤더
count += sizeof(ushort);
count += sizeof(ushort);
// 패킷을 분해 - 바디(그중에서도 _playerID)
this._playerID = BitConverter.ToInt64(span.Slice(count, span.Length - count));
count += sizeof(long);
// 패킷을 분해 - 바디(그중에서도 _name)
ushort nameLen = BitConverter.ToUInt16(span.Slice(count, span.Length - count));
count += sizeof(ushort);
this._name = Encoding.Unicode.GetString(span.Slice(count, nameLen));
}
public override ArraySegment<byte> Write()
{
ArraySegment<byte> segment = SendBufferHelper.Open(4096);
bool success = true;
ushort offset = 0;
Span<byte> span = new Span<byte>(segment.Array, segment.Offset, segment.Count);
// 패킷을 조립 - 헤더
offset += sizeof(ushort);
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), this._packetID);
offset += sizeof(ushort);
// 패킷을 조립 - 바디(그중에서 _playerID)
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), this._playerID);
offset += sizeof(long);
// 패킷을 조립 - 바디(그중에서 _name)
ushort nameLen = (ushort)Encoding.Unicode.GetBytes(this._name, 0, this._name.Length, segment.Array, segment.Offset + offset + sizeof(ushort));
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), nameLen);
offset += sizeof(ushort);
offset += nameLen;
// 패킷 완성
success &= BitConverter.TryWriteBytes(span, offset);
if (success == false)
return null;
return SendBufferHelper.Close(offset);
}
}
ClientSession은 수정하지 않으므로 생략하고 실행결과를 확인해보겠습니다.
이전과 같이 문제없이 동작하는 것을 확인할 수 있습니다.
728x90
반응형
'C#' 카테고리의 다른 글
Packet Generator #1 (0) | 2025.01.07 |
---|---|
직렬화(Serialization) 4 (0) | 2025.01.06 |
UTF-8과 UTF-16 (0) | 2025.01.06 |
직렬화(Serialization) 2 (0) | 2025.01.01 |
직렬화(Serialization) 1 (1) | 2025.01.01 |