Files
Client/Server/RylServerProject/RylServerLibrary/Network/ClientNetwork/ClientEventHandler.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

443 lines
12 KiB
C++

#include "stdafx.h"
#include "ClientEventHandler.h"
#include <Network/Packet/PacketBase.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerPacket.h>
#include <Network/XORCrypt/XORCrypt.h>
#include <Network/ClientNetwork/Session.h>
#include <Utility/Compress/MiniLZO/miniLZOWrapper.h>
#include <Thread/ThreadMgr.h>
#include <mmsystem.h>
#include "GMMemory.h"
namespace ClientNet
{
CClientEventHandler::CClientEventHandler()
: m_lpEventHandlerMgr(0),
m_dwHandlerID(0), m_dwSessionID(0),
m_bCloseCalled(false)
{
}
CClientEventHandler::~CClientEventHandler()
{
}
bool CClientEventHandler::SendPacket(CNetworkMsgBlock* lpMsgBlock)
{
return (0 != m_lpEventHandlerMgr) ?
m_lpEventHandlerMgr->SendPacket(this, lpMsgBlock) : false;
}
CClientEventHandlerMgr::CClientEventHandlerMgr()
: m_dwMaxEventHandlerID(0), m_bExit(FALSE)
{
m_SessionMgr.SetDispatchQueue(m_LockedMsgQueue);
CThreadMgr::Run(this);
}
CClientEventHandlerMgr::~CClientEventHandlerMgr()
{
// 보유중인 모든 Handler에 대해서, Close를 호출한다.
ClientEventHanderMap::iterator pos = m_ClientEventHandlerMap.begin();
ClientEventHanderMap::iterator end = m_ClientEventHandlerMap.end();
for(; pos != end; ++pos)
{
Close(pos->second);
}
// 2초동안 컨넥션이 전부 끊기기를 기다린다.
DWORD dwLastTime = timeGetTime();
do
{
HandleEvents(0xFFFFFFFF);
Sleep(100);
}
while(timeGetTime() - dwLastTime < 5000 && !m_ClientEventHandlerMap.empty());
// 스레드 중지.
End();
CThreadMgr::Stop(this, 2000);
// 데이터 전부 삭제
pos = m_ClientEventHandlerMap.begin();
end = m_ClientEventHandlerMap.end();
for(; pos != end; ++pos)
{
delete pos->second;
}
}
void CClientEventHandlerMgr::HandleEvents(unsigned long dwProcessPerCall)
{
m_LockedMsgQueue.GetMessages(m_LocalMsgQueue, ULONG_MAX);
// 얻어온 메시지를 적당히 처리한다.
CNetworkMsgBlock* lpMsgBlock = 0;
ClientEventHanderMap::iterator pos;
ClientEventHanderMap::iterator end = m_ClientEventHandlerMap.end();
for (DWORD dwCount = 0; dwCount < dwProcessPerCall; ++dwCount)
{
lpMsgBlock = m_LocalMsgQueue.Dequeue();
if(0 == lpMsgBlock)
{
break;
}
pos = m_ClientEventHandlerMap.find(lpMsgBlock->GetID());
if(pos != end)
{
// 이미 생성된 Handler에 대한 연결이다.
CClientEventHandler* lpHandler = pos->second;
switch(lpMsgBlock->GetMsgType())
{
case NET_MSG_ACK_DISPATCH:
if (-1 == lpHandler->OnDispatch(
reinterpret_cast<PktBase*>(lpMsgBlock->rd_ptr()), lpMsgBlock->GetAddress()))
{
Close(lpHandler);
}
break;
case NET_MSG_ACK_OPEN:
{
// 능동 연결시엔 Handler가 이미 존재한다.
int nErrorCode = 0;
DWORD dwSessionID = 0;
lpMsgBlock->pop(nErrorCode);
lpMsgBlock->pop(dwSessionID);
lpHandler->SetSessionID(dwSessionID);
if(-1 == lpHandler->OnOpen(nErrorCode))
{
// 세션 열기 실패. 세션 닫기를 호출한다.
if(0 == nErrorCode)
{
Close(lpHandler);
}
}
if (0 != nErrorCode)
{
lpHandler->OnClose();
}
}
break;
case NET_MSG_ACK_CLOSE:
lpHandler->OnClose();
// 핸들러를 제거합니다.
delete lpHandler;
m_ClientEventHandlerMap.erase(pos);
end = m_ClientEventHandlerMap.end();
break;
}
}
else
{
// TODO : Accept를 통한 연결시에는, Handler가 존재하지 않는다. 관련 처리를 이곳에서 해주고,
// 메시지를 한번 더 돌려줘야 한다.(CThreadEventHandler에 EventID를 설정할 수 있도록..)
}
lpMsgBlock->SetMsgType(NET_MSG_FREE);
CNetworkMsgPool::GetInstance().FreeNetworkMsgBlock(lpMsgBlock);
}
}
bool CClientEventHandlerMgr::Connect(INET_Addr& connAddr, CClientEventHandler* lpEventHandler)
{
if (0 == lpEventHandler)
{
fprintf(stderr, "CClientEventHandlerMgr::Connect - EventHandler is NULL\n");
return false;
}
CNetworkMsgBlock* lpMsgBlock =
CNetworkMsgPool::GetInstance().GetNetworkMsgBlock(1, connAddr);
if(0 == lpMsgBlock || !lpMsgBlock->is_valid())
{
fprintf(stderr, "CClientEventHandlerMgr::Connect - NetMsgBlock Create Failed\n");
return false;
}
DWORD dwHandlerID = 0;
ClientEventHanderMap::iterator pos;
ClientEventHanderMap::iterator end = m_ClientEventHandlerMap.end();
do
{
// 빈 세션ID를 찾는다.
dwHandlerID = ++m_dwMaxEventHandlerID;
pos = m_ClientEventHandlerMap.find(dwHandlerID);
}
while(pos != end);
m_ClientEventHandlerMap.insert(
ClientEventHanderMap::value_type(dwHandlerID, lpEventHandler));
lpEventHandler->SetHandlerID(dwHandlerID);
lpEventHandler->SetPeerAddress(connAddr);
lpEventHandler->SetEventHandlerMgr(this);
lpMsgBlock->SetAddress(connAddr);
lpMsgBlock->SetMsgType(NET_MSG_REQ_TCP_OPEN);
lpMsgBlock->SetID(dwHandlerID);
m_SessionMgr.PutMessage(lpMsgBlock);
return true;
}
bool CClientEventHandlerMgr::Close(CClientEventHandler* lpEventHandler)
{
CNetworkMsgBlock* lpMsgBlock =
CNetworkMsgPool::GetInstance().GetNetworkMsgBlock(1, INET_Addr());
if(0 == lpMsgBlock || !lpMsgBlock->is_valid())
{
fprintf(stderr, "CClientEventHandlerMgr::Close - NetMsgBlock Create Failed\n");
return false;
}
lpEventHandler->SetCloseCalled();
lpMsgBlock->SetMsgType(NET_MSG_REQ_CLOSE);
lpMsgBlock->SetID(lpEventHandler->GetSessionID());
m_SessionMgr.PutMessage(lpMsgBlock);
return true;
}
bool CClientEventHandlerMgr::SendPacket(CClientEventHandler* lpEventHandler, CNetworkMsgBlock* lpMsgBlock)
{
if (0 != lpEventHandler && 0 != lpMsgBlock)
{
lpMsgBlock->SetMsgType(NET_MSG_REQ_SEND);
lpMsgBlock->SetID(lpEventHandler->GetSessionID());
m_SessionMgr.PutMessage(lpMsgBlock);
return true;
}
return false;
}
unsigned int CClientEventHandlerMgr::Run()
{
while(!InterlockedCompareExchange(&m_bExit, TRUE, TRUE))
{
m_SessionMgr.HandleEvents(1000);
}
return true;
}
BOOL CClientEventHandlerMgr::End()
{
InterlockedExchange(&m_bExit, TRUE);
return TRUE;
}
CThreadEventHandler::CThreadEventHandler(CLockedMsgQueue& LockedMsgQueue, DWORD dwClientHandlerID)
: m_LockedMsgQueue(LockedMsgQueue),
m_Crypt(CXORCrypt::GetInstance()),
m_dwClientHandlerID(dwClientHandlerID),
CEventHandler(60) // 1분마다 호출한다.
{
}
// 패킷을 받았을 때 불린다. 자체적으로 메시지 큐잉을 수행한다.
int CThreadEventHandler::OnReceived(CNetworkMsgBlock& msg_block)
{
const int MAX_PACKET_PER_RECV = 32;
char* rd_ptr = 0;
PktBase* lpPktBase = 0;
DWORD length = static_cast<unsigned long>(msg_block.length());
DWORD decompress_len = 0;
CNetworkMsgBlock* lpNewMsgBlock = 0;
CNetworkMsgPool& netMessagePool = CNetworkMsgPool::GetInstance();
CNetworkMsgQueue netMsgQueue;
while(sizeof(PktBase) <= length)
{
rd_ptr = msg_block.rd_ptr();
lpPktBase = reinterpret_cast<PktBase*>(rd_ptr);
// 패킷 헤더가 vaild한지 확인한다. invalid한 경우는 그냥 끊어버린다.
if(StartBit != lpPktBase->GetStartBit())
{
fprintf(stderr, "CClientEventHandler::OnReceived - Packet header is invalid. Not equal startbit\n");
return -1;
}
// 패킷 헤더 디코딩
m_Crypt.DecodeHeader(rd_ptr + 1, sizeof(PktBase) - 1, 0, 0);
// 헤더 길이 얻어 오기.
const PktBase::CMDType nPacketCMD = lpPktBase->GetCmd();
const PktBase::LengthType nPacketLength = lpPktBase->GetLen();
if(length < nPacketLength)
{
// 전체 스트림 사이즈가 파싱을 하기에는 모자람. 다시 인코딩 한 후 다음 턴을 기다림
m_Crypt.EncodeHeader(rd_ptr + 1, sizeof(PktBase) - 1, 0, 0);
break;
}
else
{
// 패킷 디코딩.
if(lpPktBase->IsCrypt())
{
m_Crypt.DecodePacket(rd_ptr + sizeof(PktBase),
nPacketLength - sizeof(PktBase), lpPktBase->GetCodePage());
}
// 패킷 압축 해제.
if(lpPktBase->IsCompress())
{
decompress_len = PktMaxLen - sizeof(PktBase);
lpNewMsgBlock = netMessagePool.GetNetworkMsgBlock(PktMaxLen, msg_block.GetAddress());
if(0 == lpNewMsgBlock)
{
fprintf(stderr, "CClientEventHandler::OnReceived - Create NetworkMsgBlock Failed for Decompress\n");
return -1;
}
char* szDecompressedPacket = lpNewMsgBlock->wr_ptr();
memcpy(szDecompressedPacket, lpPktBase, sizeof(PktBase));
// 로컬 버퍼에서 압축 해제.
if(!CMiniLZO::Decompress(rd_ptr + sizeof(PktBase), nPacketLength - sizeof(PktBase),
szDecompressedPacket + sizeof(PktBase), &decompress_len))
{
netMessagePool.FreeNetworkMsgBlock(lpNewMsgBlock);
fprintf(stderr, "CClientEventHandler::OnReceived - Decompress Packet Failed\n");
return -1;
}
lpNewMsgBlock->wr_ptr(decompress_len + sizeof(PktBase));
lpPktBase = reinterpret_cast<PktBase*>(lpNewMsgBlock->rd_ptr());
lpPktBase->SetLen(static_cast<PktBase::LengthType>(decompress_len + sizeof(PktBase)));
}
else
{
lpNewMsgBlock = netMessagePool.GetNetworkMsgBlock(nPacketLength, msg_block.GetAddress());
if(0 == lpNewMsgBlock)
{
fprintf(stderr, "CClientEventHandler::OnReceived - Create NetworkMsgBlock Failed for Enqueue\n");
return -1;
}
memcpy(lpNewMsgBlock->wr_ptr(), rd_ptr, nPacketLength);
lpNewMsgBlock->wr_ptr(nPacketLength);
}
lpNewMsgBlock->SetMsgType(NET_MSG_ACK_DISPATCH);
lpNewMsgBlock->SetID(m_dwClientHandlerID);
netMsgQueue.Enqueue(lpNewMsgBlock);
length -= nPacketLength;
msg_block.rd_ptr(nPacketLength);
}
}
m_LockedMsgQueue.PutMessages(netMsgQueue);
return 0;
}
int CThreadEventHandler::OnOpen(CSession& ConnSession, int iErrorCode)
{
CNetworkMsgBlock* lpNewMsgBlock =
CNetworkMsgPool::GetInstance().GetNetworkMsgBlock(
sizeof(int) + sizeof(unsigned long), INET_Addr());
if(0 == lpNewMsgBlock)
{
fprintf(stderr, "CThreadEventHandler::OnOpen - NetMsgBlock Create Failed\n");
return -1;
}
lpNewMsgBlock->SetMsgType(NET_MSG_ACK_OPEN);
lpNewMsgBlock->SetID(m_dwClientHandlerID);
lpNewMsgBlock->push(iErrorCode);
lpNewMsgBlock->push(ConnSession.GetSessionID());
m_LockedMsgQueue.PutMessage(lpNewMsgBlock);
return 0;
}
int CThreadEventHandler::OnClose(CSession& CloseSession)
{
CNetworkMsgBlock* lpNewMsgBlock =
CNetworkMsgPool::GetInstance().GetNetworkMsgBlock(1, INET_Addr());
if(0 == lpNewMsgBlock)
{
fprintf(stderr, "CThreadEventHandler::OnClose - NetMsgBlock Create Failed\n");
return -1;
}
lpNewMsgBlock->SetMsgType(NET_MSG_ACK_CLOSE);
lpNewMsgBlock->SetID(m_dwClientHandlerID);
m_LockedMsgQueue.PutMessage(lpNewMsgBlock);
return 0;
}
int CThreadEventHandler::OnTimeout(CSession& Session)
{
// Ping을 보낸다.
ClientNet::CNetworkMsgBlock* lpMsgBlock =
CNetworkMsgPool::GetInstance().GetNetworkMsgBlock(sizeof(PktSyP), INET_Addr());
if(0 != lpMsgBlock)
{
PktSyP* lpSyPAckPt = reinterpret_cast<PktSyP*>(lpMsgBlock->wr_ptr());
lpSyPAckPt->m_dwTickTime = timeGetTime();
if (lpMsgBlock->WrapCrypt(sizeof(PktSyP), CmdSysPing, 0))
{
Session.SendPending(lpMsgBlock);
}
else
{
CNetworkMsgPool::GetInstance().FreeNetworkMsgBlock(lpMsgBlock);
}
}
return 0;
}
}