bool SessionManager::AcceptSessions() { FastSpinlockGuard guard(mLock); while (mCurrentIssueCount - mCurrentReturnCount < MAX_CONNECTION) { //TODO mFreeSessionList에서 ClientSession* 꺼내서 PostAccept() 해주기.. (위의 ReturnClientSession와 뭔가 반대로 하면 될 듯?) ClientSession* newClient = mFreeSessionList.front(); mFreeSessionList.pop_front(); // pop_back하면 1개일때 에러가...?? // AddRef()도 당연히 해줘야 하고... newClient->AddRef(); // 실패시 false //if (false == newClient->PostAccept()) // return false; if( false == newClient->PostAccept() ){ if( GetLastError() == ERROR_IO_PENDING ) ++mCurrentIssueCount; else return false; } else ++mCurrentIssueCount; } return true; }
bool IocpManager::StartAcceptLoop() { /// listen if (SOCKET_ERROR == listen(mListenSocket, SOMAXCONN)) return false; /// accept loop while (true) { SOCKET acceptedSock = accept(mListenSocket, NULL, NULL); if (acceptedSock == INVALID_SOCKET) { printf_s("accept: invalid socket\n"); continue; } SOCKADDR_IN clientaddr; int addrlen = sizeof(clientaddr); getpeername(acceptedSock, (SOCKADDR*)&clientaddr, &addrlen); /// 소켓 정보 구조체 할당과 초기화 ClientSession* client = GSessionManager->CreateClientSession(acceptedSock); /// 클라 접속 처리 if (false == client->OnConnect(&clientaddr)) { client->Disconnect(DR_ONCONNECT_ERROR); GSessionManager->DeleteClientSession(client); } } return true; }
void ClientSessionManager::ReturnClientSession(const std::size_t sessionId) { const auto f = [=] { decltype(sessionId) size = mClientSessionList.size(); if (size > sessionId && mClientSessionList[sessionId]) { ClientSession* client = mClientSessionList[sessionId]; CRASH_ASSERT(client->mConnected == 0 && client->mRefCount == 0); // std::string ip = boost::lexical_cast<std::string>(client->GetSocket().remote_endpoint()); // BOOST_LOG_TRIVIAL(info) << "클라이언트 접속 종료. " << client->GetSessionId() << ":" << ip; // std::cout << "클라이언트 접속 종료. " << client->GetSessionId() << ":" << ip << std::endl; client->Reset(); mClientSessionQueue.insert(static_cast<int>(sessionId)); ++mCurrentReturnCount; if (!mIsAccepting) { AcceptClientSession(); } } }; auto task = mWrapper.wrap(f); mDispatcher.post(task); }
void ForServerMsgHandler::NofityClientExit(BaseSession* pSession, const NetMsgHead* pMsg,int32 nSize) { const SSNofityClientExit* pPacket = static_cast<const SSNofityClientExit*>(pMsg); int32 nClientSessionID = pPacket->nClientSessionID; ClientSession* pClientSession = ClientSessionMgr::Instance()->GetSession(nClientSessionID); ASSERT(pClientSession); // 同步或保存重要数据到dp // 通知ls,ss SSNofityClientExit sMsgExit; sMsgExit.nClientSessionID = nClientSessionID; pClientSession->SendMsgToLs(&sMsgExit,sMsgExit.GetPackLength()); if(pClientSession->Status() == ECLIENT_STATUS_IN_SCENE) { pClientSession->SendMsgToSs(&sMsgExit,sMsgExit.GetPackLength()); }else { // 由dp去保存退出操作 pClientSession->SendMsgToDp(&sMsgExit,sMsgExit.GetPackLength()); } // 删除 ClientSessionMgr::Instance()->RemoveSession(nClientSessionID); }
CErrno NetThread::StartupClient(const Json::Value & clients) { INT32 nCount = clients.size(); for (INT32 i = 0 ;i < nCount; ++ i) { Json::Value client = clients[i]; INT32 bReconnect = client.get("reconnect", 1).asInt(); std::string strType = client.get("type" , "tcp").asCString(); std::string strAddress = client.get("address", "127.0.0.1").asCString(); INT32 nPort = client.get("port", 0000).asInt(); INT32 nSendBuf = client.get("send_buf", DEFAULT_SOCKET_BUFFER_SIZE).asInt(); INT32 nRecvBuf = client.get("recv_buf", DEFAULT_SOCKET_BUFFER_SIZE).asInt(); INetHandlerPtr pNetHandler = CreateClientHandler(m_strNetNodeName, "" , strAddress.c_str(), nPort); if (!pNetHandler) { return CErrno::Failure(); } ClientSession * pSession = dynamic_cast<ClientSession*>(pNetHandler->GetSession()); if (pSession) { pSession->SetReconnect(bReconnect); NetSocket objSocket = pSession->GetSocket(); if (objSocket != -1) { NetHelper::SetDefaultSocket(objSocket, nSendBuf, nRecvBuf); pSession->SetSendBufSize(nSendBuf); pSession->SetSendBufSize(nRecvBuf); } } } return CErrno::Success(); }
bool IOCP_Manager::StartAcceptLoop() { if (listen(mListenSocket, SOMAXCONN) == SOCKET_ERROR) { printf_s("Listen Error\n"); return false; } while (true) { SOCKET acceptedSock = accept(mListenSocket, NULL, NULL); if (acceptedSock == SOCKET_ERROR) { printf_s("accept : invalid socket\n"); continue; } SOCKADDR_IN clientaddr; int addrlen = sizeof(SOCKADDR_IN); getpeername(acceptedSock, (SOCKADDR*)&clientaddr, &addrlen); ClientSession* client = GSessionManager->CreateClientSession(acceptedSock); if (client->OnConnect(&clientaddr) == false) { client->Disconnect(DisconnectReason::DR_CONNECT_ERROR); GSessionManager->DeleteClientSession(client); } } return true; }
unsigned int WINAPI ThreadProc(LPVOID _hComport) { HANDLE hComport = (HANDLE)_hComport; ClientSession* clientSession; DWORD bytesTrans = 0; IOData* ioData = nullptr; while(1) { if(!GetQueuedCompletionStatus(hComport, &bytesTrans, (PULONG_PTR) &clientSession, (LPOVERLAPPED*) &ioData, INFINITE)) { ErrorHandling("GetQueuedCompletionStatus() error!", GetLastError()); } if(ioData->m_Mode == MODE_RECV) { if(bytesTrans == 0) { ClientManager::GetInstance()->RemoveClient(clientSession); } else { clientSession->PacketHandling(bytesTrans); clientSession->RecvFromClient(); } } SafeDelete<IOData*>(ioData); } return 0; }
bool VNetwork::Accept() { SOCKET acceptedSock = accept(listenSocket_, NULL, NULL); if (acceptedSock == INVALID_SOCKET) { Logger<VNetwork>::Fatal("Accept : Invalid socket."); return false; } ClientSession* client = sessionManager_.GetSession(sessionID_); client->Initialize(acceptedSock); client->requestQueue_ = RIOBase::GetInstance()->GetRequestQueue(acceptedSock); RioIoContext* mainContext = new RioIoContext(client, IO_RECV, true); DWORD flags = 0; if (!RIOBase::GetInstance()->RIOReceive(client->requestQueue_, client->GetRecieveBuffer(), 1, flags, mainContext)) { //TODO : Logger 포멧 적용하기 GetLastError() Logger<VNetwork>::Fatal("RIOReceive error."); delete mainContext; return false; } sessionID_++; return true; }
bool SessionManager::AcceptSessions() { FastSpinlockGuard guard(mLock); while (mCurrentIssueCount - mCurrentReturnCount < MAX_CONNECTION) { //TODO mFreeSessionList에서 ClientSession* 꺼내서 PostAccept() 해주기.. (위의 ReturnClientSession와 뭔가 반대로 하면 될 듯?) // AddRef()도 당연히 해줘야 하고... // 실패시 false //if (false == newClient->PostAccept()) // return false; //////////////////////////////////////////////////////////// ClientSession * newClient = mFreeSessionList.back(); mFreeSessionList.pop_back(); ++mCurrentIssueCount; newClient->AddRef(); if (newClient->PostAccept() == false) { return false; } //////////////////////////////////////////////////////////// } return true; }
void ClientManager::CollectGarbageSessions() { std::vector<ClientSession*> disconnectedSessions ; ///FYI: C++ 11 람다를 이용한 스타일 std::for_each(mClientList.begin(), mClientList.end(), [&](ClientList::const_reference it) { ClientSession* client = it.second ; if ( false == client->IsConnected() && 0 == client->GetRefCount() ) disconnectedSessions.push_back(client) ; } ) ; ///FYI: C언어 스타일의 루프 for (size_t i=0 ; i<disconnectedSessions.size() ; ++i) { ClientSession* client = disconnectedSessions[i] ; mClientList.erase(client->mSocket) ; delete client ; } }
void ClientListener::OnSessionClose( Session * session ) { Logger::Log( "client % disconnected" , session->ip_address() ); ClientSession * client = ( ClientSession* ) session; client->CloseFile(); ClientPool::Instance()->Pop( ( ClientSession* ) session ); SAFE_DELETE( session ); }
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; ClientSession* asCompletionKey = nullptr; int ret = 0; ///<여기에는 GetQueuedCompletionStatus(hComletionPort, ..., GQCS_TIMEOUT)를 수행한 결과값을 대입 /// check time out first if (ret == 0 && GetLastError()==WAIT_TIMEOUT) continue; if (ret == 0 || dwTransferred == 0) { /// connection closing asCompletionKey->Disconnect(DR_RECV_ZERO); GSessionManager->DeleteClientSession(asCompletionKey); continue; } // if (nullptr == context) 인 경우 처리 //{ //} bool completionOk = true; switch (context->mIoType) { case IO_SEND: completionOk = SendCompletion(asCompletionKey, context, dwTransferred); break; case IO_RECV: completionOk = ReceiveCompletion(asCompletionKey, context, dwTransferred); break; default: printf_s("Unknown I/O Type: %d\n", context->mIoType); break; } if ( !completionOk ) { /// connection closing asCompletionKey->Disconnect(DR_COMPLETION_ERROR); GSessionManager->DeleteClientSession(asCompletionKey); } } return 0; }
unsigned int WINAPI IOCP_Manager::IoWorkerThread(LPVOID lpParam) { LThreadType = THREAD_TYPE::THREAD_IO_WORKER; LIoThreadID = reinterpret_cast<int>(lpParam); HANDLE hCompletionPort = GIOCP_Manager->GetCompletionPort(); while (true) { DWORD dwTransferred = 0; OverlappedIOContext* context = nullptr; ClientSession* asCompletionKey = nullptr; int ret = GetQueuedCompletionStatus(hCompletionPort, &dwTransferred, (ULONG_PTR*)&asCompletionKey, (LPOVERLAPPED*)&context, GQCS_TIMEOUT); if (ret == 0 && GetLastError() == WAIT_TIMEOUT) { continue; } if (ret == 0 || dwTransferred == 0) { asCompletionKey->Disconnect(DisconnectReason::DR_RECV_ZERO); GSessionManager->DeleteClientSession(asCompletionKey); continue; } if (context == nullptr) { printf_s("not dequeue completion packet\n"); continue; } bool completionOk = true; switch (context->mIoType) { case IOType::IO_SEND: completionOk = SendCompletion(asCompletionKey, context, dwTransferred); break; case IOType::IO_RECV: completionOk = ReceiveCompletion(asCompletionKey, context, dwTransferred); break; default: printf_s("Unkonw I/O type: %d\n", context->mIoType); break; } if (!completionOk) { asCompletionKey->Disconnect(DisconnectReason::DR_COMPLETION_ERROR); GSessionManager->DeleteClientSession(asCompletionKey); } } return 0; }
void ClientManager::FlushClientSend() { for (auto& it : mClientList) { ClientSession* client = it.second; if (false == client->SendFlush()) { client->Disconnect(); } } }
void GameManager::DoPeriodWork() { // 게임 로직이 주기적으로 실행되는 곳이므로 게임 내 상태 변화는 여기서 일어나고 // 상태가 바뀐 부분(충돌이나 죽음, 게임 종료)에 한정해서 방송합니다 // 주기적 polling이 많은 패킷을 유발하는 것은 방송할 필요가 없는 상태까지 똑같이 방송할 때 생기는 것 아닌가요?? // 충돌 이벤트를 여기서 방송하는 이유는 충돌 판정이 되었을 때 바로 방송할 경우 // 특정 캐릭터가 여러 오브젝트와 동시에 충돌할 경우, 각각의 충돌 판정 수만큼 패킷을 보내므로 중복이라고 생각해서 // 충돌 판정 된 캐릭터들 리스트를 생성하고 일괄적으로 한번씩만 보내기 위함이거든요. // 게임 로직 진행 Update(); // 충돌 결과 방송 std::for_each( m_CollidedPlayers.begin(), m_CollidedPlayers.end(), []( const int& each ) { // 방송 요청 ClientSession* targetSession = GClientManager->GetSession( each ); if ( targetSession ) targetSession->BroadcastCollisionResult(); else DDLOG_WARN( L"invalid index" ); // printf_s( "collision : %d \n", each ); } ); m_CollidedPlayers.clear(); // 죽음(산소 == 0) 방송 std::for_each( m_DeadPlayers.begin(), m_DeadPlayers.end(), []( const int& each ) { // 방송 요청 ClientSession* targetSession = GClientManager->GetSession( each ); if ( targetSession ) targetSession->BroadcastDeadResult(); else DDLOG_WARN( L"invalid index" ); } ); m_DeadPlayers.clear(); // 게임 종료 조건 확인 if ( m_WinnerTeam != TeamColor::NO_TEAM && !m_GameEndFlag ) { DDLOG_INFO( L"game end" ); m_GameEndFlag = true; // 방송 요청 GameResultResult outPacket; outPacket.mWinnerTeam = static_cast<int>( m_WinnerTeam ); GClientManager->BroadcastPacket( nullptr, &outPacket ); } }
void ForSClientMsgHandler::ServerToClient(BaseSession* pSessioin,const NetMsgHead* pHead,int32 nSize) { ClientSession* pClientSession = ClientSessionMgr::Instance()->GetSession(pHead->nClientSessionID); if(pClientSession == NULL) { return; } pClientSession->SendMsg(const_cast<NetMsgHead*>(pHead),nSize); }
ClientSession* SessionManager::CreateClientSession(SOCKET sock) { ClientSession* client = new ClientSession(sock); client->AddRef(); mLock.EnterLock(); { mClientList.insert(ClientList::value_type(sock, client)); } mLock.LeaveLock(); return client; }
void ClientManager::BroadcastPacket(ClientSession* from, PacketHeader* pkt) { ///FYI: C++ STL iterator 스타일의 루프 for (ClientList::const_iterator it=mClientList.begin() ; it!=mClientList.end() ; ++it) { ClientSession* client = it->second ; if ( from == client ) continue ; client->SendRequest(pkt) ; } }
unsigned int WINAPI ClientHandlingThread( LPVOID lpParam ) { LThreadType = THREAD_CLIENT ; HANDLE hEvent = (HANDLE)lpParam ; /// Timer HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL) ; if (hTimer == NULL) return -1 ; LARGE_INTEGER liDueTime ; liDueTime.QuadPart = -10000000 ; ///< 1초 후부터 동작 if ( !SetWaitableTimer(hTimer, &liDueTime, 1, TimerProc, NULL, TRUE) ) return -1 ; while ( true ) { /// accept or IO/Timer completion 대기 DWORD result = WaitForSingleObjectEx(hEvent, INFINITE, TRUE) ; /// client connected if ( result == WAIT_OBJECT_0 ) { /// 소켓 정보 구조체 할당과 초기화 ClientSession* client = g_client_manager->CreateClient(g_AcceptedSocket) ; SOCKADDR_IN clientaddr ; int addrlen = sizeof(clientaddr) ; getpeername(g_AcceptedSocket, (SOCKADDR*)&clientaddr, &addrlen) ; // 클라 접속 처리 if ( false == client->OnConnect(&clientaddr) ) { client->Disconnect() ; } continue ; ///< 다시 대기로 } // APC에 있던 completion이 아니라면 에러다 if ( result != WAIT_IO_COMPLETION ) return -1 ; } CloseHandle( hTimer ) ; return 0; }
unsigned int WINAPI ClientHandlingThread( LPVOID lpParam ) { LThreadType = THREAD_CLIENT; PendingAcceptList* pAcceptList = (PendingAcceptList*)lpParam; /// Timer HANDLE hTimer = CreateWaitableTimer( NULL, FALSE, NULL ); if ( hTimer == NULL ) return -1; LARGE_INTEGER liDueTime; liDueTime.QuadPart = -10000000; ///< 1초 후부터 동작 if ( !SetWaitableTimer( hTimer, &liDueTime, 100, TimerProc, NULL, TRUE ) ) return -1; while ( true ) { SOCKET acceptSock = NULL; /// 새로 접속한 클라이언트 처리 if ( pAcceptList->Consume( acceptSock, false ) ) { /// 소켓 정보 구조체 할당과 초기화 ClientSession* client = GClientManager->CreateClient( acceptSock ); SOCKADDR_IN clientaddr; int addrlen = sizeof( clientaddr ); getpeername( acceptSock, (SOCKADDR*)&clientaddr, &addrlen ); // 클라 접속 처리 if ( false == client->OnConnect( &clientaddr ) ) { client->Disconnect(); } continue; ///< 다시 대기로 } /// 최종적으로 클라이언트들에 쌓인 send 요청 처리 GClientManager->FlushClientSend(); /// APC Queue에 쌓인 작업들 처리 SleepEx( INFINITE, TRUE ); } CloseHandle( hTimer ); return 0; }
void CALLBACK RecvCompletion(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags) { ClientSession* fromClient = static_cast<OverlappedIO*>(lpOverlapped)->mObject; fromClient->DecRefCount(); if (!fromClient->IsConnected()) return; /// 에러 발생시 해당 세션 종료 if (dwError || cbTransferred == 0) { fromClient->Disconnect(); return; } /// 받은 데이터 처리 fromClient->OnRead(cbTransferred); /// 다시 받기 if (false == fromClient->PostRecv()) { fromClient->Disconnect(); return; } }
/////////////////////////////////////////////////////////// // 비동기 입력 WSARecv()에 의해서, 입력이 완료 되면 콜백으로 RecvCompletion 실행 void CALLBACK RecvCompletion( DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags ) { // lpOverlapped 인자를 OverlappedIO 형태로 형변환 하면 // 해당 구조체의 멤버 변수 mObject => ClientSession* // 바로 이 포인터가 비동기 입력 WSARecv로 보낸 ClientSession 객체의 주소값 // PostRecv 멤소드에서 mOverlappedRecv.mObject = this ; 이 부분 참조 ClientSession* fromClient = static_cast<OverlappedIO*>( lpOverlapped )->m_Object; // Overlapped IO 완료 했음. 카운트 감소 fromClient->DecOverlappedRequest(); if ( !fromClient->IsConnected() ) return; /// 에러 발생시 해당 세션 종료 if ( dwError || cbTransferred == 0 ) { LogD( "[Disconnected from:]ClientSession::RecvCompletion dwError \n" ); fromClient->Disconnect(); return; } /// 받은 데이터 처리 fromClient->OnRead( cbTransferred ); /// 다시 받기 if ( false == fromClient->PostRecv() ) { LogD( "[Disconnected from:]ClientSession::RecvCompletion PostRecv \n" ); fromClient->Disconnect(); return; } }
// // Database test driver class // DbTester::DbTester(ClientSession &ss, const char *path, const AccessCredentials *cred, int timeout, bool sleepLock) : session(ss), dbId(ssuid, path, NULL) { params.idleTimeout = timeout; params.lockOnSleep = sleepLock; dbRef = ss.createDb(dbId, cred, NULL, params); detail("Database %s created", path); }
void CALLBACK SendCompletion( DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags ) { ClientSession* fromClient = static_cast<OverlappedIO*>(lpOverlapped)->mObject; fromClient->DecOverlappedRequest(); if ( !fromClient->IsConnected() ) return; /// 에러 발생시 해당 세션 종료 if ( dwError || cbTransferred == 0 ) { fromClient->Disconnect(); return; } fromClient->OnWriteComplete(cbTransferred); }
ClientSession* SessionManager::IssueClientSession() { FastSpinlockGuard guard(mLock); uint64_t threadId = (mCurrentIssueCount++ % MAX_RIO_THREAD) + 1; CRASH_ASSERT(threadId > 0); CRASH_ASSERT((mCurrentIssueCount - mCurrentReturnCount) <= (MAX_RIO_THREAD*MAX_CLIENT_PER_RIO_THREAD)); ClientSession* newClient = mFreeSessionList[threadId].back(); mFreeSessionList[threadId].pop_back(); newClient->AddRef(); mOccupiedSessionList.push_back(newClient); return newClient; }
bool SessionManager::PrepareSessionPool() { CRASH_ASSERT(LIoThreadId == MAIN_THREAD_ID); for (int i = 1; i <= MAX_RIO_THREAD; ++i) { for (int j = 0; j < MAX_CLIENT_PER_RIO_THREAD; ++j) { ClientSession* client = new ClientSession(i); if (false == client->RioInitialize()) return false; mFreeSessionList[i].push_back(client); } } return true; }
void GameManager::BroadcastCharacterChange( int targetId, ChangeType type ) { ClientSession* targetSession = GClientManager->GetSession( targetId ); assert( targetSession ); switch ( type ) { case ChangeType::KINETIC_STATE: targetSession->BroadcastKineticState( true, true ); break; case ChangeType::CHARACTER_STATE: targetSession->BroadcastCharacterState( ); break; case ChangeType::RESOURCE_GATHER: targetSession->BroadcastGatherResult( ); break; default: break; } }
bool ClientSessionManager::AcceptClientSessions() { FastSpinlockGuard guard(mLock); while (mCurrentIssueCount - mCurrentReturnCount < MAX_CONNECTION) { ClientSession* newClient = mFreeSessionList.back(); mFreeSessionList.pop_back(); ++mCurrentIssueCount; newClient->AddRef(); ///< refcount +1 for issuing if (false == newClient->PostAccept()) return false; } return true; }
CErrno NetThread::FetchClientsQueue() { SCreateInfo objInfo; while (m_queCreateClients.try_pop(objInfo)) { INetHandlerPtr pNetHandler = CreateClientHandler(objInfo.strNodeName, objInfo.strUUID, objInfo.strAddress.c_str(), objInfo.usPort); if (!pNetHandler) { continue; } ClientSession * pSession = dynamic_cast<ClientSession*>(pNetHandler->GetSession()); if (pSession) { pSession->SetReconnect(objInfo.bReconnect); } } return CErrno::Success(); }
void ProcServerHandler::DbRoleCreate(void* pSession,const void* pData) { #pragma pack(push,1) struct StCreateResult { int64 nNewCharID; int32 nAccountID; }; #pragma pack(pop) ClientSession* pClientSession = static_cast<ClientSession*>(pSession); const DbRecordSet* pRecordSet = static_cast<const DbRecordSet*>(pData); const StCreateResult* pCreateResult = static_cast<const StCreateResult*>(pRecordSet->GetRecordData(0)); int64 nNewID = pCreateResult->nNewCharID; int32 nAccountID = pCreateResult->nAccountID; D2LRoleCreateResult sMsg; if(nNewID == 0) { sMsg.nResult = D2LRoleCreateResult::E_FAIL_SYNC; }else if(nNewID == 1) { sMsg.nResult = D2LRoleCreateResult::E_FAIL_ROLE_MAX; } else if (nNewID == 2) { sMsg.nResult = D2LRoleCreateResult::E_FAIL_NAME_EXIST; } else if (nNewID == 3) { sMsg.nResult = D2LRoleCreateResult::E_FAIL_INSERT_FAIL; }else { sMsg.nNewCharID = nNewID; sMsg.nResult = D2LRoleCreateResult::E_SUCCESS; QueryCharacterList(pClientSession,nAccountID,0); } pClientSession->SendMsgToLs(&sMsg,sMsg.GetPackLength()); }