// Include files #include "iocpserver.h" // Local definitions #pragma warning( disable: 4127 ) #define MAX_BUFFER 0x10000 // 64KByte #define SafeCloseHandle(P) if (P!=NULL) { CloseHandle(P); (P)=NULL; } // Çڵ鸦 ¾ÈÀüÇÏ°Ô ÇØÁ¦ #define SafeDelete(P) if (P!=NULL) { delete(P); (P)=NULL; } // " // Global data // cIocpServer Constructor cIocpServer::cIocpServer(void) { // Critical Section¸¦ ÃʱâÈ­. InitializeCriticalSectionAndSpinCount( &mCs, 2000 ); mIocp = NULL; mSocket = INVALID_SOCKET; mIocpAcceptThread = NULL; mIocpWorkerThreadNumber = 0; mIocpBackendThread = NULL; mIoContextPool = NULL; mSocketContextPool = NULL; mIoContextFrontBuffer = NULL; mIoContextBackBuffer = NULL; mRunServer = false; mEndServer = false; } // ~cIocpServer Destructor. cIocpServer::~cIocpServer(void) { // Á¾·á Shutdown( ); // Critical Section¸¦ ÇØÁ¦. DeleteCriticalSection( &mCs ); } // Initialize Method bool cIocpServer::Initialize(char* ipAddr, unsigned short port, unsigned short numWorkerThreads, u_long bufferLength) { // 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++ ) { // Worker Thread»ý¼º. mIocpWorkerThread[i] = CreateThread( NULL, 0, WorkerThreadStartingPoint, (LPVOID)this, 0, &threadId ); if ( mIocpWorkerThread[i] == NULL ) return false; } // Socket Context Pool¸¦ »ý¼ºÇÑ´Ù. mSocketContextPool = new cSocketContextPool( &mCs ); if ( mSocketContextPool == NULL ) return false; // I/O Context Pool¸¦ »ý¼ºÇÑ´Ù. // Recv/Send¸¦ À§ÇØ Å¬¶óÀÌ¾ðÆ® * 2·Î °è»êÇÑ´Ù. // mIoContextPool = new cIoContextPool( bufferLength ); 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; // ¼­¹ö ¼ÒÄÏÀ» »ý¼ºÇÑ´Ù. // PHOSTENT phe; int zero; LINGER linger; // listenÇÒ ¼­¹ö ¼ÒÄÏÀ» »ý¼ºÇÑ´Ù. mSocket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED ); if ( mSocket == INVALID_SOCKET ) return false; ZeroMemory( (void*)&mAddr, sizeof(mAddr) ); mAddr.sin_family = AF_INET; mAddr.sin_port = htons( port ); mAddr.sin_addr.s_addr = strlen( ipAddr ) ? inet_addr( ipAddr ) : INADDR_NONE; if ( mAddr.sin_addr.s_addr == INADDR_NONE ) { // the host name for the server is not in dot format, therefore try it just as a string // ¼­¹öÀÇ È£½ºÆ® À̸§ÀÌ µµÆ® Çü½Ä(IPv4)ÀÌ ¾Æ´Ï¹Ç·Î, ¹®ÀÚ¿­·Î Àç ½Ãµµ. // if ( (phe = gethostbyname( "" )) != NULL ) CopyMemory( &mAddr.sin_addr, phe->h_addr_list[0], phe->h_length ); else return false; } mPort = port; if ( bind( mSocket, (LPSOCKADDR)&mAddr, sizeof(mAddr) ) == SOCKET_ERROR ) return false; if ( listen( mSocket, SOMAXCONN ) == SOCKET_ERROR ) return false; // 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; // 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 ) return 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 ) return false; // 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 ) return false; mEndServer = false; mRunServer = true; // Accept Thread¸¦ »ý¼ºÇÑ´Ù. mIocpAcceptThread = CreateThread( NULL, 0, AcceptThreadStartingPoint, (LPVOID)this, 0, &threadId ); if ( mIocpAcceptThread == NULL ) return false; // Backend Thread¸¦ »ý¼ºÇÑ´Ù. mIocpBackendThread = CreateThread( NULL, 0, BackendThreadStartingPoint, (LPVOID)this, 0, &threadId ); if ( mIocpBackendThread == NULL ) return false; return true; } // Shutdown Method void cIocpServer::Shutdown(DWORD maxWait) { // ¾ÈÀüÇÏ°Ô Á¾·áÇÑ´Ù. mEndServer = true; mRunServer = false; // Backend Thread°¡ Á¾·áµÉ¶§±îÁö ´ë±â if ( mIocpBackendThread != NULL ) { WaitForSingleObjectEx( mIocpBackendThread, maxWait, TRUE ); CloseHandle( mIocpBackendThread ); mIocpBackendThread = NULL; } // ¼ÒÄÏÀ» Á¾·áÇÑ´Ù. SOCKET sockTemp = mSocket; mSocket = INVALID_SOCKET; if ( sockTemp != INVALID_SOCKET ) { closesocket( sockTemp ); } // Accept Thread°¡ Á¾·áµÉ¶§±îÁö ´ë±â if ( mIocpAcceptThread != NULL ) { WaitForSingleObjectEx( mIocpAcceptThread, maxWait, TRUE ); CloseHandle( mIocpAcceptThread ); mIocpAcceptThread = NULL; } // Cause worker threads to exit if ( mIocp != NULL ) { for ( int i = 0; i < mIocpWorkerThreadNumber; i++ ) { PostQueuedCompletionStatus( mIocp, 0, 0, IOCP_SHUTDOWN ); } } // Make sure worker threads exits. 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 ); } } CloseHandle( mIocpWorkerThread[i] ); mIocpWorkerThread[i] = NULL; } // Overlapped I/O Model Pool¹× Socket Context Pool¸¦ ÇØÁ¦ÇÑ´Ù. if ( mIoContextFrontBuffer ) { GlobalFree( mIoContextFrontBuffer ); mIoContextFrontBuffer = NULL; } if ( mIoContextBackBuffer ) { GlobalFree( mIoContextBackBuffer ); mIoContextBackBuffer = NULL; } SafeDelete( mIoContextPool ); SafeDelete( mSocketContextPool ); // completion port¸¦ ÇØÁ¦ÇÑ´Ù. SafeCloseHandle( mIocp ); } // GetPoolUsage Method void cIocpServer::GetIoPoolUsage(SIZE_T& quotaPagedPoolUsage, SIZE_T& quotaNonePagedPoolUsage, SIZE_T& workingSetSize) { if ( mIoContextPool != NULL ) { mIoContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize ); } } // GetPoolUsage Method void cIocpServer::GetSocketPoolUsage(SIZE_T& quotaPagedPoolUsage, SIZE_T& quotaNonePagedPoolUsage, SIZE_T& workingSetSize) { if ( mSocketContextPool != NULL ) { mSocketContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize ); } } // QueueRequest Method BOOL cIocpServer::QueueRequest(ULONG_PTR completionKey, LPOVERLAPPED overlapped, DWORD bytesTransferred) { return PostQueuedCompletionStatus( mIocp, bytesTransferred, completionKey, overlapped ); } // AcceptComplete Method bool cIocpServer::AcceptComplete(PerSocketContext* /*perSocketContext*/) { return true; } // AcceptThread Method DWORD cIocpServer::AcceptThread( ) { while ( true ) { PerSocketContext* perSocketContext = NULL; PerIoContext* perIoContext = NULL; SOCKET socket; SOCKADDR_IN addr; int addrlen; // ¿¬°áÀ» ¼ö¶ôÇϰí completion port¸¦ ÁöÁ¤ÇÑ´Ù. addrlen = sizeof( addr ); socket = WSAAccept( mSocket, (sockaddr*)&addr, &addrlen, NULL, 0 ); if ( socket == SOCKET_ERROR ) break; // ¼­¹öÁ¾·á, Thread¸¦ ³ª°£´Ù. if ( mEndServer == true ) break; // ÇϳªÀÇ ¿¬°áº°·Î TCP keepalive¸¦ Ȱ¼ºÈ­½Ã۰í keepalive °£°ÝÀ» ¼³Á¤ÇÒ ¶§ »ç¿ëÇÑ´Ù. // keepaliveinterval: ÀÀ´äÀ» ¹ÞÀ» ¶§±îÁö keepalive ÆÐŶÀ» ÀçÀü¼ÛÇÒ °£°ÝÀÌ´Ù. // keepalivetime : ¿¬°áÀÌ À¯È¿ÇÑÁö È®ÀÎÇϱâ À§ÇÏ¿© keepalive ÆÐŶÀ» Àü¼ÛÇÒ °£°ÝÀÌ´Ù. // // Header : Declared in Mstcpip.h. // // tcp_keepalive keepAlive; // DWORD bytesReturned; // // keepAlive.onoff = 1; // keepAlive.keepalivetime = 3000; // keepAlive.keepaliveinterval = 1000; // // if ( WSAIoctl( socket, SIO_KEEPALIVE_VALS, &keepAlive, sizeof(keepAlive), 0, 0, &bytesReturned, NULL, NULL ) == SOCKET_ERROR ) // { // closesocket( socket ); // continue; // } // cSocketContextPool class¿¡¼­ PerSocketContext¸¦ °¡Á®¿Â´Ù. perSocketContext = mSocketContextPool->GetPerSocketContext( socket, addr, MAX_TTL ); if ( perSocketContext == NULL ) break; // Accept¿¡ ÀÇÇÏ¿© ¸®ÅÏµÈ ¼ÒÄÏ ÇÚµéÀ» completion port¿¡ ÇÒ´çÇÑ´Ù. if ( CreateIoCompletionPort( (HANDLE)socket, mIocp, (ULONG_PTR)perSocketContext, 0 ) == NULL ) break; // Accept ¿Ï·á Çڵ鷯 ÇÔ¼ö. if ( AcceptComplete( perSocketContext ) == false ) { // SocketContext ȸ¼ö. mSocketContextPool->ReleasePerSocketContext( perSocketContext ); continue; } // ¼ö½ÅÀ» À§ÇØ I/O Context¸¦ ÁغñÇÑ´Ù. perIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_READ ); if ( RecvPost( perIoContext ) == false ) { Close( perSocketContext ); continue; } } return 0; } // SendExec Method bool cIocpServer::SendExec(PerIoContext* perIoContext) { WSABUF wsaBuf; DWORD sendBytes = 0; DWORD flags = 0; int retcode; wsaBuf.len = perIoContext->offset; wsaBuf.buf = perIoContext->buffer; retcode = WSASend( perIoContext->socket, &wsaBuf, 1, &sendBytes, flags, &(perIoContext->wsaOverlapped), NULL ); if ( (retcode == SOCKET_ERROR) && (WSAGetLastError() != WSA_IO_PENDING) ) { mIoContextPool->ReleaseIoContext( perIoContext ); return false; } return true; } // SendPost Method bool cIocpServer::SendPost(PerIoContext* perIoContext) { cCSLock lock( &mCs ); PerIoContext** buffer = mIoContextBackBuffer->buffer; long& offset = mIoContextBackBuffer->offset; if ( offset < MAX_IO_CONTEXT_BUFFER_LEN ) { if ( perIoContext->offset < mIoContextPool->GetBufferLength( ) ) { buffer[ offset ] = perIoContext; offset++; return true; } else throw "Error cIocpServer::SendPost:Exceed the length of the buffer used."; } else { mIoContextPool->ReleaseIoContext( perIoContext ); } return false; } // RecvPost Method bool cIocpServer::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) ) { mIoContextPool->ReleaseIoContext( perIoContext ); return false; } return true; } // Close Method - CompletionKey¿Í Overlapped¸¦ ºñ±³ÈÄ CompletionKey¸¦ »èÁ¦ÇÑ´Ù. void cIocpServer::Close(PerSocketContext* perSocketContext, PerIoContext* perIoContext) { cCSLock lock( &mCs ); // »ç¿ëÀÌ ¿Ï·áµÈ Socket Context¸¦ ȸ¼ö. if ( perSocketContext->socket == perIoContext->socket ) { // ¼Û/¼ö½Å ºñȰ¼ºÈ­. if ( !perSocketContext->status.connectionDead ) { shutdown( perSocketContext->socket, SD_RECEIVE ); perSocketContext->status.connectionDead = 1; } } // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context¸¦ ȸ¼ö. mIoContextPool->ReleaseIoContext( perIoContext ); } void cIocpServer::Close(PerSocketContext* perSocketContext) { cCSLock lock( &mCs ); // ¼Û/¼ö½Å ºñȰ¼ºÈ­. if ( !perSocketContext->status.connectionDead ) { shutdown( perSocketContext->socket, SD_RECEIVE ); perSocketContext->status.connectionDead = 1; } } // SendComplete Method bool cIocpServer::SendComplete(PerSocketContext* /*perSocketContext*/, PerIoContext* perIoContext, DWORD /*bytesTransferred*/) { // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context´Â ȸ¼öÇÑ´Ù. mIoContextPool->ReleaseIoContext( perIoContext ); return true; } // RecvComplete Method (Default Echo Server) bool cIocpServer::RecvComplete(PerSocketContext* perSocketContext, PerIoContext* perIoContext, DWORD bytesTransferred) { // ¼ö½ÅµÈ µ¥ÀÌÅ͸¦ Echo·Î µ¹·Á º¸³½´Ù. perIoContext->offset = bytesTransferred; perIoContext->requestType = IOCP_REQUEST_WRITE; if ( SendPost( perIoContext ) == false ) { Close( perSocketContext ); return false; } // ¼ö½ÅÀ» À§ÇØ I/O Context¸¦ ÁغñÇÑ´Ù. perIoContext = mIoContextPool->GetIoContext( perSocketContext->socket, IOCP_REQUEST_READ ); if ( RecvPost( perIoContext ) == false ) { Close( perSocketContext ); return false; } return true; } // CallbackComplete Method bool cIocpServer::CallbackComplete(PerSocketContext* /*perSocketContext*/, PerIoContext* perIoContext, DWORD /*bytesTransferred*/) { // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context´Â ȸ¼öÇÑ´Ù. mIoContextPool->ReleaseIoContext( perIoContext ); return true; } /*-- IoContextPresent Method */ void cIocpServer::IoContextPresent( ) { IoContextBuffer* temp; // Double Buffering ó¸®. CSBlock( &mCs ) { temp = mIoContextBackBuffer; mIoContextBackBuffer = mIoContextFrontBuffer; mIoContextFrontBuffer = temp; } // Àü¼Û½ÃÀÛ. PerIoContext** buffer = mIoContextFrontBuffer->buffer; long& offset = mIoContextFrontBuffer->offset; while ( offset > 0 ) { SendExec( (*buffer) ); (*buffer) = NULL; buffer++; offset--; } } // WorkerThread Method DWORD cIocpServer::WorkerThread( ) { DWORD bytesTransferred; ULONG_PTR completionKey; OVERLAPPED* overlapped; BOOL retValue; PerSocketContext* perSocketContext; PerIoContext* perIoContext; while ( true ) { // completion port¿¡ ÇÒ´çµÈ ¸ðµç ¼ÒÄÏÀÇ I/O ¿Ï·á¸¦ ±â´Ù¸°´Ù. retValue = GetQueuedCompletionStatus( mIocp, &bytesTransferred, &completionKey, &overlapped, INFINITE ); // Shutdown if ( overlapped == IOCP_SHUTDOWN ) return 0; perSocketContext = (PerSocketContext*)completionKey; perIoContext = (PerIoContext*)overlapped; // client connection dropped, continue to service remaining (and possibly new) client connections if ( retValue == FALSE || bytesTransferred == 0 ) { // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context´Â ȸ¼öÇÑ´Ù. perIoContext->offset = max( perIoContext->offset, bytesTransferred ); Close( perSocketContext, perIoContext ); continue; } switch ( perIoContext->requestType ) { case IOCP_REQUEST_READ: RecvComplete( perSocketContext, perIoContext, bytesTransferred ); break; // Receive ¿Ï·á Çڵ鷯 ÇÔ¼ö. case IOCP_REQUEST_WRITE: SendComplete( perSocketContext, perIoContext, bytesTransferred ); break; // Send ¿Ï·á Çڵ鷯 ÇÔ¼ö. case IOCP_REQUEST_CALLBACK: CallbackComplete( perSocketContext, perIoContext, bytesTransferred ); break; // Callback ¿Ï·á Çڵ鷯 ÇÔ¼ö. } } return 0; } // BackendThread Method DWORD cIocpServer::BackendThread( ) { DWORD beginTick; DWORD endTick; DWORD tickDiff; while ( true ) { beginTick = GetTickCount( ); // ¼­¹öÁ¾·á, Thread¸¦ ³ª°£´Ù. if ( mEndServer == true ) break; // »ç¿ëÀÚÀÇ TTL(Time To Live)¸¦ °Ë»ç. if ( mSocketContextPool != NULL ) { cCSLock lock( &mCs ); PerSocketContext* socketContext = mSocketContextPool->GetPagedPoolUsage( ); PerSocketContext* next = NULL; while ( socketContext != NULL ) { next = socketContext->next; if ( socketContext->status.connectionDead ) { // Close Socket On. socketContext->status.closeSocket = 1; } else { // TTL°ª È®ÀÎ if ( beginTick > socketContext->timeToLive ) { PerIoContext* cbIoContext = mIoContextPool->GetIoContext( socketContext->socket, IOCP_REQUEST_CALLBACK ); PostQueuedCompletionStatus( mIocp, 0, (ULONG_PTR)socketContext, (LPOVERLAPPED)cbIoContext ); } } if ( socketContext->status.closeSocket ) { // SocketContext ȸ¼ö. mSocketContextPool->ReleasePerSocketContext( socketContext ); } socketContext = next; } // while ( socketContext != NULL ) } // I/O Context ó¸®. IoContextPresent( ); endTick = GetTickCount( ); tickDiff = endTick - beginTick; Sleep( 10 ); } return 0; } // AcceptThreadStartingPoint Method ( Accept Thread ½ÃÀÛ Æ÷ÀÎÆ® ) DWORD cIocpServer::AcceptThreadStartingPoint(void* ptr) { cIocpServer* iocpServer = (cIocpServer*)ptr; return iocpServer->AcceptThread( ); } // WorkerThreadStartingPoint Method ( Worker Thread ½ÃÀÛ Æ÷ÀÎÆ® ) DWORD cIocpServer::WorkerThreadStartingPoint(void* ptr) { cIocpServer* iocpServer = (cIocpServer*)ptr; return iocpServer->WorkerThread( ); } // BackendThreadStartingPoint Method ( Backend Thread ½ÃÀÛ Æ÷ÀÎÆ® ) DWORD cIocpServer::BackendThreadStartingPoint(void* ptr) { cIocpServer* iocpServer = (cIocpServer*)ptr; return iocpServer->BackendThread( ); }