개인공부) 서버실습(39) - Packet Generator(5)

Justin·2022년 7월 6일
0

서버공부

목록 보기
38/45

✅ 지난 시간

Client, Server에서 사용하는 GenPakcets의 내용을 계속 작성하고, 복사 붙여넣기 하는 과정을 배치 파일을 만들어 실행시켜주는 방법으로 자동화를 하였다. 이제 다시 이어서 더 개선할 수 있는 부분 작업을 할 예정이다.

🔧 OnRecvPacket 개선

기존


기존 코드를 보면 ClientSession의 OnRecvPacket에서 받은 패킷의 ID를 통해 Switch문에서 검사를 해주는 작업을 하였다.

하지만 갈수록 패킷의 종류는 다양해질 것이기에 수많은 Switch 문을 거치는 비요율적인 상황이 나오기에 이 부분을 패킷 타입에 맞게 자동 생성하는 코드로 변화시키고자 한다.

🔍 GenPacket 수정

우선 어떠한 방식으로 구성할지 코드를 짜보고, 그걸 자동화 하도록 할 것이다.

packetId를 포함한 interface를 구현한 뒤, 상속 받아 따로 값을 보관하도록 한다.

🔹 Interface - Protocol 상속


필수로 구현 되어야하는 Read와 Write를 포함해주고, packetId를 담아둘 Protocol이라는 변수를 선언한 뒤에 return 값으로 선언해준다.

🔹 추가 영역 자동화

이제 추가한 영역을 PacketFormat을 통해 자동화 해주면 된다.
IPacket 이라는 interface는 fileFormat 영역에 적당히 잘 넣어주고,


packetFormat 영역에서 생성한 클래스가 IPacket을 상속 받고, 자신의 protocol을 가질 수 있도록 구현해주면 된다.

interface, abstact 차이?
기본적으로 둘다 상속을 받아 내용을 구현하는 부분은 굉장히 유사하나 사용 목적에 차이가 있다.

  • abstact class는 기본적으로 클래스 이며 이를 상속, 확장하여 사용하기 위함
  • interface는 해당 인터페이스를 구현한 객체들에 대한 동일한 사용방법과 동작을 보장하기 위함
    이외에도 interface는 public이고 다중 상속이 가능하다라는 차이도 존재한다.

🧩 ClientSession - OnRecv 자동화

이제 앞서 말했던 Switch를 통해 하나하나 비교하며 처리해야하던 부분을 개선할 것이다.

우선 PacketHandler와 PacketManager 를 추가해주자.

🔸 PacketManager

PacketManager의 경우 여러번 등록하며 사용하는 것이 아닌 유일한 존재로 활용 가능하기에 싱글톤으로 등록한다.

#region Singleton
        static PacketManager _instance;
        public static PacketManager instance
        {
            get
            {
                if (_instance == null)
                    _instance = new PacketManager();
                return _instance;
            }
        }
        #endregion Singleton

OnRecvPacket()라는 함수를 만들고 ClientSession - OnRecvPacket()에서 동작하던 코드 일부를 여기로 옮겨준다.


그 뒤에 원래 동작하던 부분에서는 설정한 싱글톤을 활용하여 가볍게 동작시켜준다.

PacketManager.instance.OnRecvPacket(this, buffer);

🔸 Resiger

이제 해당 패킷이 어떤 패킷인지 알려주고, 검사하기 위한 작업들이 추가 될 것인데

우선 어떤 패킷인지 등록하고 알려줄 방식으로 Dictionary 를 사용할 것이다.

Dictionary<ushort, Action<PacketSession, ArraySegment<byte>>> _onRecv =
new Dictionary<ushort, Action<PacketSession, ArraySegment<byte>>>();

Dictionary는 키, 벨류 값으로 구분되며 _onRecv 라는 Dictionary에 키 값에는 packetId, value 값에는 패킷을 만들어 넣어줄 것이다.

추가로 _handler로 활용한 부분도 미리 제작해준다. 여기도 예시는 PlayerInfoReqHandler이지만 패킷에 따라 변경되게 변환해줄 것이다.

🔸 MakePacket() 함수 제작

MakePacket() 함수를 통해 기존에 패킷을 생성하고 Read를 해주던 작업을 할 것이다.

  • 기존에 이 부분을 변경할 것이다.

여기서 좀 생소한 개념들이 몇개 나오는데, 우선 PlayerInforReq라는 Class 타입을 받아야하기에 <T>를 활용해 제네틱 타입으로 선언한다.

받아온 T가 IPacket을 상속 받고 있으며, new() 선언이 가능한지 제약을 걸어준다.
where T : IPacket, new()

 void MakePacket<T>(PacketSession session, ArraySegment<byte> buffer) where T : IPacket, new()
        {

where T : 제약조건
Clss나 메서드 뒤에 where T : 제약조건을 선언 해주면 조건에 따라 원하는 제약을 설정해줄 수 있다.

제약 조건설명
where T : structT는 값 형식
where T : classT는 참조 형식
where T : new()T는 매개 변수가 없는 생성자가 있어야 함
where T : 부모 클래스 이름T는 부모 클래스의 자식 클래스여야 함
where T : 인터페이스 이름T는 인터페이스를 반드시 구현해야 함. 여러개의 인터페이스를 명시 가능
where T : UT는 다른 형식 매개 변수 U로부터 상속받은 클래스여야 함

여기서는 T 값을 통해 PacketId Class를 넣어줄 것이다. 그를 선언하여 Read 시켜준다.

이후 Delegate-Action을 선언하고, TryGetValue로 Dictionary에 넣어둔 값을 뽑아 out에 담아주고 handler를 실행 시켜준다.

💨 PacketHandler

이제 위에서 열심히 조립한 패킷이 들이 다 잘 도착했다면, 타입에 맞게 캐스트 해준 후 foreach를 돌며 출력 해주게 코드를 제작해주면 된다.

이 부분이 Switch문 안에 들어왔을 때 동작하던 부분이다.


이제 멀티 쓰레드에 영향을 받지 않는 곳에 Register를 등록 시켜주면 준비는 끝이다.

위와 같이 제작을 해주면 이제 어떤 패킷인지 검사하는 것이 아니라, 그 패킷에 맞게 생성을 하고 넘겨주는 방식으로 변경된 것이다.

시작 프로그램을 다시 여러개로 변경하여 테스트해보면 아주 잘 작동한다. 이후 자동화는 다음 시간에 진행될 예정이다. 이제 어떤 부분을 자동화 해야할지는 보인다. 어떻게 처리하면 될지를 빠르게 따라가 보자.

profile
인디 게임을 만들며 공부하고 있습니다.

0개의 댓글