void ClientSession::AcceptCompletion() { // for test // TRACE_PERF; TRACE_THIS; CRASH_ASSERT(LThreadType == THREAD_IO_WORKER); if (1 == InterlockedExchange(&mConnected, 1)) { /// already exists? CRASH_ASSERT(false); return; } bool resultOk = true; do { if (SOCKET_ERROR == setsockopt(mSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)GIocpManager->GetListenSocket(), sizeof(SOCKET))) { printf_s("[DEBUG] SO_UPDATE_ACCEPT_CONTEXT error: %d\n", GetLastError()); resultOk = false; break; } int opt = 1; if (SOCKET_ERROR == setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(int))) { printf_s("[DEBUG] TCP_NODELAY error: %d\n", GetLastError()); resultOk = false; break; } opt = 0; if (SOCKET_ERROR == setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(int))) { printf_s("[DEBUG] SO_RCVBUF change error: %d\n", GetLastError()); resultOk = false; break; } int addrlen = sizeof(SOCKADDR_IN); if (SOCKET_ERROR == getpeername(mSocket, (SOCKADDR*)&mClientAddr, &addrlen)) { printf_s("[DEBUG] getpeername error: %d\n", GetLastError()); resultOk = false; break; } HANDLE handle = CreateIoCompletionPort((HANDLE)mSocket, GIocpManager->GetComletionPort(), (ULONG_PTR)this, 0); if (handle != GIocpManager->GetComletionPort()) { printf_s("[DEBUG] CreateIoCompletionPort error: %d\n", GetLastError()); resultOk = false; break; } } while (false); if (!resultOk) { DisconnectRequest(DR_ONCONNECT_ERROR); return; } printf_s("[DEBUG] Client Connected: IP=%s, PORT=%d\n", inet_ntoa(mClientAddr.sin_addr), ntohs(mClientAddr.sin_port)); if (false == PreRecv()) { printf_s("[DEBUG] PreRecv error: %d\n", GetLastError()); } //TEST: 요놈의 위치는 원래 C_LOGIN 핸들링 할 때 해야하는거지만 지금은 접속 완료 시점에서 테스트 ㄱㄱ //todo: 플레이어 id는 여러분의 플레이어 테이블 상황에 맞게 적절히 고쳐서 로딩하도록 static int id = 101; mPlayer.TestCreatePlayerData( L"testName" ); mPlayer.RequestLoad( id++ ); mPlayer.TestDeletePlayerData( id ); // DONE }
unsigned int WINAPI IocpManager::IoWorkerThread(LPVOID lpParam) { LThreadType = THREAD_IO_WORKER; LIoThreadId = reinterpret_cast<int>(lpParam); HANDLE hComletionPort = GIocpManager->GetComletionPort(); while (true) { DWORD dwTransferred = 0; OverlappedIOContext* context = nullptr; ULONG_PTR completionKey = 0; int ret = GetQueuedCompletionStatus(hComletionPort, &dwTransferred, (PULONG_PTR)&completionKey, (LPOVERLAPPED*)&context, GQCS_TIMEOUT); ClientSession* theClient = context ? context->mSessionObject : nullptr ; if (ret == 0 || dwTransferred == 0) { int gle = GetLastError(); /// check time out first if (gle == WAIT_TIMEOUT) continue; if (context->mIoType == IO_RECV || context->mIoType == IO_SEND ) { CRASH_ASSERT(nullptr != theClient); /// In most cases in here: ERROR_NETNAME_DELETED(64) theClient->DisconnectRequest(DR_COMPLETION_ERROR); DeleteIoContext(context); continue; } } CRASH_ASSERT(nullptr != theClient); bool completionOk = false; switch (context->mIoType) { case IO_DISCONNECT: theClient->DisconnectCompletion(static_cast<OverlappedDisconnectContext*>(context)->mDisconnectReason); completionOk = true; break; case IO_ACCEPT: theClient->AcceptCompletion(); completionOk = true; break; case IO_RECV_ZERO: completionOk = PreReceiveCompletion(theClient, static_cast<OverlappedPreRecvContext*>(context), dwTransferred); break; case IO_SEND: completionOk = SendCompletion(theClient, static_cast<OverlappedSendContext*>(context), dwTransferred); break; case IO_RECV: completionOk = ReceiveCompletion(theClient, static_cast<OverlappedRecvContext*>(context), dwTransferred); break; default: printf_s("Unknown I/O Type: %d\n", context->mIoType); CRASH_ASSERT(false); break; } if ( !completionOk ) { /// connection closing theClient->DisconnectRequest(DR_IO_REQUEST_ERROR); } DeleteIoContext(context); } return 0; }
void IOThread::DoIocpJob() { DWORD dwTransferred = 0; LPOVERLAPPED overlapped = nullptr; ULONG_PTR completionKey = 0; int ret = GetQueuedCompletionStatus(mCompletionPort, &dwTransferred, (PULONG_PTR)&completionKey, &overlapped, GQCS_TIMEOUT); if (CK_DB_RESULT == completionKey) { //-todo: DB 처리 결과가 담겨오는 경우 처리 DatabaseJobContext* dbContext = reinterpret_cast<DatabaseJobContext*>(overlapped); dbContext->OnResult(); // make switch deleter? like OverlappedIOContext in deleter... delete dbContext; return; } /// 아래로는 일반적인 I/O 처리 OverlappedIOContext* context = reinterpret_cast<OverlappedIOContext*>(overlapped); Session* remote = context ? context->mSessionObject : nullptr; if (ret == 0 || dwTransferred == 0) { /// check time out first if ( context == nullptr && GetLastError() == WAIT_TIMEOUT) return; if (context->mIoType == IO_RECV || context->mIoType == IO_SEND) { CRASH_ASSERT(nullptr != remote); /// In most cases in here: ERROR_NETNAME_DELETED(64) remote->DisconnectRequest(DR_COMPLETION_ERROR); DeleteIoContext(context); return; } } CRASH_ASSERT(nullptr != remote); bool completionOk = false; switch (context->mIoType) { case IO_CONNECT: dynamic_cast<ServerSession*>(remote)->ConnectCompletion(); completionOk = true; break; case IO_DISCONNECT: remote->DisconnectCompletion(static_cast<OverlappedDisconnectContext*>(context)->mDisconnectReason); completionOk = true; break; case IO_ACCEPT: dynamic_cast<ClientSession*>(remote)->AcceptCompletion(); completionOk = true; break; case IO_RECV_ZERO: completionOk = remote->PostRecv(); break; case IO_SEND: remote->SendCompletion(dwTransferred); if (context->mWsaBuf.len != dwTransferred) printf_s("Partial SendCompletion requested [%d], sent [%d]\n", context->mWsaBuf.len, dwTransferred); else completionOk = true; break; case IO_RECV: remote->RecvCompletion(dwTransferred); /// for test //remote->EchoBack(); remote->Handle_CS_MOVE(); // test completionOk = remote->PreRecv(); break; default: printf_s("Unknown I/O Type: %d\n", context->mIoType); CRASH_ASSERT(false); break; } if (!completionOk) { /// connection closing remote->DisconnectRequest(DR_IO_REQUEST_ERROR); } DeleteIoContext(context); }
Player::~Player() { CRASH_ASSERT(false); }
unsigned int WINAPI IocpManager::IoWorkerThread(LPVOID lpParam) { LThreadType = THREAD_IO_WORKER; LIoThreadId = reinterpret_cast<int>(lpParam); LTimer = new Timer; LLockOrderChecker = new LockOrderChecker(LIoThreadId); HANDLE hComletionPort = GIocpManager->GetComletionPort(); while (true) { /// 타이머 작업은 항상 돌리고 LTimer->DoTimerJob(); /// IOCP 작업 돌리기 DWORD dwTransferred = 0; OverlappedIOContext* context = nullptr; ULONG_PTR completionKey = 0; int ret = GetQueuedCompletionStatus(hComletionPort, &dwTransferred, (PULONG_PTR)&completionKey, (LPOVERLAPPED*)&context, GQCS_TIMEOUT); ClientSession* theClient = context ? context->mSessionObject : nullptr ; if (ret == 0 || dwTransferred == 0) { int gle = GetLastError(); /// check time out first if ( gle == WAIT_TIMEOUT ) { // 조심해! // 3주차 과제 피드백에서 sm9 가라사대 ///# timeout이라고 무조건 continue시키면 안된다, timeout인데 ret==true이고 context에 데이터가 넘어오는 경우는 어떻게? // 라고 하시니 그 경우에는 context를 확인할 수 있도록 한다 (안 그러면 GQCS_TIMEOUT가 INFINITE 아닐 경우 accept를 못 하는 상황 발생) if ( ret == 0 || context == nullptr ) continue; } if (context->mIoType == IO_RECV || context->mIoType == IO_SEND ) { CRASH_ASSERT(nullptr != theClient); /// In most cases in here: ERROR_NETNAME_DELETED(64) theClient->DisconnectRequest(DR_COMPLETION_ERROR); DeleteIoContext(context); continue; } } CRASH_ASSERT(nullptr != theClient); bool completionOk = false; switch (context->mIoType) { case IO_DISCONNECT: theClient->DisconnectCompletion(static_cast<OverlappedDisconnectContext*>(context)->mDisconnectReason); completionOk = true; break; case IO_ACCEPT: theClient->AcceptCompletion(); completionOk = true; break; case IO_RECV_ZERO: completionOk = PreReceiveCompletion(theClient, static_cast<OverlappedPreRecvContext*>(context), dwTransferred); break; case IO_SEND: completionOk = SendCompletion(theClient, static_cast<OverlappedSendContext*>(context), dwTransferred); break; case IO_RECV: completionOk = ReceiveCompletion(theClient, static_cast<OverlappedRecvContext*>(context), dwTransferred); break; default: printf_s("Unknown I/O Type: %d\n", context->mIoType); CRASH_ASSERT(false); break; } if ( !completionOk ) { /// connection closing theClient->DisconnectRequest(DR_IO_REQUEST_ERROR); } DeleteIoContext(context); } return 0; }
bool DbHelper::Initialize(const wchar_t* connInfoStr, int workerThreadCount) { //todo: mSqlConnPool, mDbWorkerThreadCount를 워커스레스 수에 맞추어 초기화 mSqlConnPool = new SQL_CONN[workerThreadCount]; CRASH_ASSERT( mSqlConnPool != nullptr ); mDbWorkerThreadCount = workerThreadCount; // WIP if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &mSqlHenv)) { printf_s("DbHelper Initialize SQLAllocHandle failed\n"); return false; } if (SQL_SUCCESS != SQLSetEnvAttr(mSqlHenv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)) { printf_s("DbHelper Initialize SQLSetEnvAttr failed\n"); return false; } /// 스레드별로 SQL connection을 풀링하는 방식. 즉, 스레드마다 SQL서버로의 연결을 갖는다. for (int i = 0; i < mDbWorkerThreadCount; ++i) { //todo: SQLAllocHandle을 이용하여 SQL_CONN의 mSqlHdbc 핸들 사용가능하도록 처리 if ( SQL_SUCCESS != SQLAllocHandle( SQL_HANDLE_DBC, mSqlHenv, &mSqlConnPool[i].mSqlHdbc ) ) { printf_s( "DbHelper Initialize SQLAllocHandle failed\n" ); return false; } // WIP SQLSMALLINT resultLen = 0; //todo: SQLDriverConnect를 이용하여 SQL서버에 연결하고 그 핸들을 SQL_CONN의 mSqlHdbc에 할당 SQLRETURN ret = SQLDriverConnect( mSqlConnPool[i].mSqlHdbc, NULL, (SQLTCHAR*)connInfoStr, SQL_NTS, NULL, resultLen, NULL, SQL_DRIVER_NOPROMPT ); // WIP if (SQL_SUCCESS != ret && SQL_SUCCESS_WITH_INFO != ret) { SQLWCHAR sqlState[1024] = { 0, } ; SQLINTEGER nativeError = 0; SQLWCHAR msgText[1024] = { 0, } ; SQLSMALLINT textLen = 0; SQLGetDiagRec(SQL_HANDLE_DBC, mSqlConnPool[i].mSqlHdbc, 1, sqlState, &nativeError, msgText, 1024, &textLen); wprintf_s(L"DbHelper Initialize SQLDriverConnect failed: %s \n", msgText); return false; } //todo: SQLAllocHandle를 이용하여 SQL_CONN의 mSqlHstmt 핸들 사용가능하도록 처리 if ( SQL_SUCCESS != SQLAllocHandle( SQL_HANDLE_STMT, mSqlConnPool[i].mSqlHdbc, &mSqlConnPool[i].mSqlHstmt ) ) { printf_s( "DbHelper Initialize SQLAllocHandle failed\n" ); return false; } // WIP } return true; }
void IOThread::DoIocpJob() { DWORD dwTransferred = 0; OverlappedIOContext* context = nullptr; ULONG_PTR completionKey = 0; int ret = GetQueuedCompletionStatus( mCompletionPort, &dwTransferred, (PULONG_PTR)&completionKey, (LPOVERLAPPED*)&context, GQCS_TIMEOUT ); if ( completionKey == THREAD_QUIT_KEY ) { mIsContinue = false; return; } ClientSession* session = context ? context->mSessionObject : nullptr; if ( ret == 0 || dwTransferred == 0 ) { int gle = GetLastError(); /// check time out first if ( gle == WAIT_TIMEOUT && context == nullptr ) { return; } if ( context->mIoType == IO_RECV || context->mIoType == IO_SEND ) { CRASH_ASSERT( nullptr != session ); /// In most cases in here: ERROR_NETNAME_DELETED(64) session->DisconnectRequest( DR_COMPLETION_ERROR ); DeleteIoContext( context ); return; } } CRASH_ASSERT( nullptr != session ); bool completionOk = false; switch ( context->mIoType ) { case IO_CONNECT: session->ConnectCompletion(); completionOk = true; break; case IO_DISCONNECT: session->DisconnectCompletion( static_cast<OverlappedDisconnectContext*>( context )->mDisconnectReason ); completionOk = true; break; case IO_RECV_ZERO: completionOk = session->PostRecv(); break; case IO_SEND: session->SendCompletion( dwTransferred ); if ( context->mWsaBuf.len != dwTransferred ) printf_s( "Partial SendCompletion requested [%d], sent [%d]\n", context->mWsaBuf.len, dwTransferred ); else completionOk = true; break; case IO_RECV: session->RecvCompletion( dwTransferred ); // 일단 테스트용 에코! // session->EchoBack(); completionOk = session->PreRecv(); break; default: printf_s( "Unknown I/O Type: %d\n", context->mIoType ); CRASH_ASSERT( false ); break; } if ( !completionOk ) { /// connection closing session->DisconnectRequest( DR_IO_REQUEST_ERROR ); } DeleteIoContext( context ); }
SmallSizeMemoryPool::SmallSizeMemoryPool( DWORD allocSize ) : mAllocSize( allocSize ) { CRASH_ASSERT( allocSize > MEMORY_ALLOCATION_ALIGNMENT ); InitializeSListHead( &mFreeList ); }
void Session::AddRef() { CRASH_ASSERT(InterlockedIncrement(&mRefCount) > 0); }
void ClientSession::ConnectCompletion() { CRASH_ASSERT(LThreadType == THREAD_IO_WORKER); if (1 == InterlockedExchange(&mConnected, 1)) { /// already exists? CRASH_ASSERT(false); return; } bool resultOk = true; do { if ( SOCKET_ERROR == setsockopt( mSocket, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0 )) { printf_s("[DEBUG] SO_UPDATE_ACCEPT_CONTEXT error: %d\n", GetLastError()); resultOk = false; break; } /* int opt = 1; if (SOCKET_ERROR == setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(int))) { printf_s("[DEBUG] TCP_NODELAY error: %d\n", GetLastError()); resultOk = false; break; } opt = 0; if (SOCKET_ERROR == setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(int))) { printf_s("[DEBUG] SO_RCVBUF change error: %d\n", GetLastError()); resultOk = false; break; } int addrlen = sizeof(SOCKADDR_IN); if (SOCKET_ERROR == getpeername(mSocket, (SOCKADDR*)&mClientAddr, &addrlen)) { printf_s("[DEBUG] getpeername error: %d\n", GetLastError()); resultOk = false; break; } HANDLE handle = CreateIoCompletionPort((HANDLE)mSocket, GIocpManager->GetComletionPort(), (ULONG_PTR)this, 0); if (handle != GIocpManager->GetComletionPort()) { printf_s("[DEBUG] CreateIoCompletionPort error: %d\n", GetLastError()); resultOk = false; break; } */ } while (false); if (!resultOk) { DisconnectRequest(DR_ONCONNECT_ERROR); return; } printf_s("[DEBUG] Client Connected: IP=%s, PORT=%d\n", inet_ntoa(mClientAddr.sin_addr), ntohs(mClientAddr.sin_port)); if (false == PreSend()) { printf_s("[DEBUG] PreSend error: %d\n", GetLastError()); } /// 타이머 테스트를 위해 10ms 후에 player 가동 ㄱㄱ //DoSyncAfter(10, mPlayer, &Player::Start, 1000); }