Files
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

406 lines
10 KiB
C++

#include "stdafx.h"
#include "SessionMgr.h"
#include "ClientEventHandler.h"
#include "TCPSession.h"
#include "UDPSession.h"
#include <algorithm>
#include <ctime>
#include <mmsystem.h>
#include "GMMemory.h"
const int MAX_DEFAULT_BLOCK_NUM = 100;
const int DEFAULT_BLOCK_SIZE = 16384;
namespace ClientNet
{
CSessionMgr::CSessionMgr()
: m_dwMaxSessionID(0), m_lpLockedMsgQueue(0)
{
memset(m_hHandleSet, 0, sizeof(HANDLE) * MAXIMUM_WAIT_OBJECTS);
WSADATA wsaData;
WSAStartup(0x0202, &wsaData);
RearrangeHandleSet();
}
CSessionMgr::~CSessionMgr()
{
SessionMap::iterator end = m_SessionMap.end();
SessionMap::iterator pos = m_SessionMap.begin();
for(;pos != end; ++pos)
{
delete pos->second;
}
m_SessionMap.clear();
WSACleanup();
}
int CSessionMgr::Connect(INET_Addr& address, NetMsgType eNetMsgType, DWORD dwHandlerID)
{
if (MAXIMUM_WAIT_OBJECTS + 1 <= m_SessionMap.size())
{
// 연결이 너무 많습니다.
return -1;
}
if (0 == m_lpLockedMsgQueue)
{
// 메시지 큐가 세팅되지 않았습니다.
return -1;
}
DWORD dwSessionID = 0;
SessionMap::iterator pos;
SessionMap::iterator end = m_SessionMap.end();
do
{
// 빈 세션ID를 찾는다.
dwSessionID = ++m_dwMaxSessionID;
pos = m_SessionMap.find(dwSessionID);
}
while(pos != end);
// 차후, 타입에 따라서 UDP세션을 사용할 수 있게 한다.
CSession* lpSession = 0;
#include "GMUndefNew.h"
CEventHandler* lpEventHandler =
new (std::nothrow) CThreadEventHandler(*m_lpLockedMsgQueue, dwHandlerID);
#include "GMRedefNew.h"
if(0 == lpEventHandler)
{
// 이벤트 핸들러 생성 실패
return -1;
}
switch(eNetMsgType)
{
case NET_MSG_REQ_TCP_OPEN:
#include "GMUndefNew.h"
lpSession = new (std::nothrow) CTCPSession(dwSessionID, lpEventHandler);
#include "GMRedefNew.h"
break;
case NET_MSG_REQ_UDP_OPEN:
#include "GMUndefNew.h"
lpSession = new (std::nothrow) CUDPSession(dwSessionID, lpEventHandler);
#include "GMRedefNew.h"
break;
}
if(0 == lpSession || -1 == lpSession->Open(address))
{
delete lpSession;
return -1;
}
m_SessionMap.insert(SessionMap::value_type(dwSessionID, lpSession));
return 0;
}
void CSessionMgr::RearrangeHandleSet()
{
SessionMap::iterator pos = m_SessionMap.begin();
SessionMap::iterator end = m_SessionMap.end();
m_hHandleSet[0] = GetQueueHandle();
for (DWORD dwHandleIndex = 1; pos != end; ++pos, ++dwHandleIndex)
{
m_hHandleSet[dwHandleIndex] = pos->second->GetEvent();
}
}
int CSessionMgr::HandleEvents(DWORD dwMilliseconds)
{
SessionMap::iterator pos = m_SessionMap.begin();
SessionMap::iterator end = m_SessionMap.end();
CEventHandler* lpEventHandler = 0;
DWORD dwResult = WaitForMultipleObjects(
m_SessionMap.size() + 1, m_hHandleSet, FALSE, dwMilliseconds);
if (WAIT_FAILED != dwResult)
{
// 타임아웃 걸렸다 하더라도, 실패만 아니면 모든 멤버를 다 돌아서 체크를 함.
unsigned long dwRemoveCount = 0;
// 큐 데이터 처리를 한다.
if (-1 == HandleMsgQueue())
{
++dwRemoveCount;
}
// 패킷 처리를 한다.
for (; pos != end;)
{
bool bRemove = false;
CSession* lpSession = pos->second;
lpEventHandler = lpSession->GetEventHandler();
if(0 == lpEventHandler)
{
fprintf(stderr, "CSessionMgr::HandleEvents - Session event handler is null\n");
bRemove = true;
}
else
{
int rc = WaitForSingleObject(lpSession->GetEvent(), 0);
if(WAIT_FAILED == rc)
{
fprintf(stderr, "CSessionMgr::HandleEvents - WaitForSingleObject failed : %d\n",
WSAGetLastError());
}
else if(WAIT_TIMEOUT != rc && -1 == HandleIO(lpSession))
{
bRemove = true;
}
}
if (-1 == lpEventHandler->OnDispatch(*lpSession))
{
bRemove = true;
}
DWORD dwCurrentTime = timeGetTime();
if (lpEventHandler->IsTimeout(dwCurrentTime))
{
if(-1 == lpEventHandler->OnTimeout(*lpSession))
{
bRemove = true;
}
lpEventHandler->UpdateTimeoutCalled(dwCurrentTime);
}
if (bRemove)
{
lpEventHandler->OnDispatch(*lpSession);
lpEventHandler->OnClose(*lpSession);
lpSession->Send();
lpSession->Close();
delete lpSession;
pos = m_SessionMap.erase(pos);
++dwRemoveCount;
}
else
{
++pos;
}
}
if (0 < dwRemoveCount)
{
RearrangeHandleSet();
}
}
return 0;
}
int CSessionMgr::HandleIO(CSession* lpSession)
{
WSANETWORKEVENTS netEvents;
memset(&netEvents, 0, sizeof(WSANETWORKEVENTS));
CEventHandler* lpEventHandler = lpSession->GetEventHandler();
// Enumerate the events
int rc = WSAEnumNetworkEvents(lpSession->GetSocket(), lpSession->GetEvent(), &netEvents);
if (SOCKET_ERROR == rc)
{
fprintf(stderr, "CSessionMgr::HandleIO - WSAEnumNetworkEvents failed: %d\n", WSAGetLastError());
return -1;
}
if (netEvents.lNetworkEvents & FD_CONNECT)
{
fprintf(stderr, "CSessionMgr::HandleIO - Connect : %d\n", netEvents.iErrorCode[FD_CONNECT_BIT]);
lpEventHandler->OnOpen(*lpSession, netEvents.iErrorCode[FD_CONNECT_BIT]);
}
if (netEvents.lNetworkEvents & FD_READ)
{
// Check for read error
if (netEvents.iErrorCode[FD_READ_BIT] == 0)
{
if (-1 == lpSession->Recv())
{
return -1;
}
}
else
{
fprintf(stderr, "CSessionMgr::HandleIO - FD_READ error %d\n",
netEvents.iErrorCode[FD_READ_BIT]);
return -1;
}
}
if (netEvents.lNetworkEvents & FD_WRITE)
{
// Check for write error
if (netEvents.iErrorCode[FD_WRITE_BIT] == 0)
{
if (-1 == lpSession->Send())
{
return -1;
}
}
else
{
fprintf(stderr, "CSessionMgr::HandleIO - FD_WRITE error %d\n",
netEvents.iErrorCode[FD_WRITE_BIT]);
return -1;
}
}
if (netEvents.lNetworkEvents & FD_CLOSE)
{
if (netEvents.iErrorCode[FD_CLOSE_BIT] == 0)
{
while(1)
{
// 읽을 거 있으면 끝까지 읽는다.
rc = lpSession->Recv();
if(-1 == rc)
{
lpSession->Close();
return -1;
}
else if (rc != 0)
{
continue;
}
else
{
break;
}
}
// 보낼 거 있으면 다 보낸다.
if(-1 == lpSession->Send())
{
return -1;
}
}
else
{
fprintf(stderr, "CSessionMgr::HandleIO - FD_CLOSE error %d\n",
netEvents.iErrorCode[FD_CLOSE_BIT]);
return -1;
}
}
return 0;
}
int CSessionMgr::HandleMsgQueue()
{
CNetworkMsgQueue netMsgQueue;
GetMessages(netMsgQueue, 0xFFFFFFFF);
CNetworkMsgBlock* lpMsgBlock = 0;
DWORD dwHandleSetChange = 0;
DWORD dwEventHandlerID = 0;
SessionMap::iterator pos;
while (0 != (lpMsgBlock = netMsgQueue.Dequeue()))
{
bool bDeleteMsg = true;
switch(lpMsgBlock->GetMsgType())
{
case NET_MSG_REQ_TCP_OPEN:
case NET_MSG_REQ_UDP_OPEN:
dwEventHandlerID = lpMsgBlock->GetID();
if(0 == Connect(lpMsgBlock->GetAddress(),
lpMsgBlock->GetMsgType(), dwEventHandlerID))
{
// 연결 성공. 핸들 set이 변경되었다. (콜백은 나중에 OnOpen호출시 발생한다)
++dwHandleSetChange;
}
else
{
// 연결 시도조차 하지 못한 경우이다. 오류를 Callback으로 리턴한다.
int nError = WSAGetLastError();
if (0 == nError) { nError = INT_MAX; }
if(0 != m_lpLockedMsgQueue)
{
CNetworkMsgBlock* lpAckMsgBlock =
CNetworkMsgPool::GetInstance().GetNetworkMsgBlock(sizeof(int), INET_Addr());
if(0 != lpAckMsgBlock)
{
lpAckMsgBlock->SetID(dwEventHandlerID);
lpAckMsgBlock->SetMsgType(NET_MSG_ACK_OPEN);
lpAckMsgBlock->push(nError);
m_lpLockedMsgQueue->PutMessage(lpAckMsgBlock);
}
}
}
break;
case NET_MSG_REQ_CLOSE:
// 세션을 찾아 Shutdown/Close를 호출한다. (콜백은 나중에 OnClose호출시 발생한다)
pos = m_SessionMap.find(lpMsgBlock->GetID());
if (pos != m_SessionMap.end())
{
pos->second->Close();
}
break;
case NET_MSG_REQ_SEND:
// 세션을 찾아 패킷을 전송 큐에 넣는다. (콜백은 없다.)
pos = m_SessionMap.find(lpMsgBlock->GetID());
if (pos != m_SessionMap.end())
{
pos->second->SendPending(lpMsgBlock);
bDeleteMsg = false;
}
break;
}
if (bDeleteMsg)
{
lpMsgBlock->SetMsgType(NET_MSG_FREE);
CNetworkMsgPool::GetInstance().FreeNetworkMsgBlock(lpMsgBlock);
}
}
return (0 < dwHandleSetChange) ? -1 : 0;
}
}