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파일이 생긴것을 볼 수있다.


이제 배치파일을 실행시켜 보면 배치파일과 동일한 폴더에 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을 이용해서 클라이언트 쪽에서만 필요한 패킷, 서버에서만 필요한 패킷을 분류해두는 식으로 구현이 가능하다.
'서버 공부' 카테고리의 다른 글
| [서버 공부] 미니 프로젝트: 채팅 방 만들기 1편 (0) | 2025.01.30 |
|---|---|
| [서버 공부]19. Job Queue1,2 (0) | 2025.01.28 |
| [서버 공부]17.Packet Generator 1,2 (0) | 2025.01.24 |
| [서버 공부]16.PacketSession (0) | 2025.01.16 |
| [서버 공부]15.SendBuffer (0) | 2025.01.15 |