Files
Client/Server/NFAuthTool/NFAuthServer/NaveServer/NFServerCtrl.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

446 lines
13 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Filename: IOCPServerCtrl.cpp
// Coder : 강동명 (Edithe@chollian.net)
// Comp. : Navezine
// Compiler: Visual C++ .net
// Title : IOCP Base 컨트롤러 클래스
///////////////////////////////////////////////////////////////////////////////
#include "Global.h"
#include "NFServerCtrl.h"
#include <Nave/NFLog.h>
#include <Nave/NFSync.h>
namespace NaveServer {
// INT iMaxRecvPacket = 0;
// INT iRecvPacket = 0; // 이 객체가 클라이언트와 연결 검사
// DWORD uRecvTickCnt = 0; // 수신 Tick Count
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
NFServerCtrl::NFServerCtrl()
{
m_bServerRun = FALSE; // 서버 구동상태
m_bPause = FALSE;
m_nMaxThreadNum = (GetNumberOfProcess() * 2); // CPU수 * 2개로 Thread 수 결정
m_iPort = 0;
m_iMaxConn = 50;
m_pWorkThread = NULL; // 메인 스레드 핸들
m_hProcThread = 0;
m_hPacketThread = 0;
m_hIOCP = NULL; // IOCP 핸들
m_pUpdateManager = new NFUpdateManager();
}
NFServerCtrl::~NFServerCtrl()
{
m_bServerRun = FALSE;
_DELETE(m_pUpdateManager);
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : IOCP 핸들 생성 //
// [2]PARAMETER : void //
// [3]RETURN : HANDLE = 생성된 IOCP //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
HANDLE NFServerCtrl::CreateIOCP()
{
return CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Listen 소켓 생성 //
// [2]PARAMETER : void //
// [3]RETURN : SOCKET = 생성된 LISTEN SOCKET //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
SOCKET NFServerCtrl::CreateListenSocket(INT nServerPort, CHAR cBindQue)
{
SOCKET Socket = NULL; // a Socket Variable for using Listener
// 소켓 상태 설정 구조체 선언
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons( (short)nServerPort );
// [1] Create Listen Socket
Socket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED );
if(Socket == NULL)
return Socket;
// [2] bind listen socket
if(bind(Socket,(SOCKADDR *)&addr,sizeof(addr)) != 0)
return Socket;
// [3] listening for an concurrent incoming connections limited in 5
listen(Socket, cBindQue);
LISTENER stLin;
stLin.Init();
stLin.s = Socket;
stLin.nPort = nServerPort;
stLin.cBindQue = cBindQue;
m_vecListener.push_back(stLin);
return Socket;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Listen Socket 을 iocp 소켓에 연결 //
// [2]PARAMETER : SOCKET sckListener //
// [3]RETURN : HANDLE = 연결된 IOCP //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
HANDLE NFServerCtrl::ConnectIOCPSocket(SOCKET sckListener)
{
return CreateIoCompletionPort((HANDLE)sckListener,m_hIOCP,0,0);
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : SOCKET과련 라이브러리 활성화 //
// [2]PARAMETER : void //
// [3]RETURN : void //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
BOOL NFServerCtrl::InitSocket()
{
WSADATA wsaData; // Initialzing Variables
return (SOCKET_ERROR != WSAStartup(0x202,&wsaData)); // Start Up
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 시스템의 CPU 수를 구함 //
// [2]PARAMETER : void //
// [3]RETURN : INT = 현재 시스템의 CPU 수 반환 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
INT NFServerCtrl::GetNumberOfProcess()
{
SYSTEM_INFO si; // a System Info Structure Object
GetSystemInfo( &si ); // Get the System Information
return (INT)si.dwNumberOfProcessors; // return the number of processors
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 서버 정지 //
// [2]PARAMETER : void //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
BOOL NFServerCtrl::Stop()
{
//////////////////////////////////////////////////////////////////////
// Server Closing Process //
//////////////////////////////////////////////////////////////////////
INT nCnt;
// [01] Queue Suicide Packets into each IOCP Main Thread
for( nCnt = 0; nCnt < m_nMaxThreadNum; nCnt++ )
{
if(PostQueuedCompletionStatus(m_hIOCP,0,IOCP_SHUTDOWN,0) ==NULL)
return FALSE;
}
// [02] Wait for thread terminations
nCnt = WaitForMultipleObjects( m_nMaxThreadNum, m_pWorkThread, TRUE, 15000 );
switch ( nCnt )
{
case WAIT_TIMEOUT:
LOG_ERROR((L"Not All WorkThreads Died in Time."));
break;
case WAIT_FAILED:
LOG_ERROR((L"WAIT_FAILED, WaitForMultipleObjects()."));
break;
default:
break;
}
// [03] Close Thread Handles
if( m_pWorkThread )
{
for( nCnt = 0; nCnt < m_nMaxThreadNum; nCnt++ )
CloseHandle( m_pWorkThread[nCnt] );
_DELETE_ARRAY(m_pWorkThread);
}
// [04] Process Thread Stop and Close
m_bServerRun = FALSE;
CloseHandle(m_hProcThread);
CloseHandle(m_hPacketThread);
if(ReleaseSession() == TRUE)
{
LOG_IMPORTANT((L"shutdown session.."));
}
// [05] Close Listener Socket
int iListener = m_vecListener.size();
for(int i = 0; i < iListener; ++i)
{
m_vecListener[i].Release();
}
// [6] Close IOCP Handle
if(m_hIOCP) CloseHandle(m_hIOCP);
// [7] 소켓 라이브러리 종료
WSACleanup();
// [8] Show the Result of Close Processing
LOG_IMPORTANT((L"shutdown sequence finished.."));
return TRUE;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 서버 구동 //
// [2]PARAMETER : void //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
BOOL NFServerCtrl::Start(INT nPort, INT nMaxConn, INT nMaxThreadNum)
{
m_iPort = nPort;
m_iMaxConn = nMaxConn;
// 현재 사용되는 스레드는 Work용 스레드 CPU*2개
// 받은 패킷이나 이벤트등을 처리하는 Update용 스레드 1개를 사용한다.
// 만약 몬스터나 기타 다른 게임의 상태를 업데이트 하기위해서는
// 직접 작업용 스레드를 생성해 물리면 된다.
/*
생각하는것
IOCP용 스레드 CPU개수만큼 (IOCP를 통해 Accept, Recv, Send를 하기 위한 스레드)
EventUpdate용 스레드 1개 (패킷을 업데이트 하기 위한 큐)
게임의 상태(몬스터및기타상태)용 스레드 ?개
*/
// 0이아니면 그 개수 사용
if(nMaxThreadNum != 0)
m_nMaxThreadNum = nMaxThreadNum;
else
m_nMaxThreadNum = (GetNumberOfProcess() * 2); // CPU수 * 2개로 Thread 수 결정
// [00] 변수 및 객체 선언
INT nCnt = 0; // 루프 변수
UINT nDummy; // 쓰레기 값 처리
SOCKET skListener;
// [01] initialize socket library
if(!InitSocket()) goto Error_Procss;
// [02] Create IOCP
if((m_hIOCP = CreateIOCP()) == NULL) goto Error_Procss;
// [03] Create Listener Socket
if((skListener = CreateListenSocket(m_iPort)) == INVALID_SOCKET) goto Error_Procss;
// [04] Connect listener socket to IOCP
if(ConnectIOCPSocket(skListener) == NULL) goto Error_Procss;
// [05] 화면에 서버 정보 표시
ShowServerInfo();
// [06] Create Session..
if(CreateSession(skListener) == FALSE) goto Error_Procss;
// [07] Update Session..
if((m_pWorkThread = new HANDLE[m_nMaxThreadNum]) == NULL) goto Error_Procss; // Create thread Control Handle
for(nCnt = 0; nCnt < m_nMaxThreadNum; nCnt++) // Run Thread
{
if((m_pWorkThread[nCnt] = (HANDLE)_beginthreadex(0,0,Thread_MainEx,m_hIOCP,0,&nDummy)) == NULL) goto Error_Procss;
}
//--------------> Server Initializing has been done <---------------//
// [8] Process Thread 생성하기
m_bServerRun = TRUE;
if((m_hProcThread = (HANDLE)_beginthreadex(0,0,Process_MainEx,this,0,&nDummy)) == NULL) goto Error_Procss;
// [9] Process Thread 생성하기
if((m_hPacketThread = (HANDLE)_beginthreadex(0,0,Packet_MainEx,this,0,&nDummy)) == NULL) goto Error_Procss;
return TRUE;
Error_Procss:
Stop();
m_bServerRun = FALSE;
return FALSE;
}
VOID NFServerCtrl::ShowServerInfo()
{
WCHAR szDate[32],
szTime[32];
_tzset();
_wstrdate( szDate );
_wstrtime( szTime );
LOG_IMPORTANT((L"------------------------------------------------"));
LOG_IMPORTANT((L" %s initialized at %s, %s", L"Server", szDate, szTime) );
LOG_IMPORTANT((L"------------------------------------------------"));
//////////////////////////////////////////////////////////////////////
// Server start //
//////////////////////////////////////////////////////////////////////
LOG_IMPORTANT((L"------------------------------------------------"));
LOG_IMPORTANT((L"| SERVER START |"));
LOG_IMPORTANT((L"------------------------------------------------"));
WCHAR Ip[32];
GetLocalIP(Ip);
LOG_IMPORTANT((L"IP(%s) Port(%d)", Ip, m_iPort ));
}
VOID NFServerCtrl::Pause(BOOL bPause)
{
m_bPause = bPause;
}
VOID NFServerCtrl::Update()
{
/* if(timeGetTime() - uRecvTickCnt > 1000)
{
InterlockedExchange((LONG*)&iMaxRecvPacket,iRecvPacket);
InterlockedExchange((LONG*)&iRecvPacket,0);
InterlockedExchange((LONG*)&uRecvTickCnt,timeGetTime());
}
*/
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : IOCP처리를 담당하는 메인 스레드 //
// [2]PARAMETER : lpVoid - IOCP Handle //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
unsigned NFServerCtrl::Thread_MainEx(LPVOID lpVoid)
{
DWORD dwIoSize; // 완료처리 사이즈 얻음
ULONG lCompletionKey; // 무한 루프 탈출 용이 됨
BOOL bSuccess; // 블럭킹 처리 에러 확인
HANDLE hIOCP = (HANDLE)lpVoid; // IOCP 핸들 얻음
LPOVERLAPPED lpOverlapped; // 중첩 확장 포인터
////////////
// 무한 루프
while( TRUE )
{
// IOCP 처리를 기다리는 BLOCKING MODE
bSuccess = GetQueuedCompletionStatus(hIOCP, // IOCP Handle
&dwIoSize, // 처리 사이즈
&lCompletionKey, // 완료 키
(LPOVERLAPPED*) &lpOverlapped, // 중첩 확장
INFINITE); // Waiting Time
LPOVERLAPPEDPLUS lpOverlapPlus = (LPOVERLAPPEDPLUS)lpOverlapped;
if(bSuccess)
{
// 종료 신호가 들어왔다면, 루프 탈출
if( lCompletionKey == IOCP_SHUTDOWN ) break;
if( NULL != lpOverlapPlus )
{
///////////////////////////////////////////////
// 처리가 정상적으로 이루어진다면 이쪽으로...
lpOverlapPlus->dwBytes = dwIoSize; // 처리 데이타 Size
// 처리 변수 Cast 변환
// 속도를 위해 메인프레임에선 try~catch를 뺀다.
// 알수 없는 오류를 검사하기 위해선 Exception::EnableUnhandledExceptioinFilter(true)를 사용한다
/*
// Recv 카운트 처리
if(lpOverlapPlus->nConnState == ClientIoRead)
{
InterlockedExchange((LONG*)&iRecvPacket,iRecvPacket+1);
}
*/
NFConnection* lpClientConn = (NFConnection*) lpOverlapPlus->pClientConn;
lpClientConn->DoIo(lpOverlapPlus); // IOCP 처리 핸들링
}
}
else
{
if(!lpOverlapPlus)
{
LOG_ERROR((L"Critical Error on GetQueuedCompletionStatus()."));
}
else
{
// 처리 변수 Cast 변환
NFConnection* lpClientConn = (NFConnection*) lpOverlapPlus->pClientConn;
// 강제로 Clear해주면 안된다. (데이타가 제대로 초기화 되지 않을수도 있다)
// lpClientConn->Clear();
// LOG_ERROR(("[%04d] IOCP OverlapPlus Error, Close_Open()호출. SOCKET_ERROR, %d", lpClientConn->GetIndex(), WSAGetLastError()));
lpClientConn->SetClose_Open(lpOverlapPlus, TRUE); // 연결 해제
}
}
}
return 0;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : IOCP처리를 담당하는 메인 스레드 //
// [2]PARAMETER : lpVoid - IOCP Handle //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
unsigned NFServerCtrl::Process_MainEx(LPVOID lpVoid)
{
NFServerCtrl* pCtrl = (NFServerCtrl*)lpVoid; // IOCP 핸들 얻음
assert(pCtrl);
while( pCtrl->IsRun())
{
if(pCtrl->IsPause())
{
Sleep(1);
continue;
}
pCtrl->Update();
Sleep(1);
}
return 0;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : UpateQue처리를 담당하는 메인 스레드 //
// [2]PARAMETER : lpVoid - IOCP Handle //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
unsigned NFServerCtrl::Packet_MainEx(LPVOID lpVoid)
{
NFServerCtrl* pCtrl = (NFServerCtrl*)lpVoid; // IOCP 핸들 얻음
NFUpdateManager* pUpdateManager = NFUpdateManager::GetInstancePtr();
assert(pCtrl);
assert(pUpdateManager);
while( pCtrl->IsRun())
{
pUpdateManager->Update();
Sleep(1);
}
return 0;
}
}