728x90
반응형
이번에는 패킷안에 스탯과 같은 구조체가 존재하고
이 구조체의 인스턴스를 저장하는 자료구조가 있는 경우에
Read와 Write를 수행할 수 있도록 코드를 수정해보겠습니다.
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 StatInfo _statInfo;
public List<StatInfo> _statInfoList = new List<StatInfo>();
public struct StatInfo
{
public int _statID;
public short _level;
public float _connectDuration;
public bool Write(Span<byte> span, ref ushort offset)
{
bool success = true;
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), this._statID);
offset += sizeof(int);
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), this._level);
offset += sizeof(short);
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), this._connectDuration);
offset += sizeof(float);
return success;
}
public void Read(ReadOnlySpan<byte> span, ref ushort count)
{
_statID = BitConverter.ToInt32(span.Slice(count, span.Length - count));
count += sizeof(int);
_level = BitConverter.ToInt16(span.Slice(count, span.Length - count));
count += sizeof(short);
_connectDuration = BitConverter.ToSingle(span.Slice(count, span.Length - count));
count += sizeof(float);
}
}
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));
count += nameLen;
// 패킷을 분해 - 바디(그중에서도 _statList)
_statInfoList.Clear();
ushort statLen = BitConverter.ToUInt16(span.Slice(count, span.Length - count));
count += sizeof(ushort);
// _statList를 순회하면서 Stat을 Read
for (int i = 0; i < statLen; i++)
{
StatInfo stat = new StatInfo();
stat.Read(span, ref count);
_statInfoList.Add(stat);
}
}
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;
// 패킷을 조립 - 바디(그중에서도 _statList)
success &= BitConverter.TryWriteBytes(span.Slice(offset, span.Length - offset), (ushort)_statInfoList.Count);
offset += sizeof(ushort);
// _statList를 순회하면서 Stat을 Write
foreach (StatInfo stat in _statInfoList)
{
success &= stat.Write(span, ref offset);
}
// 패킷 완성
success &= BitConverter.TryWriteBytes(span, offset);
if (success == false)
return null;
return SendBufferHelper.Close(offset);
}
}
패킷을 위와 같이 수정하였습니다.
StatInfo 구조체를 만들어 3개의 멤버 변수를 갖게끔 설정하였고,
StatInfo 구조체에서는 내부적으로 Read와 Write를 수행할 수 있도록 멤버 함수를 만들었습니다.
패킷에서는 StatInfo 구조체를 저장할 수 있는 List를 만들었고,
패킷의 Read와 Write에서는 해당 List를 순회하면서 List의 인덱스마다 Read와 Write를 수행합니다.
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}");
foreach (PlayerInfoRequire.StatInfo stat in playerInfoRequire._statInfoList)
{
Console.WriteLine($"{playerInfoRequire._name}`s StatID is {stat._statID}");
Console.WriteLine($"{playerInfoRequire._name}`s Level is {stat._level}");
Console.WriteLine($"{playerInfoRequire._name}`s ConnectDuration is {stat._connectDuration}");
}
}
break;
}
Console.WriteLine($"RecvPacketId : {id}, Size : {size}");
}
}
서버에서는 클라이언트로부터 보내진 패킷을 확인할 수 있도록
OnRecvPacket 함수를 위와 같이 수정하였습니다.
최종 실행결과는 다음과 같습니다.
728x90
반응형
'C#' 카테고리의 다른 글
Packet Generator #2 (0) | 2025.01.07 |
---|---|
Packet Generator #1 (0) | 2025.01.07 |
직렬화(Serialization) 3 (0) | 2025.01.06 |
UTF-8과 UTF-16 (0) | 2025.01.06 |
직렬화(Serialization) 2 (0) | 2025.01.01 |