/*-- Include */ #include "StdAfx.h" #include "iocpclient.h" #include "criticalsectionlock.h" #include "StageManager.h" #include "UIMsgboxEvent.h" #include "GameResourceManager.h" #include "UIManager.h" //#include "Application.h" #include "packet.h" //#include "HShield.h" //#include "HackShield_Common.h" /*-- Local definitions */ #pragma warning( disable: 4127 ) #pragma warning( disable: 4702 ) #define SafeCloseHandle(P) if (P!=NULL) { CloseHandle(P); (P)=NULL; } // Çڵ鸦 ¾ÈÀüÇÏ°Ô ÇØÁ¦ #define SafeDelete(P) if (P!=NULL) { delete(P); (P)=NULL; } // " #define MAX_CONNECT_RETRY 3 // Àç½Ãµµ Á¦ÇÑ È½¼ö ÃÖ´ë #pragma pack(1) // 1Byte Á¤·Ä ¼³Á¤ struct Login2Game : Packet { unsigned long ipv4; unsigned short port; }; struct Game2Game : Packet { unsigned long ipv4; unsigned short port; }; #pragma pack( ) // 1Byte Á¤·Ä ÇØÁ¦ /*-- Global data */ cIOCPClient* cIOCPClient::mIOCPClient = NULL; #define MAX_RC5_KEY 16 RC5_32_KEY RC5_32_KEY_TABLE[ MAX_RC5_KEY ]; BYTE SY_RKEY_TABLE[16][4]= { { 0x0F,0xAD,0x37,0x6C }, //6 { 0x5C,0x55,0x6A,0xD8 }, //10 { 0x6C,0xA9,0xDB,0x64 }, //11 { 0x5A,0xBE,0xF2,0xF6 }, //12 { 0xB3,0xB7,0xEC,0x7B }, //8 { 0xE8,0x9B,0x3A,0x8A }, //5 { 0xD9,0x64,0x28,0x5C }, //7 { 0xA6,0x4D,0x8F,0xB9 }, //13 { 0x17,0xb3,0x67,0xaa }, //0 { 0x4B,0x1C,0x15,0x11 }, //4 { 0x9C,0x13,0xFE,0xF5 }, //9 { 0x53,0xf2,0x74,0xb1 }, //1 { 0x67,0x9F,0x7D,0xF6 }, //14 { 0x98,0xea,0x55,0x29 }, //3 { 0x8c,0x72,0x59,0x32 }, //2 { 0xCA,0x6A,0xA9,0xEF }, //15 }; BYTE SY_KEY_TABLE[16][4]= { { 0xC0,0x3C,0x06,0xEA }, //0 { 0x68,0xCC,0x56,0x3A }, //10 { 0xB9,0x78,0xB3,0x45 }, //1 { 0xA1,0x8A,0x0B,0x70 }, //12 { 0x3B,0xB1,0x60,0x0B }, //8 { 0x88,0x48,0xC0,0x71 }, //2 { 0x43,0xC8,0x53,0x63 }, //7 { 0x5C,0xF8,0x90,0x4A }, //11 { 0x54,0x1B,0x0F,0x9C }, //15 { 0x44,0x95,0xAF,0x99 }, //6 { 0x85,0x4C,0xB6,0xE7 }, //3 { 0x28,0x45,0x80,0xB1 }, //13 { 0x2D,0x83,0x06,0x55 }, //5 { 0x98,0xAE,0xD9,0xBF }, //4 { 0x71,0x9D,0x4A,0x80 }, //14 { 0x19,0x2D,0x57,0xC6 }, //9 }; // ErrorLog prototype void ErrorLog( char* format, ... ) { LPVOID msgBuf = NULL; DWORD bufferLength; va_list args; va_start( args, format ); bufferLength = _vscprintf( format, args ) + 1; msgBuf = malloc( bufferLength ); vsprintf_s( (char*)msgBuf, bufferLength, format, args ); va_end( args ); if ( msgBuf != NULL ) { FILE* stream = NULL; char filename[ MAX_PATH ]; SYSTEMTIME systemtime; char buffer[ 1024 ]; GetLocalTime( &systemtime ); sprintf_s( filename, "Network_%04d_%02d_%02d.log", systemtime.wYear, systemtime.wMonth, systemtime.wDay ); if ( fopen_s( &stream, filename, "at" ) != 0 ) { sprintf_s( buffer, "%04d %02d %02d::%02d %02d %02d : %s\n", systemtime.wYear, systemtime.wMonth, systemtime.wDay, systemtime.wHour, systemtime.wMinute, systemtime.wSecond, (char*)msgBuf ); fputs( buffer, stream ); fclose( stream ); } free( msgBuf ); } } /*-- cIOCPClient Constructor */ cIOCPClient::cIOCPClient(HWND wnd, DWORD messageID) : mWnd( wnd ), mMessageID( messageID ) { // Á¤Àûº¯¼ö ¼³Á¤. mIOCPClient = this; // ws2_32.dll Áغñ mVersionRequested = MAKEWORD(2,2); ::WSAStartup( mVersionRequested, &mWsaData ); // Critical Section¸¦ ÃʱâÈ­. InitializeCriticalSection( &mCs ); // ³»ºÎº¯¼ö ÃʱâÈ­. mIocp = NULL; mSocket = INVALID_SOCKET; mIocpWorkerThreadNumber = 0; mIoContextPool = NULL; mIoContextFrontBuffer = NULL; mIoContextBackBuffer = NULL; mConnectThread = NULL; mConnected = false; mConnectionResult = true; mConnectRetry = 0; mRC5Enable = true; mLastTos = 0; mLastCategory = 0; mLastProtocol = 0; mWepLuncherInit = false; /*-- ... */ RC5_32_KEY* rc532Key = RC5_32_KEY_TABLE; // ... unsigned char tiv1[ 4 ] = {'s','L','e','e'}; // unsigned char tiv2[ 4 ] = {'p','H','A','h'}; // DWORD skey[ 2 ]; // ... memset( RC5_32_KEY_TABLE, 0, sizeof(RC5_32_KEY_TABLE) ); for ( int i = 0; i < MAX_RC5_KEY; i++, rc532Key++ ) { skey[ 0 ] = (*(DWORD*)(SY_RKEY_TABLE + i)) ^ (*(DWORD*)tiv1); skey[ 1 ] = (*(DWORD*)(SY_RKEY_TABLE + i)) ^ (*(DWORD*)tiv2); RC5_32_set_key( rc532Key, sizeof(skey), (unsigned char*)&skey, RC5_8_ROUNDS ); } } /*-- ~cIOCPClient Destructor */ cIOCPClient::~cIOCPClient(void) { // Á¾·á Shutdown( ); // Critical Section¸¦ ÇØÁ¦. DeleteCriticalSection( &mCs ); // ws2_32.dll Á¾·á ::WSACleanup( ); // À©µµ¿ì ÇÚµé & ¸Þ½ÃÁö Ãʱâ. mWnd = NULL; mMessageID = 0; // Á¤Àûº¯¼ö °ª »èÁ¦. mIOCPClient = NULL; } /*-- Initialize Method */ bool cIOCPClient::Initialize(unsigned short numWorkerThreads) { /*-- CreateIoCompletionPort¸¦ ÀÌ¿ëÇÏ¿© completion port ¿ÀºêÁ§Æ®¸¦ »ý¼ºÇÒ ¶§ ÀϹÝÀûÀ¸·Î ½Å°æ½á¾ß ÇÒ ÆÄ ¶ó¸ÞÅÍ´Â NumberOfConcurrentThreads ÀÌ´Ù. óÀ½ 3°³ÀÇ ÆÄ¶ó¸ÞÅÍ´Â °ªÀ» ¼³Á¤ÇÏÁö ¾Ê´Â´Ù. NumberOfConcurrentThreads´Â ÇöÀç completion port¿¡ ´ëÇÏ¿© ¸î°³ÀÇ ½º·¹µå¸¦ ½ÇÇàÇÒ °ÍÀΰ¡¸¦ °áÁ¤ÇÑ ´Ù. °¡Àå ÀÌ»óÀûÀÎ °æ¿ì´Â ½º·¹µåÀÇ context switchingÀ» ¹æÁöÇϱâ À§ÇÏ¿© ¼­ºñ½º¿¡ ´ëÇÏ¿© ÇϳªÀÇ ÇÁ·Î ¼¼¼­´ç ÇϳªÀÇ ½º·¹µå¸¸ µ¿ÀÛÇϵµ·Ï ÇÏ´Â °ÍÀÌ´Ù. NumberOfConcurrentThreadsÀÇ °ªÀ» 0À¸·Î ¼³Á¤ÇÏ¸é ½Ã ½ºÅÛÀº ÇÁ·Î¼¼¼­¿¡ ÀûÇÕÇÑ ½º·¹µå °³¼ö¸¦ ÇÒ´çÇÏ¿© »ç¿ëÇÑ´Ù. (ÇϳªÀÇ CPU¿¡ ´ëÇÏ¿© IOCP Çϳª´ç ÇϳªÀÇ ½º·¹µå¸¦ »ý¼º) */ mIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 ); if ( mIocp == NULL ) return false; /*-- CreateIoCompletionPort ¿ÀºêÁ§Æ®¸¦ »ý¼ºÇÑ µÚ¿¡ ÀÛ¾÷½º·¹µå¸¦ »ý¼ºÇÏ°í ¼ÒÄÏÇÚµéÀ» ÁöÁ¤ÇÒ ¼ö Àִµ¥, À̶§ »ý¼ºÇÏ´Â ÀÛ¾÷½º·¹µå´Â, ½º·¹µå°¡ ºí·ÏµÉ °¡´É¼ºÀÌ ÀÖ´Ù¸é, NumberOfConcurrentThreads ÀÇ °¹¼öº¸ ´Ù ¸¹ÀÌ »ý¼ºÇÏ´Â °ÍÀÌ ÁÁ´Ù. NumberOfConcurrentThreads¸¸Å­ ½º·¹µå°¡ Ç×»ó ½ÇÇàµÇ°Ô ÇÒ ¼ö Àֱ⠶§¹® ¿¡, ºí·ÏµÈ ½º·¹µå°¡ ÀÖ´Ù¸é ´Ù¸¥ ½º·¹µå°¡ ´ë½Å µ¿ÀÛÇÒ °ÍÀ̱⠶§¹®ÀÌ´Ù. */ SYSTEM_INFO si; DWORD threadId; // ½Ã½ºÅÛ¿¡ ¸î °³ÀÇ ÇÁ·Î¼¼¼­°¡ ÀÖ´ÂÁö È®ÀÎÇÑ´Ù. GetSystemInfo( &si ); mIocpWorkerThreadNumber = min( si.dwNumberOfProcessors * numWorkerThreads, IOCP_MAX_WORKER_THREAD ); for ( int i = 0; i < mIocpWorkerThreadNumber; i++ ) { mIocpWorkerThread[i] = CreateThread( NULL, 0, WorkerThreadStartingPoint, (LPVOID)this, 0, &threadId ); if ( mIocpWorkerThread[i] == NULL ) return false; } /*-- I/O Context Pool¸¦ »ý¼ºÇÑ´Ù. */ mIoContextPool = new cIoContextPool( ); if ( mIoContextPool == NULL ) return false; // I/O Context Buffer¸¦ »ý¼ºÇÑ´Ù. mIoContextFrontBuffer = (IoContextBuffer*)GlobalAlloc( GPTR, sizeof(IoContextBuffer) ); mIoContextBackBuffer = (IoContextBuffer*)GlobalAlloc( GPTR, sizeof(IoContextBuffer) ); if ( !mIoContextFrontBuffer && !mIoContextBackBuffer ) return false; // ȯ°æº¯¼ö ¼³Á¤. mConnected = false; mConnectionResult = true; return true; } /*-- Shutdown Method */ void cIOCPClient::Shutdown(DWORD maxWait) { /*-- Connect Thread°¡ Á¾·áµÉ¶§±îÁö ´ë±â. */ WaitForSingleObjectEx( mConnectThread, maxWait, TRUE ); SafeCloseHandle( mConnectThread ); /*-- ¼­¹ö¿Í ¿¬°áÁ¾·á. */ SOCKET sockTemp = mSocket; /*-- ¼ÒÄÏÀ» ¾ÈÀüÇÏ°Ô Á¾·áÇÑ´Ù. */ mSocket = INVALID_SOCKET; mConnected = false; mConnectionResult = false; if ( sockTemp != INVALID_SOCKET ) { closesocket( sockTemp ); } /*-- Worket Thread Á¾·á¿äû. */ if ( mIocp != NULL ) { for ( int i = 0; i < mIocpWorkerThreadNumber; i++ ) { PostQueuedCompletionStatus( mIocp, 0, 0, IOCP_SHUTDOWN ); } } /*-- Worket Thread Á¾·áÈ®ÀÎ ¹× °­Á¦Á¾·á. */ for ( int i = 0; i < mIocpWorkerThreadNumber; i++ ) { if ( WaitForSingleObject( mIocpWorkerThread[ i ], 60000 ) != WAIT_OBJECT_0 ) { DWORD exitCode; GetExitCodeThread( mIocpWorkerThread[ i ], &exitCode); if ( exitCode == STILL_ACTIVE ) { TerminateThread( mIocpWorkerThread[ i ], 0 ); } } SafeCloseHandle( mIocpWorkerThread[ i ] ); } /*-- Overlapped I/O Model Pool¹× Socket Context Pool¸¦ ÇØÁ¦ÇÑ´Ù. */ if ( mIoContextFrontBuffer ) { GlobalFree( mIoContextFrontBuffer ); mIoContextFrontBuffer = NULL; } if ( mIoContextBackBuffer ) { GlobalFree( mIoContextBackBuffer ); mIoContextBackBuffer = NULL; } SafeDelete( mIoContextPool ); /*-- completion port¸¦ ÇØÁ¦ÇÑ´Ù. */ SafeCloseHandle( mIocp ); } /// PostConnect Method bool cIOCPClient::PostConnect(char* addr, unsigned short port) { cCSLock lock( &mCs ); if ( mConnectThread != NULL ) return false; // ÁÖ¼Òº¯È¯. ZeroMemory( (void*)&mAddr, sizeof(mAddr) ); mAddr.sin_family = AF_INET; mAddr.sin_port = htons( port ); mAddr.sin_addr.s_addr = inet_addr( addr ); if ( mAddr.sin_addr.s_addr == INADDR_NONE ) { PHOSTENT phe = gethostbyname( addr ); // the host name for the server is not in dot format, therefore try it just as a string if ( phe != NULL ) CopyMemory( &mAddr.sin_addr, phe->h_addr_list[0], phe->h_length ); else return false; } mSeq = 0; mConnectRetry = 0; return PostConnect( ); } bool cIOCPClient::PostConnect(unsigned long ipv4, unsigned short port) { cCSLock lock( &mCs ); if ( mConnectThread != NULL ) return false; // ÁÖ¼Òº¯È¯. ZeroMemory( (void*)&mAddr, sizeof(mAddr) ); mAddr.sin_family = AF_INET; mAddr.sin_port = htons( port ); mAddr.sin_addr.s_addr = ipv4; if ( mAddr.sin_addr.s_addr == INADDR_NONE ) return false; mSeq = 0; mConnectRetry = 0; return PostConnect( ); } bool cIOCPClient::PostConnect( ) { if ( mConnectRetry < MAX_CONNECT_RETRY ) { // ¼ÒÄϰ˻ç. if ( mSocket != INVALID_SOCKET ) { closesocket( mSocket ); mSocket = INVALID_SOCKET; } // connectÇÒ Å¬¶óÀÌ¾ðÆ® ¼ÒÄÏÀ» »ý¼ºÇÑ´Ù. mSocket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED ); if ( mSocket == INVALID_SOCKET ) return false; // ¿¬°á °á°ú°ª ÃʱâÈ­. mConnected = false; mConnectionResult = false; mConnectRetry++; DWORD threadId; mConnectThread = CreateThread( NULL, 0, ConnectThreadStartingPoint, (LPVOID)this, 0, &threadId ); return (mConnectThread != NULL) ? true : false; } return false; } /*-- GetConnectionResult Method; */ bool cIOCPClient::GetConnectionResult( ) { cCSLock lock( &mCs ); /*-- Connect Thread°¡ Á¾·áµÉ¶§±îÁö ´ë±â. */ WaitForSingleObjectEx( mConnectThread, INFINITE, TRUE ); SafeCloseHandle( mConnectThread ); if ( mConnected == true ) { /*-- The purpose of this algorithm is to reduce the number of very small segments sent, especially on high-delay (remote) links. Windows Sockets applications can disable the Nagle algorithm for their connections by setting the TCP_NODELAY socket option. However, this practice should be used wisely and only when needed because this practice increases network traffic. Usually, applications that need to disable the Nagle algorithm are applications that need to send small packets and require that the peer application receive them immediately. Some network applications may not perform well if their design does not take into account the effects of transmitting large numbers of small packets and the Nagle algorithm. Nagel ¾Ë°í¸®ÁòÀÇ ¸ñÀûÀº ƯÈ÷ °íÁö¿¬(¿ø°Ý) ¸µÅ©¿¡¼­ Å©±â°¡ ÀÛÀº ¼¼±×¸ÕÆ®°¡ Àü¼ÛµÇ´Â Ƚ¼ö¸¦ ÁÙÀÌ´Â °ÍÀÌ´Ù. Windows ¼ÒÄÏ ÀÀ¿ë ÇÁ·Î±×·¥¿¡¼­´Â ÀÚ½ÅÀÇ ¿¬°á¿¡ ´ëÇØ Nagle ¾Ë°í¸®ÁòÀ» »ç¿ëÇÒ ¼ö ¾øµµ·Ï TCP_NODELAY ¼ÒÄÏ ¿É¼ÇÀ» ¼³Á¤ÇÒ ¼ö ÀÖ½À´Ï´Ù. ±×·¯³ª ÀÌ·¯ÇÑ ÀÛ¾÷Àº ³×Æ®¿öÅ© ÀÌ¿ëÀ» ´Ã¸®±â ¶§¹®¿¡ ¹Ýµå½Ã ÇÊ¿äÇÑ °æ¿ì°¡ ¾Æ´Ï¸é °¡±ÞÀû »ç¿ëÇÏÁö ¾Ê¾Æ¾ß ÇÕ´Ï´Ù. ´Ù¼öÀÇ ¼ÒÇü ÆÐŶ Àü¼ÛÀ¸·Î ÀÎÇØ ¹ß»ýÇÏ´Â ¿µÇâ°ú Nagle ¾Ë°í¸®ÁòÀ» °í·ÁÇÏÁö ¾ÊÀº »óÅ·Π¼³°èµÈ ³×Æ®¿öÅ© ÀÀ¿ë ÇÁ·Î±×·¥Àº Á¦´ë·Î ¼öÇàµÇÁö ¾ÊÀ» ¼ö ÀÖ½À´Ï´Ù. Nagle ¾Ë°í¸®ÁòÀº ¼º´É»óÀÇ ÀÌÀ¯·Î ÀÎÇØ TCP ¿¬°áÀ» ·çÇÁ¹éÇÏ´Â µ¥´Â Àû¿ëµÇÁö ¾Ê½À´Ï´Ù. Windows 2000 Netbt´Â Á÷Á¢ È£½ºÆ®µÈ ¸®µð·ºÅÍ/¼­¹ö ¿¬°á»Ó¸¸ ¾Æ´Ï¶ó TCP¸¦ ÅëÇÑ NetBIOS ¿¬°á¿¡ ´ëÇØ¼­µµ ³×À̱۸µÀ» »ç¿ëÇÒ ¼ö ¾ø°Ô ¸¸µì´Ï´Ù. µû¶ó¼­ ´Ù¼öÀÇ ¼ÒÇü ÆÄÀÏ Á¶ÀÛ ¸í·ÉÀ» ¹ßÇàÇÏ´Â ÀÀ¿ë ÇÁ·Î±×·¥ÀÇ °æ¿ì ¼º´ÉÀÌ Çâ»óµÉ ¼ö ÀÖ½À´Ï´Ù. ±×·¯ÇÑ ÀÀ¿ë ÇÁ·Î±×·¥À¸·Î´Â ÆÄÀÏ Àá±Ý/Àá±Ý ÇØÁ¦ ±â´ÉÀ» ÀÚÁÖ »ç¿ëÇÏ´Â ÀÀ¿ë ÇÁ·Î ±×·¥À» µé ¼ö ÀÖ½À´Ï´Ù. */ int nodelay = 1; if ( setsockopt( mSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay) ) == SOCKET_ERROR ) return false; int zero; /*-- Disable receive buffering on the socket. Setting SO_RCVBUF to 0 causes winsock to stop bufferring receive and perform receives directly from our buffers, thereby reducing CPU usage. SO_RCVBUF¸¦ 0À¸·ÎÇÏ¿© ¼ö½Å¹öÆÛ¸¦ »ç¿ëÇÏÁö ¾Ê´Â´Ù. ¼ö½Å¹öÆÛ°¡ 0ÀÌ ¾Æ´Ï¸é ºÒÇÊ¿äÇÏ°Ô ÀÌ ¼ö½Å¹öÆÛ¸¦ °ÅÃļ­ overlapped I/O¸¦ È£ÃâÇßÀ» ¶§ Á¦°øÇÑ ¹öÆÛ·Î º¹»ç°¡ ÀÌ·ç¾îÁø´Ù. ¼ö½Å¹öÆÛ °ø°£ÀÌ ¾øÀ¸¸é overlapped I/O¸¦ È£ÃâÇßÀ» ¶§ Á¦°øÇÑ ¹öÆÛ·Î ½Ã½ºÅÛ¿¡¼­ Á÷Á¢ µ¥ÀÌÅͰ¡ º¹»çµÈ ´Ù. */ zero = 0; if ( setsockopt( mSocket, SOL_SOCKET, SO_RCVBUF, (char*)&zero, sizeof(zero)) == SOCKET_ERROR ) { closesocket( mSocket ); mSocket = INVALID_SOCKET; return (mConnectionResult = false); } /*-- Disable send buffering on the socket. Setting SO_SNDBUF to 0 causes winsock to stop bufferring sends and perform sends directly from our buffers, thereby reducing CPU usage. SO_SNDBUF¸¦ 0À¸·ÎÇÏ¿© ¼Û½Å¹öÆÛ¸¦ »ç¿ëÇÏÁö ¾Ê´Â´Ù. ¼Û½Å¹öÆÛ°¡ 0ÀÌ ¾Æ´Ï¸é ºÒÇÊ¿äÇÏ°Ô ÀÌ ¼Û½Å¹öÆÛ¸¦ °ÅÃļ­ overlapped I/O¸¦ È£ÃâÇßÀ» ¶§ Á¦°øÇÑ ¹öÆÛ·Î º¹»ç°¡ ÀÌ·ç¾îÁø´Ù. ¼Û½Å¹öÆÛ °ø°£ÀÌ ¾øÀ¸¸é overlapped I/O¸¦ È£ÃâÇÞÀ» ¶§ Á¦°øÇÑ ¹öÆÛ·Î ½Ã½ºÅÛ¿¡¼­ Á÷Á¢ µ¥ÀÌÅͰ¡ º¹»çµÈ ´Ù. */ zero = 0; if ( setsockopt( mSocket, SOL_SOCKET, SO_SNDBUF, (char*)&zero, sizeof(zero) ) == SOCKET_ERROR ) { closesocket( mSocket ); mSocket = INVALID_SOCKET; return (mConnectionResult = false); } LINGER linger; /*-- closesocket ÇÔ¼ö°¡ È£ÃâµÈ ÀÌÈÄ¿¡ º¸³»Áø µ¥ÀÌÅÍ¿¡ ´ëÇÑ Ã³¸®¸¦ Á¦¾îÇϱâ À§ÇÑ ¿É¼ÇÀÌ´Ù. linger::u_short l_onoff : option on/off linger::u_short l_linger : linger time SO_LINGER ¿É¼ÇÀÇ Å¸ÀӾƿôÀ» 0À¸·Î ¼³Á¤Çϸé (linger ±¸Á¶Ã¼ÀÇ l_onoff Çʵ带 0ÀÌ ¾Æ´Ñ°ª, l_linger ¸¦ 0À¸·Î ¼³Á¤), ¾ÆÁ÷ Àü¼ÛµÇÁö ¾ÊÀº µ¥ÀÌÅͰ¡ ÀÖ´õ¶óµµ closesocket ÇÔ¼ö È£ÃâÀº ºí·ÏµÇÁö ¾Ê°í Áï½Ã ³¡³­´Ù. ÀÌ·¸°Ô È£ÃâµÈ ¼ÒÄÏÀÇ °¡¼º¿¬°á (virtual circuit)Àº Áï½Ã ´ÜÀý(reset)µÇ°í Àü¼ÛµÇÁö ¾ÊÀº µ¥ ÀÌÅÍ´Â ¼Õ½ÇµÈ´Ù. µ¥ÀÌÅÍ ¼ö½ÅÇÏ´Â ÃøÀº ¼ö½Å ÇÔ¼ö°¡ WSAECONNRESET·Î ½ÇÆÐÇÑ´Ù. */ linger.l_onoff = 1; linger.l_linger = 0; if ( setsockopt( mSocket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger) ) == SOCKET_ERROR ) { closesocket( mSocket ); mSocket = INVALID_SOCKET; return (mConnectionResult = false); } /*-- connect¿¡ ÀÇÇÏ¿© ¸®ÅÏµÈ ¼ÒÄÏ ÇÚµéÀ» completion port¿¡ ÇÒ´çÇÑ´Ù. --*/ if ( CreateIoCompletionPort( (HANDLE)mSocket, mIocp, (ULONG_PTR)NULL, 0 ) == NULL ) { closesocket( mSocket ); mSocket = INVALID_SOCKET; return (mConnectionResult = false); } /*-- ¼ö½ÅÀ» À§ÇØ I/O Context¸¦ ÁغñÇÑ´Ù. --*/ PerIoContext* perIoContext = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_READ ); if ( perIoContext == NULL ) { closesocket( mSocket ); mSocket = INVALID_SOCKET; return (mConnectionResult = false); } if ( RecvPost( perIoContext ) == false ) { closesocket( mSocket ); mSocket = INVALID_SOCKET; return (mConnectionResult = false); } else return (mConnectionResult = true); } else { closesocket( mSocket ); mSocket = INVALID_SOCKET; return (mConnectionResult = false); } } /*-- Disconnect Method */ bool cIOCPClient::Disconnect(void) { if ( mConnected == true && mConnectionResult == true ) { cCSLock lock( &mCs ); SOCKET sockTemp = mSocket; /*-- ¼ÒÄÏÀ» ¾ÈÀüÇÏ°Ô Á¾·áÇÑ´Ù. */ mSocket = INVALID_SOCKET; mConnected = false; mConnectionResult = false; if ( sockTemp != INVALID_SOCKET ) { closesocket( sockTemp ); } PostMessage( mWnd, mMessageID, (WPARAM)ICT_DISCONNECT, (LPARAM)0L ); return true; } return false; } /*-- SendNetworkMsg Method; */ bool cIOCPClient::SendNetworkMsg(char* buf, u_short len) { if ( IsConnected( ) == true ) { cCSLock lock( &mCs ); try { PerIoContext* perIoConect = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_WRITE ); Packet* packet = (Packet*)perIoConect->buffer; char* buffer = NULL; packet->ver = PHVer; packet->hlen = PHLen; packet->tos = TOS_CLIENT; packet->tlen = packet->hlen + len; packet->seq = (++mSeq); buffer = perIoConect->buffer + packet->hlen; memcpy( buffer, buf, len ); perIoConect->offset = packet->tlen; return SendPost( perIoConect ); } catch ( ... ) { ErrorLog( "Throwing 'cIOCPClient::SendNetworkMsg' exception" ); throw; } } return false; } bool cIOCPClient::SendHackShieldMsg(char* buf, u_short len) { if ( IsConnected( ) == true ) { cCSLock lock( &mCs ); try { PerIoContext* perIoConect = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_WRITE ); Packet* packet = (Packet*)perIoConect->buffer; char* buffer = NULL; packet->ver = PHVer; packet->hlen = PHLen; packet->tos = TOS_HACKSHIELD; packet->tlen = packet->hlen + len; packet->seq = (++mSeq); buffer = perIoConect->buffer + packet->hlen; memcpy( buffer, buf, len ); perIoConect->offset = packet->tlen; return SendPost( perIoConect ); } catch ( ... ) { ErrorLog( "Throwing 'cIOCPClient::SendHackShieldMsg' exception" ); throw; } } return false; } /*-- ReleaseNetworkMsg Method; */ bool cIOCPClient::ReleaseNetworkMsg(PerIoContext* perIoContext) { DWORD error = 0; if ( perIoContext->socket == mSocket ) { mIoContextPool->ReleaseIoContext( perIoContext ); if ( error != 0 ) assert( "WARNING - cIOCPClient::ReleaseNetworkMsg." ); return true; } return false; } // SendHeartbeat Method bool cIOCPClient::SendHeartbeat( ) { if ( IsConnected( ) == true ) { cCSLock lock( &mCs ); PerIoContext* perIoConect = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_WRITE ); Packet* packet = (Packet*)perIoConect->buffer; packet->ver = PHVer; packet->hlen = PHLen; packet->tos = TOS_TTL; packet->tlen = packet->hlen; packet->seq = (++mSeq); perIoConect->offset = packet->tlen; return SendPost( perIoConect ); } return false; } // Decrypt Method void cIOCPClient::Decrypt(BYTE* ptr, RC5_32_KEY key) { unsigned char tiv[8] = {'I','R','I','S','I','R','I','S'}; int num = 0; LONG decryptlen = ((Packet*)ptr)->tlen; BYTE* decryptptr = ptr; decryptlen -= PHLen; decryptptr += PHLen; if ( decryptlen > 0 ) { RC5_32_cfb64_encrypt( decryptptr, decryptptr, decryptlen, &key, tiv, &num, RC5_DECRYPT ); } } // Encrypt Method void cIOCPClient::Encrypt(BYTE* ptr, RC5_32_KEY key) { unsigned char tiv[8] = {'I','R','I','S','I','R','I','S'}; int num = 0; LONG encryptlen = ((Packet*)ptr)->tlen; BYTE* encryptptr = ptr; encryptlen -= PHLen; encryptptr += PHLen; if ( encryptlen > 0 ) { RC5_32_cfb64_encrypt( encryptptr, encryptptr, encryptlen, &key, tiv, &num, RC5_ENCRYPT ); } } // Decrypt Method void cIOCPClient::Decrypt(BYTE key, long len, char* ptr) { len -= PHLen; ptr += PHLen; if ( len > 0 ) { for ( long i = 0; i < len; i++, ptr++ ) { (*ptr) = (*ptr) ^ SY_KEY_TABLE[key][i%3]; } } } /*-- Encrypt Method */ void cIOCPClient::Encrypt(BYTE key, long len, char* ptr) { len -= PHLen; ptr += PHLen; if ( len > 0 ) { for ( LONG i = 0; i < len; i++, ptr++ ) { (*ptr) = (*ptr) ^ SY_KEY_TABLE[key][i%3]; } } } /*-- SendPost Method */ bool cIOCPClient::SendPost(PerIoContext* perIoContext) { WSABUF wsaBuf; DWORD sendBytes = 0; DWORD flags = 0; int retcode; wsaBuf.len = perIoContext->offset; wsaBuf.buf = perIoContext->buffer; if ( mRC5Enable ) { BYTE key = (BYTE)(rand( ) & 0x0f); (*wsaBuf.buf) = ((*wsaBuf.buf) & 0xf0) | key; Encrypt( (BYTE*)wsaBuf.buf, RC5_32_KEY_TABLE[ ((*wsaBuf.buf) & 0x0f) ] ); } else { BYTE key = (BYTE)(rand( ) & 0x0f); Encrypt( key, wsaBuf.len, wsaBuf.buf ); (*wsaBuf.buf) = ((*wsaBuf.buf) & 0xf0) | key; } retcode = WSASend( perIoContext->socket, &wsaBuf, 1, &sendBytes, flags, &(perIoContext->wsaOverlapped), NULL ); if ( (retcode == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING) ) { PostQueuedCompletionStatus( mIocp, 0, NULL, (LPOVERLAPPED)perIoContext ); return false; } return true; } /*-- RecvPost Method */ bool cIOCPClient::RecvPost(PerIoContext* perIoContext) { WSABUF wsaBuf; DWORD recvBytes = 0; DWORD flags = 0; int retcode; wsaBuf.len = (perIoContext->length - perIoContext->offset); wsaBuf.buf = (perIoContext->buffer + perIoContext->offset); retcode = WSARecv( perIoContext->socket, &wsaBuf, 1, &recvBytes, &flags, &(perIoContext->wsaOverlapped), NULL ); if ( (retcode == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING) ) { PostQueuedCompletionStatus( mIocp, 0, NULL, (LPOVERLAPPED)perIoContext ); return false; } return true; } /*-- Close Method - CompletionKey¿Í Overlapped¸¦ ºñ±³ÈÄ CompletionKey¸¦ »èÁ¦ÇÑ´Ù. */ void cIOCPClient::Close(ULONG_PTR /*completionKey*/, PerIoContext* perIoContext) { cCSLock lock( &mCs ); DWORD error = 0; // ¿¬°áÇØÁ¦ ¿øÀÎ °Ë»ç. if ( (mConnected == true) && (mConnectionResult == true) ) { SOCKET sockTemp = mSocket; /*-- ¼ÒÄÏÀ» ¾ÈÀüÇÏ°Ô Á¾·áÇÑ´Ù. */ mSocket = INVALID_SOCKET; mConnected = false; mConnectionResult = false; if ( sockTemp != INVALID_SOCKET ) { closesocket( sockTemp ); } PostMessage( mWnd, mMessageID, ICT_CONNECTION_DEAD, (LPARAM)GetLastError( ) ); } // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context¸¦ ȸ¼ö. mIoContextPool->ReleaseIoContext( perIoContext ); if ( error != 0 ) assert( "WARNING - cIOCPClient::Close." ); } /*-- SendComplete Method */ bool cIOCPClient::SendComplete(ULONG_PTR, PerIoContext* perIoContext, DWORD) { // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context´Â ȸ¼öÇÑ´Ù. DWORD error = 0; mIoContextPool->ReleaseIoContext( perIoContext ); if ( error != 0 ) assert( "WARNING - cIOCPClient::SendComplete." ); return true; } /*-- RecvComplete Method (Default Echo Server) */ bool cIOCPClient::RecvComplete(ULONG_PTR, PerIoContext* perIoContext, DWORD bytesTransferred) { cCSLock lock( &mCs ); PerIoContext** backBuffer = mIoContextBackBuffer->buffer; long& backOffset = mIoContextBackBuffer->offset; // ¿ÀÇÁ¼ÂÀÇ Å©±â¸¦ ´©ÀûÇÑ´Ù. perIoContext->offset += bytesTransferred; DWORD length = perIoContext->offset; // ¼ö½ÅµÈ ´©Àû¹öÆÛÀÇ Å©±â DWORD offset = 0; // ¼ö½ÅµÈ ´©Àû¹öÆÛÀÇ ¿ÀÇÁ¼Â while ( length >= PHLen ) { Packet* packet = (Packet*)(perIoContext->buffer + offset); DWORD packetLen = packet->tlen; // µ¥ÀÌÅÍÀÇ ±æÀ̰¡ ÃÖ¼Ò ´ÜÀ§º¸´Ù ÀÛÀ» °æ¿ì ¿À·ù ó¸®ÇØ¾ß ÇÑ´Ù. if ( packetLen < PHLen ) return false; // µ¥ÀÌÅÍÀÇ ±æÀ̰¡ ¿ÏÀüÇÒ¶§±îÁö ÀÌ¾î ¹Þ±â¸¦ ÇØ¾ß ÇÑ´Ù. if ( packetLen > length ) break; // ¿©±â¼­ºÎÅÍ µ¥ÀÌÅÍ Ã³¸®¸¦ À§ÇÑ Äڵ带 »ðÀÔÇÑ´Ù. PerIoContext* ioContext = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_READ ); memcpy( ioContext->buffer, packet, packet->tlen ); ioContext->offset = packet->tlen; if ( mRC5Enable ) { Decrypt( (BYTE*)ioContext->buffer, RC5_32_KEY_TABLE[ packet->ver ] ); } else { Decrypt( packet->ver, packet->tlen, ioContext->buffer ); } if ( backOffset < MAX_IO_CONTEXT_BUFFER_LEN ) { backBuffer[ backOffset ] = ioContext; backOffset++; } // ó¸®ÈÄ Offset ¹× Length¸¦ ´Ù½Ã °è»êÇÑ´Ù. length -= packetLen; offset += packetLen; } // ÀÌ¾î ¹Þ±â¸¦ À§ÇØ µ¥ÀÌÅ͸¦ º¸°üÇÑÇÑ´Ù. if ( offset > 0 ) { PerIoContext* recvIoContext = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_READ ); DWORD error = 0; if ( length > 0 ) { memcpy( recvIoContext->buffer, (perIoContext->buffer + offset), length ); recvIoContext->offset = length; } mIoContextPool->ReleaseIoContext( perIoContext ); if ( error != 0 ) assert( "WARNING - cIOCPClient::RecvComplete." ); perIoContext = recvIoContext; } return RecvPost( perIoContext ); } /*-- Process Method; */ DWORD cIOCPClient::ConnectThread(void) { cCSLock lock( &mCs ); mConnected = (connect( mSocket, (LPSOCKADDR)&mAddr, sizeof(mAddr) ) != SOCKET_ERROR ) ? true : false; PostMessage( mWnd, mMessageID, (WPARAM)ICT_CONNECT, (LPARAM)0L ); return 0L; } /*-- Process Method; */ bool cIOCPClient::Process(WPARAM wParam, LPARAM lParam) { IOCP_CLIENT_TYPE type = (IOCP_CLIENT_TYPE)wParam; switch ( type ) { case ICT_CONNECT: // Ŭ¶ó¾ðÆ®¿¡¼­ ¿¬°á¸¦ ¿äûÇÑ °æ¿ì... if ( GetConnectionResult( ) ) { /// ¿¬°á¼º°ø. if( mWepLuncherInit == false ) { /*long authcode = THEAPP->GetAuthCode(); if( authcode != -1 ) { MSG_REQ_WEB_LAUNCHER msg; msg.Category = NM_USER; msg.Protocol = NM_USER_WEB_LAUNCHER_REQ; msg.LoginIdx = authcode; SendNetworkMsg( (char*)&msg, sizeof(msg) ); }*/ mWepLuncherInit = true; } } else if ( PostConnect( ) == false ) { /// ¿¬°á½ÇÆÐ. UIMAN->SendEventTopMsgBox( false ); UIMAN->CreateMsgBox( eStage_Login ,eMSGBOX_MODAL ,eMBEVENT_CONNECT_LOGINFAILE ,eSKIN_OK ,GAMERESOURCEMAN->GetGameText( 3 ) ,GAMERESOURCEMAN->GetGameText( 301 ) ); } break; case ICT_DISCONNECT: // Ŭ¶ó¾ðÆ®¿¡¼­ ¿¬°á ÇØÁ¦¸¦ ¿äûÇÑ °æ¿ì¸¸... break; case ICT_CONNECTION_DEAD: { // ¼­¹ö·ÎºÎÅÍ ¿¬°áÀÌ ÇØÁ¦ µÇ¾úÀ» °æ¿ì... UIMAN->CreateMsgBox( eStage_Login, eMSGBOX_MODAL, eMBEVENT_CONNECT_DEAD, eSKIN_OK, GAMERESOURCEMAN->GetGameText( 61 ), GAMERESOURCEMAN->GetGameText( 301 ) ); } break; case ICT_RECV: { IoContextBuffer* temp; // Double Buffering ó¸®. EnterCriticalSection( &mCs ); temp = mIoContextBackBuffer; mIoContextBackBuffer = mIoContextFrontBuffer; mIoContextFrontBuffer = temp; LeaveCriticalSection( &mCs ); // Àü¼Û½ÃÀÛ. PerIoContext** frontBuffer = mIoContextFrontBuffer->buffer; long& frontOffset = mIoContextFrontBuffer->offset; while ( frontOffset > 0 ) { PerIoContext* perIoContext = (*frontBuffer); Packet* packet = (Packet*)perIoContext->buffer; char* buffer = perIoContext->buffer + packet->hlen; u_long length = packet->tlen - packet->hlen; mLastTos = packet->tos; switch ( packet->tos ) { case TOS_LOGIN: try { MSGROOT* msgRoot = (MSGROOT*)buffer; mLastCategory = msgRoot->Category; mLastProtocol = msgRoot->Protocol; STAGEMAN->NetworkMsgParser( 0, buffer, length ); ReleaseNetworkMsg( perIoContext ); } catch ( ... ) { ErrorLog( "In TOS_LOGIN:NetworkMsgParser(%d,%d) Method. Throwing 'cIOCPClient::Process' exception", mLastCategory, mLastProtocol ); throw; } break; case TOS_LOGIN_ACCEPT: mRC5Enable = true; /// ·Î±×ÀÎ ¼­¹ö¿ÍÀÇ ¿¬°á ¼º°ø UIMAN->SendEventTopMsgBox( true ); ReleaseNetworkMsg( perIoContext ); break; case TOS_LOGIN_2_GAME: { /// °ÔÀÓ¼­¹ö Á¢¼Ó. Login2Game* login2Game = (Login2Game*)packet; unsigned long ipv4 = login2Game->ipv4; unsigned short port = login2Game->port; ReleaseNetworkMsg( perIoContext ); PostConnect( ipv4, port ); } break; case TOS_GAME: try { MSGROOT* msgRoot = (MSGROOT*)buffer; mLastCategory = msgRoot->Category; mLastProtocol = msgRoot->Protocol; STAGEMAN->NetworkMsgParser( 0, buffer, length ); ReleaseNetworkMsg( perIoContext ); } catch ( ... ) { ErrorLog( "In TOS_GAME:NetworkMsgParser(%d,%d) Method. Throwing 'cIOCPClient::Process' exception", mLastCategory, mLastProtocol ); throw; } break; case TOS_GAME_ACCEPT: { mRC5Enable = false; /// °ÔÀÓ¼­¹ö ÀÎÁõ½Ãµµ. MSG_REQ_GAMESRV gamein; ::memset( &gamein, 0, sizeof(gamein) ); gamein.Category = NM_USER; gamein.Protocol = NM_USER_GAMESRV_REQ; gamein.LoginIdx = STAGEMAN->GetLoginIdx( ); gamein.MemberIdx = STAGEMAN->GetUserIdx( ); SendNetworkMsg( (char*)&gamein, sizeof(gamein) ); /// ¸Þ¼¼Áö °»½Å UIMAN->ChangeTextMsgBox( eMBEVENT_CONNECT_GAMESRV, GAMERESOURCEMAN->GetGameText( 12 ) ); ReleaseNetworkMsg( perIoContext ); } break; case TOS_GAME_2_GAME: { /// °ÔÀÓ³» ä³Îº¯°æ. Game2Game* game2Game = (Game2Game*)packet; unsigned long ipv4 = game2Game->ipv4; unsigned short port = game2Game->port; ReleaseNetworkMsg( perIoContext ); PostConnect( ipv4, port ); } break; /* case TOS_HACKSHIELD: { MSGROOT* msgRoot = (MSGROOT*)buffer; mLastCategory = msgRoot->Category; mLastProtocol = msgRoot->Protocol; switch( msgRoot->Category ) { case NM_HACKSHIELD: { switch( msgRoot->Protocol ) { case NM_HACKSHIELD_SYN: { MSG_SYN_HACKSHIELD* pSyn = (MSG_SYN_HACKSHIELD*)msgRoot; AHNHS_TRANS_BUFFER stResponseBuf; unsigned long ulRet = _AhnHS_MakeResponse( pSyn->byBuffer, pSyn->nLength, &stResponseBuf ); if( stResponseBuf.nLength > ANTICPX_TRANS_BUFFER_MAX ) { cString str; str.Format("Error ANTICPX_TRANS_BUFFER_MAX %u", ulRet ); ::MessageBoxA(NULL, str.Cstr(), "Error", MB_OK ); break; } MSG_REQ_HACKSHIELD reqMsg; ::memset( &reqMsg, 0, sizeof(reqMsg) ); reqMsg.Category = NM_HACKSHIELD; reqMsg.Protocol = NM_HACKSHIELD_REQ; reqMsg.nLength = stResponseBuf.nLength; memcpy( reqMsg.byBuffer, stResponseBuf.byBuffer, stResponseBuf.nLength ); SendHackShieldMsg( (char*)&reqMsg, (unsigned short)reqMsg.GetMsgLength() ); } break; default: assert(NULL); } } break; default: assert(NULL); } ReleaseNetworkMsg( perIoContext ); } break; */ default: assert( 0 && "UNKNOWN - TYPE OF SERVICE." ); ReleaseNetworkMsg( perIoContext ); break; } (*frontBuffer) = NULL; frontBuffer++; frontOffset--; } } break; } return true; } /*-- WorkerThread Method */ DWORD cIOCPClient::WorkerThread(void) { DWORD bytesTransferred; ULONG_PTR completionKey; OVERLAPPED* overlapped; BOOL retValue; PerIoContext* perIoContext; try { while ( true ) { /*-- completion port¿¡ ÇÒ´çµÈ ¸ðµç ¼ÒÄÏÀÇ I/O ¿Ï·á¸¦ ±â´Ù¸°´Ù. */ retValue = GetQueuedCompletionStatus( mIocp, &bytesTransferred, &completionKey, &overlapped, INFINITE ); /*-- Shutdown */ if ( overlapped == IOCP_SHUTDOWN ) return 0; perIoContext = (PerIoContext*)overlapped; /*-- client connection dropped, continue to service remaining (and possibly new) client connections */ if ( retValue == FALSE || bytesTransferred == 0 ) { perIoContext->offset = max( perIoContext->offset, bytesTransferred ); Close( completionKey, perIoContext ); continue; } switch ( perIoContext->requestType ) { case IOCP_REQUEST_READ: if ( RecvComplete( completionKey, perIoContext, bytesTransferred ) == false ) { Close( completionKey, perIoContext ); } break; // Receive ¿Ï·á Çڵ鷯 ÇÔ¼ö. case IOCP_REQUEST_WRITE: if ( SendComplete( completionKey, perIoContext, bytesTransferred ) == false ) { Close( completionKey, perIoContext ); } break; // Send ¿Ï·á Çڵ鷯 ÇÔ¼ö. } } } catch ( ... ) { ErrorLog( "Throwing 'cIOCPClient::WorkerThread' exception" ); throw; } return 0; } /*-- ConnectThreadStartingPoint Method ( Connect Thread ½ÃÀÛ Æ÷ÀÎÆ® ) */ DWORD cIOCPClient::ConnectThreadStartingPoint(void* ptr) { cIOCPClient* client = (cIOCPClient*)ptr; return client->ConnectThread( ); } /*-- WorkerThreadStartingPoint Method ( Worker Thread ½ÃÀÛ Æ÷ÀÎÆ® ) */ DWORD cIOCPClient::WorkerThreadStartingPoint(void* ptr) { cIOCPClient* client = (cIOCPClient*)ptr; return client->WorkerThread( ); }