C#/네트워크
[c#][서버] PacketGenerator (3)
goliot
2024. 6. 12. 17:50
반응형
파일 참조 자동화
- 지금까지는 파일이나 xml을 만들고 수동으로 반영을 했어야 했다.
- 이제 경로를 설정하여 생성된 파일을 자동으로 참조하도록 해보자
.bat 파일 작성
- .bat파일을 생성하고 각자 경로에 맞게 다음과 같이 수정
START ../../PacketGenerator/bin/Debug/PacketGenerator.exe ../../PacketGenerator/PDL.xml
XCOPY /Y GenPackets.cs "../../DummyClient/Packet"
XCOPY /Y GenPackets.cs "../../Server/Packet"
- 뒤쪽에 있는 인자가 main의 args에 들어가서 실행되게 된다.
- XCOPY는 파일을 복사해서 넣는다는 것이고
- /Y 옵션을 넣으면 동일 파일 존재할 시 덮어쓰기 이다
- 생성 bat파일을 실행시켜보면?
- 바로 밑에 GenPackets.cs가 생겨나고, XCOPY에 설정한 경로에도 복사되어 있
Packet 개선
switch((PacketID)id)
{
case PacketID.PlayerInfoReq:
{
PlayerInfoReq p = new PlayerInfoReq();
p.Read(buffer);
Console.WriteLine($"PlayerInfoReq: {p.playerId} {p.name}");
foreach(PlayerInfoReq.Skill skill in p.skills)
{
Console.WriteLine($"Skill({skill.id})({skill.level})({skill.duration})");
}
}
break;
}
- 패킷을 읽을 때 ClientSession의 이 부분에서 case가 많아지면 많아질수록 성능 저하로 이어질 것이다
- OnRecvPacket 함수의 내부를 이 한줄로 대체하고, PacketManager, PacketHandler를 만든다
- PacketManager.Instance.onRecvPacket(this, buffer);
namespace Server
{
class PacketHandler
{
public static void PlayerInfoReqHandler(PacketSession session, IPacket packet)
{
PlayerInfoReq p = packet as PlayerInfoReq;
Console.WriteLine($"PlayerInfoReq: {p.playerId} {p.name}");
foreach (PlayerInfoReq.Skill skill in p.skills)
{
Console.WriteLine($"Skill({skill.id})({skill.level})({skill.duration})");
}
}
}
class PacketManager
{
#region Singleton
static PacketManager _instance;
public static PacketManager Instance
{
get
{
if(_instance == null )
_instance = new PacketManager();
return _instance;
}
}
#endregion
//Protocol ID, 작업
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.PlayerInfoReq, MakePacket<PlayerInfoReq>);
_handler.Add((ushort)PacketID.PlayerInfoReq, PacketHandler.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;
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);
}
}
}
- 새로운 패킷 종류가 생긴다면, 해당 패킷 형식을 처리하는 인터페이스를 PacketHandler에다가 파주면 자동으로 찾아서 갈 것이다.
- PacketManager는 자동화가 가능하니 PacketFormat에 넣어주자
Packet 안전성
- 사실 DummyClient에서는 PacketHandler안의 PlayerInfoReqHandler가 정의될 필요가 없다.
- 이건 서버에서 온 패킷을 읽을때 쓰는 것이다.
- 그러므로, 이런 필요 없는 것들이 정의되지 않아야, 조금이라도 해킹에 안전성을 높일 수 있다.
- 이런 저런 패킷 이름으로 패킷을 쏘다가 우연히 딱 걸릴 확률을 줄이자.
- 또한, 패킷의 용도, 사용처 등등의 안전처리를 해두고, 매니저에는 딱 필요한 것만 추가해야 한다.
- 패킷 정의 수정
<packet name="C_PlayerInfoReq">
...
</packet>
<packet name="S_Test">
...
</packet>
- 위와 같이 C가 붙은 것은 클라 -> 서버로 보내는 것 = 서버 쪽 매니저에 등록
- S가 붙은 것은 서버 -> 클라로 보내는 것 = 클라 쪽 매니저에 등록하게 한다
if(packetName.StartsWith("S_") || packetName.StartsWith("s_"))
clientRegister += string.Format(PacketFormat.managerRegisterFormat, packetName) + Environment.NewLine;
else
serverRegister += string.Format(PacketFormat.managerRegisterFormat, packetName) + Environment.NewLine;
- 위와 같이 코드 작성시 필터를 하나 씌워주고
File.WriteAllText("ClientPacketManager.cs", clientManagerText);
string serverManagerText = string.Format(PacketFormat.managerFormat, serverRegister);
File.WriteAllText("ServerPacketManager.cs", serverManagerText);
- 위와 같이 파일을 각각 따로 작성하게 한다, 물론 .bat파일도 수정해야 한다.
반응형