// Include #include "gamesrv.h" #include "packet.h" // Local definitions #pragma warning( disable: 4127 ) // Global data // cRecver Constructor cRecver::cRecver(void) : mPacketPool(NULL) {} // ~cRecver Destructor cRecver::~cRecver(void) {} // Initialize Method bool cRecver::Initialize(char* ipAddr, unsigned short port, unsigned short numWorkerThreads, unsigned int bufferLength) { mPacketPool = new PacketPool( ); return IocpUdpRecv::Initialize( ipAddr, port, numWorkerThreads, bufferLength ); } // Shutdown Method void cRecver::Shutdown(DWORD maxWait) { Sleep( 50 ); IocpUdpRecv::Shutdown( maxWait ); mPacketPool->Shutdown( ); delete mPacketPool; mPacketPool = NULL; } // PostServerEvent Method bool cRecver::PostServerEvent(LPCTSTR format, ...) { cSender* sender = g_gameSrv->GetSender( ); bool retvalue = false; if ( sender != NULL ) { LPVOID msgBuf = NULL; DWORD bufferLength; va_list args; va_start( args, format ); bufferLength = _vscprintf( format, args ) + 1; msgBuf = malloc( bufferLength ); vsprintf( (char*)msgBuf, format, args ); va_end( args ); if ( msgBuf != NULL ) { retvalue = sender->PostServerEvent( (char*)msgBuf ); free( msgBuf ); } } return retvalue; } // BatchComplete Method bool cRecver::BatchComplete(MSGBUF* msgBuf, int msgLen) { cGameProcess* gameProcess = g_gameSrv->GetGameProcess( ); if ( gameProcess == NULL ) return false; else if ( gameProcess->IsChannelCheck( ) == false ) return false; switch ( msgBuf->protocol ) { case MB_CHAT_MEGAPHONE_SYN: { MB_SYN_CHAT_MEGAPHONE* recvMsg = (MB_SYN_CHAT_MEGAPHONE*)msgBuf; int len = msgLen - (sizeof(MB_SYN_CHAT_MEGAPHONE) - sizeof(recvMsg->msg) ); gameProcess->ChatMegaphone( recvMsg->cid, recvMsg->msg, len ); } break; case MB_CHAT_SYN: { MB_SYN_CHAT_SYNC* recvMsg = (MB_SYN_CHAT_SYNC*)msgBuf; int len = msgLen - (sizeof(MB_SYN_CHAT_SYNC) - sizeof(recvMsg->msg) ); gameProcess->ChatSync( recvMsg->cid, recvMsg->msg, len ); } break; case MB_SERVER_NOTICE_SYN: { MB_SYN_SERVER_NOTICE* recvMsg = (MB_SYN_SERVER_NOTICE*)msgBuf; gameProcess->ServerNotice( recvMsg->cid, recvMsg->message ); } break; case MB_SERVER_DOWN_COUNT_NOTICE_SYN: { MB_SYN_SERVER_DOWN_COUNT_NOTICE* recvMsg = (MB_SYN_SERVER_DOWN_COUNT_NOTICE*)msgBuf; gameProcess->ServerDownCountNotice( recvMsg->cid, recvMsg->count ); } break; case MB_FRIEND_SYN: { MB_SYN_FRIEND_SYN* recvMsg = (MB_SYN_FRIEND_SYN*)msgBuf; int len = msgLen - (sizeof(MB_SYN_FRIEND_SYN) - sizeof(recvMsg->msg)); gameProcess->FriendSync( recvMsg->cid, recvMsg->msg, len ); } break; case MB_GUILD_SYNC_SYN: { MB_SYN_GUILD_SYNC* recvMsg = (MB_SYN_GUILD_SYNC*)msgBuf; int len = msgLen - (sizeof(MB_SYN_GUILD_SYNC) - sizeof(recvMsg->msg)); gameProcess->GuildSync( recvMsg->cid, recvMsg->msg, len ); } break; case MB_PVP_STATUS_SYN: { MB_SYN_PVP_STATUS* recvMsg = (MB_SYN_PVP_STATUS*)msgBuf; gameProcess->PVPStatus( recvMsg->cid, recvMsg->status, recvMsg->pvpType, recvMsg->aryPos ); } break; case MB_PVP_NOTICE_SYN: { MB_SYN_PVP_NOTICE* recvMsg = (MB_SYN_PVP_NOTICE*)msgBuf; gameProcess->PVPNotice( recvMsg->cid, recvMsg->leftMinute, recvMsg->pvpType, recvMsg->aryPos ); } break; case MB_PVP_PLAYERLIST_SYN: { MB_SYN_PVP_PLAYERLIST* recvMsg = (MB_SYN_PVP_PLAYERLIST*)msgBuf; gameProcess->PVPPlayerList( recvMsg ); } break; case MB_PVP_GMCHANNELMOVE_SYN: { MB_SYN_PVPGM_CHANNELMOVE* recvMsg = (MB_SYN_PVPGM_CHANNELMOVE*)msgBuf; gameProcess->PvPGMChannelRes( recvMsg ); } break; case MB_THEME_STATUS_SYN: { MB_SYN_THEME_STATUS* recvMsg = (MB_SYN_THEME_STATUS*)msgBuf; gameProcess->ThemeStatus( recvMsg->cid, recvMsg->status, recvMsg->mapType ); } break; case MB_CH_STATUS_SYN: { MB_SYN_CH_STATUS* recvMsg = (MB_SYN_CH_STATUS*)msgBuf; gameProcess->ChannelStatus( recvMsg->cid, recvMsg->status ); } break; case MB_CH_LIST_SYN: { MB_SYN_CH_LIST* recvMsg = (MB_SYN_CH_LIST*)msgBuf; for ( BYTE i = 0; i < recvMsg->rowCount; i++ ) gameProcess->ChannelStatus( recvMsg->channels[ i ].cid, recvMsg->channels[ i ].status ); } break; case MB_MONSTER_SYN_SYN: { MB_SYN_MONSTER_SYNC* pRecMsg = (MB_SYN_MONSTER_SYNC*)msgBuf; int len = msgLen - (sizeof(MB_SYN_MONSTER_SYNC) - sizeof(pRecMsg->msg)); gameProcess->MonsterSync( pRecMsg->cid, pRecMsg->msg, len ); } break; case MB_CH_SYNC_SYN: gameProcess->ChannelSync( ); break; case MB_POST_RECEIVED_SYN: gameProcess->PostReceived( (MB_SYN_POST_RECEIVED*)msgBuf ); break; case MB_PARTY_SYN: gameProcess->PartySync( (MB_SYN_PARTY*)msgBuf ); break; case MB_ENHANCED_RESULT_SYN: gameProcess->EnhancedSync( (MB_SYN_ENHANCED_RESULT*)msgBuf ); break; case MB_NO_CHAT_SYN: gameProcess->NoChatSync( (MB_SYN_NO_CHAT*)msgBuf ); break; case MB_UNIQUEITEM_GET_SYN: gameProcess->UniqueItemSync( (MB_SYN_UNIQUEITEM_GET*)msgBuf ); break; case MB_ADD_GM_EVENT_SYN: gameProcess->AddGMEventSync( (MB_SYN_ADD_GM_EVENT*)msgBuf ); break; case MB_DEL_GM_EVENT_SYN: gameProcess->DelGMEventSync( (MB_SYN_DEL_GM_EVENT*)msgBuf ); break; case MB_GM_KICK_EVENT_SYN: gameProcess->GMKickEventSync( (MB_SYN_GM_KICK_EVENT*)msgBuf ); break; case MB_ITEM_DROPLIMIT_UPDATE: gameProcess->ItemDropLimitUpdate( (MB_SYN_DROPITEM_UPDATE*)msgBuf ); break; default: return false; } return true; } // RecvComplete Method // // UDP:.......User Datagram Protocol. // Datagram:..ÆÐŶ ±³È¯¿¡¼­, µ¥ÀÌÅÍ ´Ü¸» ÀåÄ¡¿Í ¸Á°úÀÇ »çÀü Á¢¼Ó ÀýÂ÷¿¡ ÀÇÇÏÁö ¾Ê°í, // ÇϳªÇϳªÀÇ ÆÐŶÀÌ ¹ß½Å µ¥ÀÌÅÍ ´Ü¸» ÀåÄ¡¿Í ¼ö½Åó µ¥ÀÌÅÍ ´Ü¸» ÀåÄ¡ °£ÀÇ // °æ·Î ÁöÁ¤À» À§ÇÑ ÃæºÐÇÑ Á¤º¸¸¦ °¡Áö°í ÀÖ´Â ÆÐŶ. bool cRecver::RecvComplete(ULONG_PTR completionKey, PerIoContext* perIoContext, DWORD bytesTransferred) { // ¼ö½ÅµÈ µ¥ÀÌÅÍ Ã³¸®. perIoContext->offset = bytesTransferred; // Double Buffering ó¸®. CSBlock( &m_cs ) { PerIoContext** buffer = m_ioContextBackBuffer->buffer; long& offset = m_ioContextBackBuffer->offset; if ( offset < MAX_IO_CONTEXT_BUFFER_LEN ) { buffer[ offset ] = perIoContext; offset++; } else { m_ioContextPool->ReleaseIoContext( perIoContext ); PostServerEvent( "Warning - cRecver::RecvComplete Method - Overflow I/O Context Buffer." ); } } // ¼ö½ÅÀ» À§ÇØ I/O Context¸¦ ÁغñÇÑ´Ù. perIoContext = m_ioContextPool->GetIoContext( m_socket, IOCP_REQUEST_READ ); if ( RecvPost( completionKey, perIoContext ) == false ) { PostServerEvent( "Error - cRecver::RecvPost Method - Return value is false." ); return false; } return true; } // BackendThread Method DWORD cRecver::BackendThread( ) { DWORD currentTick; DWORD elapsedTick; try { while ( true ) { currentTick = GetTickCount( ); // ¼­¹öÁ¾·á, Thread¸¦ ³ª°£´Ù. if ( m_endServer == true ) break; // Double Buffering ó¸®. CSBlock( &m_cs ) { IoContextBuffer* temp = m_ioContextBackBuffer; m_ioContextBackBuffer = m_ioContextFrontBuffer; m_ioContextFrontBuffer = temp; } // ¼ö½ÅµÈ µ¥ÀÌÅÍ Ã³¸®. PerIoContext** buffer = m_ioContextFrontBuffer->buffer; long& offset = m_ioContextFrontBuffer->offset; if ( offset > 0 ) { do { try { PerIoContext* ioContext = (*buffer); RUDP* rudp = (RUDP*)ioContext->buffer; if ( g_udpFrom == true ) { SOCKADDR_IN& addr = ioContext->addr; printf( "#Packets: Addr(%d.%d.%d.%d):Port(%d)\n" ,addr.sin_addr.S_un.S_un_b.s_b1 ,addr.sin_addr.S_un.S_un_b.s_b2 ,addr.sin_addr.S_un.S_un_b.s_b3 ,addr.sin_addr.S_un.S_un_b.s_b4 ,addr.sin_port ); } if ( g_udpPacket == true ) { // ÆÐÅ¶ÇØ´õ(Packet Header) Á¤º¸ Ãâ·Â - ¹öÀü/Çì´õ±æÀÌ/¼­ºñ½º Á¾·ù/Àüü±æÀÌ. printf( "#Packets: VER:%02x/HLEN:%02x/TOS:%02x/TLEN:%04x/SEQ:%08x\n" ,rudp->ver ,rudp->hlen ,rudp->tos ,rudp->tlen ,rudp->seq ); } if ( rudp->tos == TOS_RUDP ) { cSender* sender = g_gameSrv->GetSender( ); bool retvalue = sender->ReSendPost( rudp->addr, rudp->port, rudp->seq ); if ( g_udp == true ) printf( "#Packets: ReSendPost->Seq(%08x) = %d\n", rudp->seq, retvalue ); m_ioContextPool->ReleaseIoContext( ioContext ); } else if ( rudp->ver == RUDP_PHVer && rudp->hlen == RUDP_PHLen ) { u_long pid = (ioContext->addr.sin_addr.s_imp << 0x10) | ioContext->addr.sin_port; PacketBuffer* packetBuffer = mPacketPool->SearchPacketBuffer( pid ); if ( packetBuffer == NULL ) { packetBuffer = mPacketPool->GetPacketBuffer( pid ); } if ( packetBuffer != NULL ) { // Packet È帧 °Ë»ç¸¦ À§ÇÑ ¹öÆÛ¸µ. if ( packetBuffer->seq < rudp->seq ) { if ( g_udp == true ) printf( "PacketBuffer->Seq(%08x) < Packet->Seq(%08x)\n", packetBuffer->seq, rudp->seq ); if ( packetBuffer->seq != 0 ) { u_long lost; packetBuffer->seq = rudp->seq; lost = packetBuffer->seq - packetBuffer->ack; if ( lost >= MAX_PACKET_BUFFER_LEN ) { while ( packetBuffer->ack < packetBuffer->seq ) { u_long mask = (packetBuffer->ack & SM_ClassD); PerIoContext* temp = packetBuffer->buffer[ mask ]; if ( temp != NULL ) { RUDP* rudp = (RUDP*)temp->buffer; if ( rudp->seq == packetBuffer->ack ) break; } if ( g_udp == true ) printf( "\tPackets: Loss = %08x\n", packetBuffer->ack ); PostServerEvent( "Packets: Loss = %08x", packetBuffer->ack ); packetBuffer->ack++; } } lost = packetBuffer->seq - packetBuffer->ack; if ( lost > 0 ) { u_long mask = (packetBuffer->ack & SM_ClassD); PerIoContext* temp = packetBuffer->buffer[ mask ]; bool reliable = true; if ( temp != NULL ) { RUDP* rudp = (RUDP*)temp->buffer; reliable = (packetBuffer->ack != rudp->seq); } if ( reliable ) { cSender* sender = g_gameSrv->GetSender( ); sender->SendReliable( rudp->addr, rudp->port, packetBuffer->ack ); if ( g_udp == true ) printf( "\tPackets: Lost = %08x\n", packetBuffer->ack ); } } } else { packetBuffer->seq = rudp->seq; packetBuffer->ack = rudp->seq; } } else if ( packetBuffer->seq > rudp->seq ) { if ( packetBuffer->ack <= rudp->seq ) { if ( g_udp == true ) printf( "\tPackets: Recovery = %08x\n", rudp->seq ); } } // Packet È帧 Á¤¸®¸¦ À§ÇÑ ¹öÆÛ¸µ. if ( packetBuffer->ack <= rudp->seq ) { u_long mask = (rudp->seq & SM_ClassD); PerIoContext** temp = &packetBuffer->buffer[ mask ]; if ( (*temp) != NULL ) { m_ioContextPool->ReleaseIoContext( (*temp) ); } (*temp) = ioContext; packetBuffer->offset = max( packetBuffer->offset, mask ); packetBuffer->update = currentTick + 180000;// ÃÖ´ë3ºÐ´ë±â. } else { m_ioContextPool->ReleaseIoContext( ioContext ); } // Packet È帧 󸮸¦ À§ÇÑ ¹öÆÛ¸µ. while ( packetBuffer->ack <= packetBuffer->seq ) { u_long mask = (packetBuffer->ack & SM_ClassD); PerIoContext** temp = &packetBuffer->buffer[ mask ]; if ( (*temp) == NULL ) break; RUDP* rudp = (RUDP*)(*temp)->buffer; if ( rudp->seq != packetBuffer->ack ) break; // ¼­ºñ½º ŸÀÔÀÌ [TOS_LOGIN] ¶Ç´Â [TOS_GAME] ¾Æ´Ò°æ¿ì ¿À·ù ó¸®. if ( rudp->tos == TOS_LOGIN || rudp->tos == TOS_GAME ) { MSGBUF* msgBuf = (MSGBUF*)((char*)rudp + rudp->hlen); int msgLen = rudp->tlen - rudp->hlen; BatchComplete( msgBuf, msgLen ); } m_ioContextPool->ReleaseIoContext( (*temp) ); (*temp) = NULL; packetBuffer->ack++; } } else { m_ioContextPool->ReleaseIoContext( ioContext ); } } else { m_ioContextPool->ReleaseIoContext( ioContext ); } } catch ( char* str ) { PostServerEvent( "Caught 'BatchComplete' exception type: '%s'. Throwing 'cRecver::BackendThread' exception", str ); Sleep( 100 ); throw; } catch ( ... ) { PostServerEvent( "In BatchComplete Method. Throwing 'cRecver::BackendThread' exception." ); Sleep( 100 ); throw; } buffer++; offset--; } while ( offset > 0 ); } // PacketPool µ¥ÀÌÅÍ Ã³¸®. if ( mPacketPool != NULL ) { PacketBuffer* packetBuffer = mPacketPool->GetPagedPoolUsage( ); PacketBuffer* next = NULL; while ( packetBuffer ) { next = packetBuffer->next; if ( packetBuffer->update < currentTick ) { for ( u_long i = 0; i <= packetBuffer->offset; i++ ) { PerIoContext* ioContext = packetBuffer->buffer[ i ]; if ( ioContext != NULL ) { m_ioContextPool->ReleaseIoContext( ioContext ); } } mPacketPool->ReleasePacketBuffer( packetBuffer ); } packetBuffer = next; } } // Áö¿¬½Ã°£ ¾à16(ms) - CPU °úºÎÈ­ ¹æÁö & ³»ºÎ µ¿±âÈ­. elapsedTick = GetTickCount( ) - currentTick; if ( elapsedTick < 16 ) { Sleep( (16 - elapsedTick) ); } } } catch ( char* str ) { PostServerEvent( "Caught 'while ( true ) { ... }' exception type: '%s'. Throwing 'cRecver::BackendThread' exception", str ); Sleep( 100 ); throw; } catch ( ... ) { PostServerEvent( "In while ( true ) { ... }. Throwing 'cRecver::BackendThread' exception." ); Sleep( 100 ); throw; } return 0; }