서버 공부

[서버 공부]18. Packet Generator 3,4,5,6

myjeongjun 2025. 1. 27. 17:15

3에서 배운 내용은 크게 변한점은 없고 패킷 코드 Base 포맷 + Byte로 데이터가 들어왔을때 처리하는 포맷 + 패킷 유형 분리가 추가되었다.

 

        //{0}패킷 이름
        //{1}패킷 번호
        public static string packetEnumFormat =
@"{0} = {1},";
        //파일 자체애 대한 포맷
        //{0} 패킷 이름/번호 목록
        //{1} 패킷 목록
        public static string fileFormat = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public enum PacketID 
{{
    {0}
}}


{1}
";

 

Byte유형 처리 방식도 전에 소개했던 방식과 동일하다.

        //{0} 변수 이름
        //{1} 변수 형식
        public static string readByteForamt =
@"this.{0} = ({1})segment.Array[segment.Offset + count];
count += sizeof({1});

";

 

 

4편에서는 이제 자동으로 생성한 코드를 서버,클라 세션에다가 자동으로 할당해주는것을 알려준다.

 

원래 PDL.xml에 접근하는 방법은 비주얼 스튜디오에서 F5를 눌러 최종적으로 PacketGenerator.exe를 실행하고

using (XmlReader r = XmlReader.Create(pdlPath, settings))

pdlPath로 설정되어있는 경로에서 읽어왔다.

 

다른 방식으로 배치 파일을 이용해  PacketGenerator.exe을 실행시켜주고 PDL.xml에서 변경사항이 생겼어도  GenPackets.cs을 업데이트 해 줄수있다.

START ../../PacketGenerator/bin/PacketGenerator.exe ../../PacketGenerator/PDL.xml
XCOPY /Y GenPackets.cs "../../DummyClient/Packet"
XCOPY /Y GenPackets.cs "../../server/Packet"
XCOPY /Y ClientPacketManager.cs "../../DummyClient/Packet"
XCOPY /Y ServerPacketManager.cs "../../server/Packet"

여기서 주의 깊게 볼점은 아래의 코드에서 WirteAllText 함수에 절대 경로를 넣어주지않았기때문에 실행한 파일 기준으로 cs파일이 생긴다. 

즉,배치파일에서 exe를 실행하는 명령을 내렸기때문에 배치파일과 동일한 폴더에 GenPackets.cs,ClientPacketManager.cs등을 생성한다는것이다.

                string fileText = string.Format(PacketFormat.fileFormat, packetEnums, genPackets);
               File.WriteAllText("GenPackets.cs", fileText);
                string clientManagerText = string.Format(PacketFormat.managerFormat, clientRegister);
                File.WriteAllText("ClientPacketManager.cs", clientManagerText);
                string serverManagerText = string.Format(PacketFormat.managerFormat, serverRegister);
                File.WriteAllText("ServerPacketManager.cs", serverManagerText);

 

이것을 확인해보기 위해서 PacketGenerator.exe를 비주얼스튜디오에서 F5를눌러 확인해보면 exe와 같은 폴더에 cs파일이 생긴것을 볼 수있다.

직접 exe실행 전과 비주얼스튜디오에서 직접 실행 후

 

 

이제 배치파일을 실행시켜 보면 배치파일과 동일한 폴더에 cs파일들이 생겼고

밑의 XCOPY는 같은 폴더안의 파일을 기준으로 동작하기때문에 GenPacket.cs를 찾아서 저 경로에 넣어줄수있다.

/Y옵션은 "../../DummyClient/Packet"경로에갔는데 GenPacket.cs라는 폴더가 존재하면 덮어쓰는 옵션을 켜준것이다.

 

5,6편은 OnRecvPacket을 받을때 첫번째로 패킷의 종류를 switch를 이용해서 분류를하고 parsing을 진행했다.

이 강의에서 제시한 문제점은 패킷을 주고받는일이 많을텐데, 패킷의 종류도 많아지면 그만큼 switch문으로 매번 비교하면서 패킷을 분류하는작업은 비효율적이라는 점이다.

 

그렇기때문에 Dictionary로 미리 등록을 시켜두고, 효율적으로 찾는 방식을 소개한다.

 

using ServerCore;
using System;
using System.Collections.Generic;

internal 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.C_PlayerInfoReq, MakePacket<C_PlayerInfoReq>);
        _handler.Add((ushort)PacketID.C_PlayerInfoReq, PacketHandler.C_PlayerInfoReqHandler);



}

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;

    //패킷의 종류가 많아지면 switch문에서 하나씩비교하면서 내려가야햠

    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 pkt = new T();
    pkt.Read(buffer);

    Action<PacketSession, IPacket> action = null;
    if (_handler.TryGetValue(pkt.Protocol, out action)) 
    {
        action.Invoke(session, pkt);
    }

}
}

 

기존과 똑같이 작동하지만 Action을 이용해서 클라이언트 쪽에서만 필요한 패킷, 서버에서만 필요한 패킷을 분류해두는 식으로 구현이 가능하다.