/*-- Include files */ #include "iocpclient.h" /*-- Local definitions */ #pragma warning( disable: 4127 ) #define SafeCloseHandle(P) if (P!=NULL) { CloseHandle(P); (P)=NULL; } // Çڵ鸦 ¾ÈÀüÇÏ°Ô ÇØÁ¦ #define SafeDelete(P) if (P!=NULL) { delete(P); (P)=NULL; } // " /*-- Global data */ /*-- cIocpClient Constructor */ cIocpClient::cIocpClient(void) { // Critical Section¸¦ ÃʱâÈ­. InitializeCriticalSectionAndSpinCount( &mCs, 2000 ); mIocp = NULL; mSocket = INVALID_SOCKET; mIocpWorkerThreadNumber = 0; mIoContextPool = NULL; mIocpBackendThread = NULL; mConnected = false; mConnectionDead = false; mCloseSocket = false; mTimeToLive = 0; mRunClient = false; mEndClient = false; } /*-- ~cIocpClient Destructor. */ cIocpClient::~cIocpClient(void) { // Á¾·á Shutdown( ); // Critical Section¸¦ ÇØÁ¦. DeleteCriticalSection( &mCs ); } /*-- Initialize Method */ bool cIocpClient::Initialize(char* ipAddr, unsigned short port, unsigned short numWorkerThreads, unsigned int 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++ ) { mIocpWorkerThread[i] = CreateThread( NULL, 0, WorkerThreadStartingPoint, (LPVOID)this, 0, &threadId ); if ( mIocpWorkerThread[i] == NULL ) return false; } /*-- I/O Context Pool¸¦ »ý¼ºÇÑ´Ù. */ mIoContextPool = new cIoContextPool( bufferLength ); if ( mIoContextPool == NULL ) return false; /*-- Ŭ¶óÀÌ¾ðÆ® ¼ÒÄÏÀ» »ý¼ºÇÑ´Ù. */ SOCKADDR_IN addr; PHOSTENT phe; ZeroMemory( (void*)&addr, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_port = htons( port ); addr.sin_addr.s_addr = inet_addr( ipAddr ); if ( addr.sin_addr.s_addr == INADDR_NONE ) { // the host name for the server is not in dot format, therefore try it just as a string if ( (phe = gethostbyname( ipAddr )) != NULL ) CopyMemory( &addr.sin_addr, phe->h_addr_list[0], phe->h_length ); else return false; } // ÁÖ¼ÒÀúÀå. mAddr = addr; mPort = port; // ȯ°æº¯¼ö ¼³Á¤. mConnected = false; mConnectionDead = false; mCloseSocket = false; mTimeToLive = 0; mEndClient = false; mRunClient = true; // Backend Thread¸¦ »ý¼ºÇÑ´Ù. mIocpBackendThread = CreateThread( NULL, 0, BackendThreadStartingPoint, (LPVOID)this, 0, &threadId ); if ( mIocpBackendThread == NULL ) return false; return true; } /*-- Shutdown Method */ void cIocpClient::Shutdown(DWORD maxWait) { /*-- ¾ÈÀüÇÏ°Ô Á¾·áÇÑ´Ù. */ mEndClient = true; mRunClient = false; // Backend Thread°¡ Á¾·áµÉ¶§±îÁö ´ë±â if ( mIocpBackendThread != NULL ) { WaitForSingleObjectEx( mIocpBackendThread, maxWait, TRUE ); CloseHandle( mIocpBackendThread ); mIocpBackendThread = 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¸¦ ÇØÁ¦ÇÑ´Ù. */ SafeDelete( mIoContextPool ); /*-- completion port¸¦ ÇØÁ¦ÇÑ´Ù. */ SafeCloseHandle( mIocp ); /*-- ¼­¹ö¿Í ¿¬°áÁ¾·á. */ Disconnect( ); } /*-- Connect Method */ bool cIocpClient::Connect(SOCKADDR_IN addr) { int zero; LINGER linger; // connectÇÒ Å¬¶óÀÌ¾ðÆ® ¼ÒÄÏÀ» »ý¼ºÇÑ´Ù. mSocket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED ); if ( mSocket == INVALID_SOCKET ) return false; // connect¸¦ ½Ãµµ ÇÑ´Ù. if ( connect( mSocket, (LPSOCKADDR)&addr, sizeof(addr) ) == SOCKET_ERROR ) { closesocket( mSocket ); return (mConnected=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 ¿¬°á¿¡ ´ëÇØ¼­µµ ³×À̱۸µÀ» »ç¿ëÇÒ ¼ö ¾ø°Ô ¸¸µì´Ï´Ù. µû¶ó¼­ ´Ù¼öÀÇ ¼ÒÇü ÆÄÀÏ Á¶ÀÛ ¸í·ÉÀ» ¹ßÇàÇÏ´Â ÀÀ¿ë ÇÁ·Î±×·¥ÀÇ °æ¿ì ¼º´ÉÀÌ Çâ»óµÉ ¼ö ÀÖ½À´Ï´Ù. ±×·¯ÇÑ ÀÀ¿ë ÇÁ·Î±×·¥À¸·Î´Â ÆÄÀÏ Àá±Ý/Àá±Ý ÇØÁ¦ ±â´ÉÀ» ÀÚÁÖ »ç¿ëÇÏ´Â ÀÀ¿ë ÇÁ·Î ±×·¥À» µé ¼ö ÀÖ½À´Ï´Ù. BOOL nodelay = TRUE; if ( setsockopt( mSocket, IPPROTO_TCP, TCP_NODELAY, (BOOL*)&nodelay, sizeof(BOOL) ) == 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 ) { closesocket( mSocket ); return (mConnected=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 ); return (mConnected=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 ) { closesocket( mSocket ); return (mConnected=false); } // TTL. mTimeToLive = GetTickCount( ) + MIN_TTL; /*-- connect¿¡ ÀÇÇÏ¿© ¸®ÅÏµÈ ¼ÒÄÏ ÇÚµéÀ» completion port¿¡ ÇÒ´çÇÑ´Ù. */ if ( CreateIoCompletionPort( (HANDLE)mSocket, mIocp, (ULONG_PTR)NULL, 0 ) == NULL ) { closesocket( mSocket ); return (mConnected=false); } /*-- ¼ö½ÅÀ» À§ÇØ I/O Context¸¦ ÁغñÇÑ´Ù. */ PerIoContext* perIoContext = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_READ ); RecvPost( NULL, perIoContext ); return (mConnected=true); } /*-- Disconnect Method */ bool cIocpClient::Disconnect(void) { cCSLock lock( &mCs ); SOCKET sockTemp = mSocket; mSocket = INVALID_SOCKET; mConnected = false; mConnectionDead = false; mCloseSocket = false; if ( sockTemp != INVALID_SOCKET ) { closesocket( sockTemp ); } return true; } /*-- GetIoPoolUsage Method */ void cIocpClient::GetIoPoolUsage(SIZE_T& quotaPagedPoolUsage, SIZE_T& quotaNonePagedPoolUsage, SIZE_T& workingSetSize) { if ( mIoContextPool != NULL ) { mIoContextPool->GetProcessMemoryInfo( quotaPagedPoolUsage, quotaNonePagedPoolUsage, workingSetSize ); } } /*-- QueueRequest Method */ BOOL cIocpClient::QueueRequest(ULONG_PTR completionKey, LPOVERLAPPED overlapped, DWORD bytesTransferred) { return PostQueuedCompletionStatus( mIocp, bytesTransferred, completionKey, overlapped ); } /*-- SendPost Method */ bool cIocpClient::SendPost(ULONG_PTR completionKey, 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) ) { PostQueuedCompletionStatus( mIocp, 0, completionKey, (LPOVERLAPPED)perIoContext ); return false; } return true; } /*-- RecvPost Method */ bool cIocpClient::RecvPost(ULONG_PTR completionKey, 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, completionKey, (LPOVERLAPPED)perIoContext ); return false; } return true; } /*-- CallbackPost Method */ bool cIocpClient::CallbackPost(ULONG_PTR completionKey, PerIoContext* perIoContext) { int retcode; retcode = PostQueuedCompletionStatus( mIocp, perIoContext->offset, completionKey, (LPOVERLAPPED)perIoContext ); if ( retcode == 0 ) { return false; } return true;; } /*-- Close Method - CompletionKey¿Í Overlapped¸¦ ºñ±³ÈÄ CompletionKey¸¦ »èÁ¦ÇÑ´Ù. */ void cIocpClient::Close(ULONG_PTR /*completionKey*/, PerIoContext* perIoContext) { cCSLock lock( &mCs ); if ( perIoContext->socket == mSocket ) { // ¼Û/¼ö½Å ºñȰ¼ºÈ­. if ( !mConnectionDead ) { shutdown( mSocket, SD_BOTH ); mConnectionDead = true; // ¼­¹ö¿Í ¿¬°á ÇØÁ¦. Disconnect( ); } } // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context¸¦ ȸ¼ö. mIoContextPool->ReleaseIoContext( perIoContext ); } /*-- SendComplete Method */ bool cIocpClient::SendComplete(ULONG_PTR /*completionKey*/, PerIoContext* perIoContext, DWORD /*bytesTransferred*/) { // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context´Â ȸ¼öÇÑ´Ù. mIoContextPool->ReleaseIoContext( perIoContext ); return true; } /*-- RecvComplete Method (Default Echo Server) */ bool cIocpClient::RecvComplete(ULONG_PTR completionKey, PerIoContext* perIoContext, DWORD bytesTransferred) { cCSLock lock( &mCs ); /*-- ¼ö½ÅµÈ µ¥ÀÌÅ͸¦ Echo·Î µ¹·Á º¸³½´Ù. */ perIoContext->offset = bytesTransferred; perIoContext->requestType = IOCP_REQUEST_WRITE; if ( SendPost( NULL, perIoContext ) == false ) return false; /*-- ¼ö½ÅÀ» À§ÇØ I/O Context¸¦ ÁغñÇÑ´Ù. */ perIoContext = mIoContextPool->GetIoContext( mSocket, IOCP_REQUEST_READ ); return RecvPost( completionKey, perIoContext ); } /*-- CallbackComplete Method */ bool cIocpClient::CallbackComplete(ULONG_PTR /*completionKey*/, PerIoContext* perIoContext, DWORD /*bytesTransferred*/) { // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context´Â ȸ¼öÇÑ´Ù. mIoContextPool->ReleaseIoContext( perIoContext ); return true; } /*-- WorkerThread Method */ DWORD cIocpClient::WorkerThread(void) { DWORD bytesTransferred; ULONG_PTR completionKey; OVERLAPPED* overlapped; BOOL retValue; PerIoContext* perIoContext; 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 ) { // »ç¿ëÀÌ ¿Ï·áµÈ I/O Context´Â ȸ¼öÇÑ´Ù. perIoContext->offset = max( perIoContext->offset, bytesTransferred ); Close( completionKey, perIoContext ); continue; } switch ( perIoContext->requestType ) { case IOCP_REQUEST_READ: RecvComplete( completionKey, perIoContext, bytesTransferred ); break; // Receive ¿Ï·á Çڵ鷯 ÇÔ¼ö. case IOCP_REQUEST_WRITE: SendComplete( completionKey, perIoContext, bytesTransferred ); break; // Send ¿Ï·á Çڵ鷯 ÇÔ¼ö. case IOCP_REQUEST_CALLBACK: CallbackComplete( completionKey, perIoContext, bytesTransferred ); break; // Callback ¿Ï·á Çڵ鷯 ÇÔ¼ö. } } return 0; } // BackendThread Method DWORD cIocpClient::BackendThread( ) { while ( true ) { // ¼­¹öÁ¾·á, Thread¸¦ ³ª°£´Ù. if ( mEndClient == true ) break; Sleep( 50 ); } return 0; } /*-- WorkerThreadStartingPoint Method ( Worker Thread ½ÃÀÛ Æ÷ÀÎÆ® ) */ DWORD cIocpClient::WorkerThreadStartingPoint(void* ptr) { cIocpClient* iocpClient = (cIocpClient*)ptr; return iocpClient->WorkerThread( ); } // BackendThreadStartingPoint Method ( Backend Thread ½ÃÀÛ Æ÷ÀÎÆ® ) DWORD cIocpClient::BackendThreadStartingPoint(void* ptr) { cIocpClient* iocpClient = (cIocpClient*)ptr; return iocpClient->BackendThread( ); }