이번 포스트에서는 기존의 패킷을 조금 더 개량하여
인터페이스를 상속받아 패킷을 더 유동적으로 사용할 수 있게 개선해보겠습니다.
현재 패킷은 자동생성되고 있으므로
자동생성하기위한 양식에 조금만 변형을 가하면 패킷을 쉽게 변경할 수 있습니다.
1) PacketFormat
class PacketFormat
{
.
.
.
interface IPacket
{{
ushort Protocol {{ get; }}
void Read(ArraySegment<byte> segment);
ArraySegment<byte> Write();
}}
{1}
";
.
.
.
// {0} 패킷 이름
// {1} 패킷 멤버
// {2} 멤버 변수 Read
// {3} 멤버 변수 Write
public static string packetFormat =
@"
class {0} : IPacket
{{
{1}
public ushort Protocol {{ get {{ return (ushort)PacketID.{0}; }} }}
.
.
.
}
따라서 기존의 PackFormat을 위와 같이 변경하도록 하겠습니다.
새로 추가한 Protocol 멤버 변수의 경우 해당하는 패킷의 PacketID를 저장하는 용도로 사용합니다.
이를 통해 따로 매번 Switch-Case를 사용하지 않고 실행하고자 하는 동작으로 적재적소에 실행가능합니다.
또 Server의 Packet 관련 작업을 수행하는 2가지의 클래스를 추가하겠습니다.
- PacketHandler : 조립된 패킷이 수행할 동작이 정의되어 있는 클래스
- PacketManager : 패킷을 조립하기 위한 동작들을 정의한 클래스
2) PacketHandler
class PacketHandler
{
// 인자는 어떤 세션에서 , 어떤 패킷이 조립되었는지를 의미함
public static void PlayerInfoRequireHandler(PacketSession session, IPacket packet)
{
PlayerInfoRequire playerInfoRequire = packet as PlayerInfoRequire;
Console.WriteLine($"{playerInfoRequire._name}`s PlayerID is {playerInfoRequire._playerID}");
foreach (PlayerInfoRequire.Stat stat in playerInfoRequire._statList)
{
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}");
}
}
}
이전의 서버에서 동작하는 ClientSession의 함수 OnRecvPacket에서
수행하던 동작들을 PacketHandler에서 수행합니다.
이때 PacketHandler의 패킷 별 함수의 이름 규칙은
해당 패킷이름 + Handler라는 이름으로 규정하였습니다.
3) PacketManager
class PacketManager
{
static PacketManager _instance;
public static PacketManager Instance
{
get
{
if(_instance == null)
_instance = new PacketManager();
return _instance;
}
}
Dictionary<ushort, Action<PacketSession, ArraySegment<byte>>> _onRecv = new Dictionary<ushort, Action<PacketSession, ArraySegment<byte>>>();
Dictionary<ushort, Action<PacketSession, IPacket>> _handler = new Dictionary<ushort, Action<PacketSession, IPacket>>();
public void Register()
{
_onRecv.Add((ushort)PacketID.PlayerInfoRequire, MakePacket<PlayerInfoRequire>);
_handler.Add((ushort)PacketID.PlayerInfoRequire, PacketHandler.PlayerInfoRequireHandler);
}
public void OnRecvPacket(PacketSession session, 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;
Action<PacketSession, ArraySegment<byte>> action = null;
if (_onRecv.TryGetValue(id, out action))
action.Invoke(session, buffer);
}
void MakePacket<T>(PacketSession session, ArraySegment<byte> buffer) where T : IPacket, new()
{
T packet = new T();
packet.Read(buffer);
Action<PacketSession, IPacket> action = null;
if (_handler.TryGetValue(packet.Protocol, out action))
action.Invoke(session, packet);
}
}
PacketManager의 경우 싱글톤 패턴으로 동작합니다.
_onRecv와 _handler라는 2개의 딕셔너리를 사용하여
해당하는 이벤트가 발생하면 해당 딕셔너리에 연결된 콜백함수를 호출하는 구조로 되어 있습니다.
함수 OnRegister에서는 _onRecv에 PacketID가 PlayerInfoRequire인 패킷을 생성하여 연결하고
동시에 _handler에 PacketID가 PlayerInfoRequire인 경우
실행할 PlayerInfoRequireHandler를 연결합니다.
함수 OnRecvPacket에서는 패킷의 헤더를 뜯어서
분석하고 PacketID에 해당하는 콜백 함수를 호출합니다.
템플릿 MakePacket에서는 템플릿 인자 T를 통해 새로운 Packet을 만들고
해당 Packet의 Protocol을 통해 해당하는 콜백 함수를 호출합니다.
4) ClientSession
class ClientSession : PacketSession
{
.
.
.
public override void OnRecvPacket(ArraySegment<byte> buffer)
{
PacketManager.Instance.OnRecvPacket(this, buffer);
}
.
.
.
}
우선, ClientSession에서는클라이언트로부터 패킷을 받는 경우 호출되는
함수 OnRecvPacket에서 PacketManager에게
ClientSession과 Recv 이벤트로 받은 버퍼를 넘겨주게 됩니다.
5) Server
class Server
{
static Listener serverListener = new Listener();
static void Main(string[] args)
{
PacketManager.Instance.Register();
.
.
.
}
}
또한 Server의 코드에서 Main 코드를 수행하는 첫 부분에서
PacketManager가 등록을 수행할 수 있도록 싱글톤을 사용하여 Register 함수를 호출하였습니다.
최종 실행 결과는 다음과 같습니다.
'C#' 카테고리의 다른 글
채팅 테스트 #1 (0) | 2025.01.21 |
---|---|
Packet Generator #6 (0) | 2025.01.20 |
Packet Generator #4 (0) | 2025.01.08 |
Packet Generator #3 (0) | 2025.01.08 |
Packet Generator #2 (0) | 2025.01.07 |