Files
2026-11-07 13:30:45 +07:00

400 lines
9.6 KiB
C++

// cWorkerThread.cpp: implementation of the cWorkerThread class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "cIocpContext.h"
#include "cPacket.h"
#include "cSystem.h"
#include "cWorkerThread.h"
#include "../include/cSingleton.h"
#ifndef CONTAINING_RECORD
#define CONTAINING_RECORD(address, type, field) \
((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
cWorkerThread::cWorkerThread() : m_packet(NULL)
{
Init();
}
cWorkerThread::~cWorkerThread()
{
Destroy();
}
void cWorkerThread::Init()
{
uint32 maxPacket = cSingleton<cSystem>::GetInstance()->GetPacket();
uint32 encoding = cSingleton<cSystem>::GetInstance()->GetEncoding();
m_packet = new cPacket( encoding );
m_storage.Create( maxPacket * 4 );
}
void cWorkerThread::Destroy()
{
if(m_packet)
{
delete m_packet;
m_packet = NULL;
}
}
//////////////////////////////////////////////////////////////////////
// operation
//////////////////////////////////////////////////////////////////////
void cWorkerThread::Process()
{
BOOL success;
DWORD bytesTransferred;
cSystem* system = cSingleton<cSystem>::GetInstance();
cOverlapped* iocpOv = NULL;
cIocpContext* iocpContext = NULL;
LPWSAOVERLAPPED lpOverlapped = NULL;
cIOCP* IOCP = system->GetIOCP();
cNetwork* network = system->GetNetwork();
Trace( _T("WorkerThread :: %lu\r\n"), GetCurrentThreadId() );
while( !IsTerminated() )
{
iocpContext = NULL;
lpOverlapped = NULL;
success = IOCP->GetIocpStatus( &bytesTransferred,
reinterpret_cast<PULONG>(&iocpContext),
&lpOverlapped );
if( !lpOverlapped )
{
Trace( _T("GQCS timeout\r\n") );
continue;
}
// 이벤트 검사
iocpOv = reinterpret_cast<cOverlapped*>(lpOverlapped);
switch( iocpOv->GetOperation() )
{
case E_IO_ACCEPT : // 여기서 iocpContext는 cServerSocket객체임
{
iocpContext = CONTAINING_RECORD( lpOverlapped, cIocpContext, m_olAccept );
// Accept 요청완료
network->AcceptCompleted( iocpContext->GetPortID() );
if( !success )
{
// 접속 대기 중 오류 - backlog된 소켓이 종료됐을때
Debug( _T("WorkerThread :: E_IO_ACCEPT failed\r\n") );
network->Finish( iocpContext );
continue;
}
if( !iocpContext->IsAssociated() )
{
// 미등록된 Context를 IOCP에 등록
if( !IOCP->AssocInstance( reinterpret_cast<HANDLE>(iocpContext->GetSockHandle()),
reinterpret_cast<DWORD>(iocpContext) ) )
{
// 접속 대기 중 오류 - 이미 등록되어 있거나 기타 오류시
Debug( _T("WorkerThread :: AssocInstance failed\r\n") );
network->Finish( iocpContext );
continue;
}
iocpContext->Associate();
}
// 접속성공
iocpContext->Reset();
// Receive 요청
if( !iocpContext->ReceiveRequest() )
{
iocpContext->Disconnect();
}
OnConnect( iocpContext );
// 새로운 Accept 요청
network->AcceptReqeust( iocpContext->GetPortID() );
}
break;
case E_IO_RECEIVE :
{
if( !success || !bytesTransferred )
{
OnDisconnect( iocpContext );
continue;
}
// 패킷 처리
if( OnReceive( iocpContext, bytesTransferred ) )
{
// Receive 요청
if( !iocpContext->ReceiveRequest() )
{
OnDisconnect( iocpContext );
}
}
}
break;
case E_IO_SEND :
{
cIocpOv* sendOvl = reinterpret_cast<cIocpOv*>(lpOverlapped);
if( iocpContext )
{
// 전송버퍼 push
iocpContext->SendCompleted( sendOvl );
//Debug( _T("WorkerThread :: Sent(%lu)\r\n"), bytesTransferred );
}
if( !success )
{
Debug( _T("WorkerThread :: E_IO_SEND failed\r\n") );
}
}
break;
case E_IO_DISCONNECT :
{
if( success && iocpContext )
{
// Context를 반환하고 새로운 접속을 요청
network->Finish( iocpContext );
}
else
{
Debug( _T("WorkerThread :: E_IO_DISCONNECT failed\r\n") );
}
}
break;
default :
{
Debug( _T("WorkerThread :: Bad operation\r\n") );
}
break;
}
}
}
BOOL cWorkerThread::OnConnect( cIocpContext* iocpContext )
{
if( iocpContext->IsActive() )
{
PushCommand( EVENT_CONNECT, iocpContext );
return TRUE;
}
return FALSE;
}
BOOL cWorkerThread::OnDisconnect( cIocpContext* iocpContext )
{
if( iocpContext->IsActive() )
{
PushCommand( EVENT_CLOSE, iocpContext );
return TRUE;
}
return FALSE;
}
BOOL cWorkerThread::OnReceive( cIocpContext* iocpContext, DWORD length )
{
if( iocpContext->IsActive() )
{
//Trace( _T("[%lu]OnReceive : %lu\r\n"), GetCurrentThreadId(), length );
// 임시버퍼에 보관된 데이타와 현재받은 데이타를 복사한다
if( iocpContext->GetStorageLength() > 0 )
{
m_storage.Erase();
if( !m_storage.Copy( iocpContext->GetStorageBuffer(), iocpContext->GetStorageLength() ) )
{
OnDisconnect( iocpContext );
return FALSE;
}
if( !m_storage.Append( iocpContext->GetWsaBuffer(), length ) )
{
OnDisconnect( iocpContext );
return FALSE;
}
}
else
{
m_storage.Erase();
if( !m_storage.Copy( iocpContext->GetWsaBuffer(), length ) )
{
OnDisconnect( iocpContext );
return FALSE;
}
}
// 저장된 버퍼를 초기화
iocpContext->CleanStorage();
// 임시버퍼에 저장된 패킷을 검사하고 프로토콜 별로 나누어서 command queue에 push
return CheckPacket( iocpContext,
m_storage.GetBuffer(),
m_storage.GetLength() );
}
return FALSE;
}
BOOL cWorkerThread::CheckPacket( cIocpContext* iocpContext, uint8* buffer, uint32 length )
{
//Trace( _T("[%lu]CheckPacket : %lu\r\n"), GetCurrentThreadId(), length );
// 패킷의 헤더길이 검사
if( length < sizeof(cHeader) )
{
// 헤더보다 작은 패킷이 들어왔을 경우 저장하고 끝난다
iocpContext->StoreBuffer( buffer, length );
//Trace( _T("[%lu]very short header : %lu, total : %lu\r\n"), GetCurrentThreadId(), length, iocpContext->GetBufferLength() );
return TRUE;
}
// 헤더로 페이로드 길이 확인
cHeader* header = reinterpret_cast<cHeader*>(buffer);
Trace(_T("header : %lu, context : %lu\r\n"), header->GetSequence(), iocpContext->GetSequence());
if( !header->IsPerfect(iocpContext->GetSequence()) )
{
// 잘못된 헤더가 들어왔을때 패킷을 drop한다
OnDisconnect( iocpContext );
Debug( _T("[%lu]invalid packet : %lu\r\n"), GetCurrentThreadId() );
return FALSE;
}
if( header->GetPayload() > (m_storage.GetMaxLength()-m_storage.GetLength()) )
{
Debug( _T("[%lu]too large payload : %lu\r\n"), GetCurrentThreadId(), header->GetPayload() );
OnDisconnect( iocpContext );
return FALSE;
}
// 패킷 헤더에 따른 길이 검사
uint32 packetLength = sizeof(cHeader) + header->GetPayload();
if( length < packetLength ) // 패킷길이가 잘려서 들어왔다
{
//Trace( _T("[%lu]short than payload : %lu\r\n"), GetCurrentThreadId(), packetLength );
iocpContext->StoreBuffer( buffer, length );
Trace( _T("[%lu]very short payload : %lu, store : %lu, total : %lu\r\n"), GetCurrentThreadId(), header->GetPayload(), length, iocpContext->GetStorageLength() );
}
else if( length == packetLength ) // 패킷길이가 정확하게 들어왔다
{
Trace( _T("[%lu]good payload : %lu\r\n"), GetCurrentThreadId(), header->GetPayload() );
// decryption 후 command queue에 push
if( !PushCommand( EVENT_RECEIVE,
iocpContext,
buffer,
packetLength ) )
{
OnDisconnect( iocpContext );
return FALSE;
}
}
else if( length > packetLength ) // 패킷길이가 붙어서 들어왔다
{
Trace( _T("[%lu]too large payload : %lu\r\n"), GetCurrentThreadId(), header->GetPayload() );
// decryption 후 command queue에 push
if( !PushCommand( EVENT_RECEIVE,
iocpContext,
buffer,
packetLength ) )
{
OnDisconnect( iocpContext );
return FALSE;
}
// 다시 한번 패킷 검사
return CheckPacket( iocpContext, buffer+packetLength, length-packetLength );
}
return TRUE;
}
BOOL cWorkerThread::PushCommand( EventID eventId, cIocpContext* iocpContext, uint8* buffer, uint32 length )
{
cSystem* system = cSingleton<cSystem>::GetInstance();
cCommandQueue* commandQueue = system->GetCommandQueue();
if( !commandQueue ) return TRUE;
switch( eventId )
{
case EVENT_CONNECT :
{
commandQueue->PushCommand( iocpContext, system->GetConnectEvent() );
}
break;
case EVENT_CLOSE :
{
commandQueue->PushCommand( iocpContext, system->GetDisconnectEvent() );
}
break;
case EVENT_RECEIVE :
{
if( length == 0 )
{
// 디코딩 실패
OnDisconnect( iocpContext );
return FALSE;
}
// 시퀀스 추가
iocpContext->PopSequence();
//// 패킷의 유효성 검사
cHeader* header = reinterpret_cast<cHeader*>(buffer);
//if( !header->IsPerfect(iocpContext->GetSequence()) )
//{
// OnDisconnect( iocpContext );
// return FALSE;
//}
uint8* packet = buffer + sizeof(cHeader);
uint32 payload = length - sizeof(cHeader);
// 패킷 디코딩
uint32 result = 0;
if(TRUE == m_packet->Decode(
packet,
length - sizeof(cHeader),
header->GetSequence(),
result))
{
commandQueue->PushCommand(
iocpContext,
header->GetCommand(),
m_packet->GetOutput(),
result );
}
else
{
commandQueue->PushCommand(
iocpContext,
header->GetCommand(),
packet,
static_cast<uint16>(payload) );
}
}
break;
}
return TRUE;
}