// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include #define new DEBUG_NEW #define _afxSockThreadState AfxGetModuleThreadState() #define _AFX_SOCK_THREAD_STATE AFX_MODULE_THREAD_STATE #ifndef _WIN32_WCE #pragma comment(lib, "wsock32.lib") #else #pragma comment(lib, "ws2.lib") #endif // !_WIN32_WCE ///////////////////////////////////////////////////////////////////////////// // socket state cleanup _AFX_SOCK_STATE::~_AFX_SOCK_STATE() { if (m_pfnSockTerm != NULL) m_pfnSockTerm(); } ///////////////////////////////////////////////////////////////////////////// // sockets globals and implementation helpers void AFXAPI AfxSocketTerm() { _AFX_SOCK_STATE* pState = _afxSockState.GetData(); if (pState->m_hInstSOCK != NULL) { if (pState->m_pfnSockTerm != NULL) WSACleanup(); #ifdef _WIN32_WCE // after WSACleanup, so that the sockets get their final events properly CAsyncSocket::CSocketInfo::Term(); #endif // _WIN32_WCE FreeLibrary(pState->m_hInstSOCK);// handle of WSOCK32.DLL pState->m_hInstSOCK = NULL; } } #ifndef _AFXDLL BOOL AFXAPI AfxSocketInit(WSADATA* lpwsaData) #else BOOL AFXAPI _AfxSocketInit(WSADATA* lpwsaData) #endif { _AFX_SOCK_STATE* pState = _afxSockState.GetData(); if (pState->m_pfnSockTerm == NULL) { // initialize Winsock library WSADATA wsaData; if (lpwsaData == NULL) lpwsaData = &wsaData; WORD wVersionRequested = MAKEWORD(1, 1); int nResult = WSAStartup(wVersionRequested, lpwsaData); if (nResult != 0) return FALSE; if (LOBYTE(lpwsaData->wVersion) != 1 || HIBYTE(lpwsaData->wVersion) != 1) { WSACleanup(); return FALSE; } #ifdef _WIN32_WCE if (CAsyncSocket::CSocketInfo::Init() != TRUE) { WSACleanup(); return FALSE; } #endif // _WIN32_WCE // setup for termination of sockets pState->m_pfnSockTerm = &AfxSocketTerm; } #ifndef _AFXDLL //BLOCK: setup maps and lists specific to socket state { _AFX_SOCK_THREAD_STATE* pThreadState = _afxSockThreadState; if (pThreadState->m_pmapSocketHandle == NULL) pThreadState->m_pmapSocketHandle = new CMapPtrToPtr; if (pThreadState->m_pmapDeadSockets == NULL) pThreadState->m_pmapDeadSockets = new CMapPtrToPtr; if (pThreadState->m_plistSocketNotifications == NULL) pThreadState->m_plistSocketNotifications = new CPtrList; } #endif return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CAsyncSocket Construction CAsyncSocket::CAsyncSocket() { m_hSocket = INVALID_SOCKET; #ifdef _WIN32_WCE m_lEvent = 0; #endif // _WIN32_WCE } BOOL CAsyncSocket::Create(UINT nSocketPort, int nSocketType, long lEvent, LPCTSTR lpszSocketAddress) { if (Socket(nSocketType, lEvent)) { if (Bind(nSocketPort,lpszSocketAddress)) return TRUE; int nResult = GetLastError(); Close(); WSASetLastError(nResult); } return FALSE; } ///////////////////////////////////////////////////////////////////////////// // CAsyncSocket Attributes BOOL CAsyncSocket::Attach(SOCKET hSocket, long lEvent) { ASSERT(hSocket != INVALID_SOCKET); if (hSocket == INVALID_SOCKET) { return FALSE; } m_hSocket = hSocket; CAsyncSocket::AttachHandle(hSocket, this); return AsyncSelect(lEvent); } SOCKET CAsyncSocket::Detach() { SOCKET hSocket = m_hSocket; if (AsyncSelect(0)) { CAsyncSocket::KillSocket(hSocket, this); m_hSocket = INVALID_SOCKET; return hSocket; } return INVALID_SOCKET; } BOOL CAsyncSocket::GetPeerName(CString& rPeerAddress, UINT& rPeerPort) { SOCKADDR_IN sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); int nSockAddrLen = sizeof(sockAddr); BOOL bResult = GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen); if (bResult) { rPeerPort = ntohs(sockAddr.sin_port); rPeerAddress = inet_ntoa(sockAddr.sin_addr); } return bResult; } BOOL CAsyncSocket::GetSockName(CString& rSocketAddress, UINT& rSocketPort) { SOCKADDR_IN sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); int nSockAddrLen = sizeof(sockAddr); BOOL bResult = GetSockName((SOCKADDR*)&sockAddr, &nSockAddrLen); if (bResult) { rSocketPort = ntohs(sockAddr.sin_port); rSocketAddress = inet_ntoa(sockAddr.sin_addr); } return bResult; } ///////////////////////////////////////////////////////////////////////////// // CAscynSocket Operations BOOL CAsyncSocket::Accept(CAsyncSocket& rConnectedSocket, SOCKADDR* lpSockAddr, int* lpSockAddrLen) { ASSERT(rConnectedSocket.m_hSocket == INVALID_SOCKET); ASSERT(CAsyncSocket::FromHandle(INVALID_SOCKET) == NULL); CAsyncSocket::AttachHandle(INVALID_SOCKET, &rConnectedSocket); if (CAsyncSocket::FromHandle(INVALID_SOCKET) == NULL) { // AttachHandle Call has failed return FALSE; } SOCKET hTemp = accept(m_hSocket, lpSockAddr, lpSockAddrLen); if (hTemp == INVALID_SOCKET) { DWORD dwProblem = GetLastError(); CAsyncSocket::DetachHandle(rConnectedSocket.m_hSocket, FALSE); rConnectedSocket.m_hSocket = INVALID_SOCKET; SetLastError(dwProblem); } else if (CAsyncSocket::FromHandle(INVALID_SOCKET) != NULL) { rConnectedSocket.m_hSocket = hTemp; CAsyncSocket::DetachHandle(INVALID_SOCKET, FALSE); CAsyncSocket::AttachHandle(hTemp, &rConnectedSocket); #ifdef _WIN32_WCE // The newly created socket from the call to accept is the socket that will // handle the actual connection; it has the same properties as socket m_hSocket, // including the asynchronous events registered with the WSAAsyncSelect or WSAEventSelect // functions. if (rConnectedSocket.AsyncSelect(m_lEvent) == FALSE) { return FALSE; } #endif // _WIN32_WCE } return (hTemp != INVALID_SOCKET); } BOOL CAsyncSocket::Bind(UINT nSocketPort, LPCTSTR lpszSocketAddress) { USES_CONVERSION_EX; SOCKADDR_IN sockAddr; memset(&sockAddr,0,sizeof(sockAddr)); LPSTR lpszAscii; if (lpszSocketAddress != NULL) { lpszAscii = T2A_EX((LPTSTR)lpszSocketAddress, _ATL_SAFE_ALLOCA_DEF_THRESHOLD); if (lpszAscii == NULL) { // OUT OF MEMORY WSASetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } } else { lpszAscii = NULL; } sockAddr.sin_family = AF_INET; if (lpszAscii == NULL) sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); else { DWORD lResult = inet_addr(lpszAscii); if (lResult == INADDR_NONE) { WSASetLastError(WSAEINVAL); return FALSE; } sockAddr.sin_addr.s_addr = lResult; } sockAddr.sin_port = htons((u_short)nSocketPort); return Bind((SOCKADDR*)&sockAddr, sizeof(sockAddr)); } void CAsyncSocket::Close() { if (m_hSocket != INVALID_SOCKET) { VERIFY(SOCKET_ERROR != closesocket(m_hSocket)); #ifdef _WIN32_WCE VERIFY(AsyncSelect(0)); #endif // !_WIN32_WCE CAsyncSocket::KillSocket(m_hSocket, this); m_hSocket = INVALID_SOCKET; } } BOOL CAsyncSocket::Connect(LPCTSTR lpszHostAddress, UINT nHostPort) { USES_CONVERSION_EX; ASSERT(lpszHostAddress != NULL); if (lpszHostAddress == NULL) { return FALSE; } SOCKADDR_IN sockAddr; memset(&sockAddr,0,sizeof(sockAddr)); LPSTR lpszAscii = T2A_EX((LPTSTR)lpszHostAddress, _ATL_SAFE_ALLOCA_DEF_THRESHOLD); if (lpszAscii == NULL) { WSASetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = inet_addr(lpszAscii); if (sockAddr.sin_addr.s_addr == INADDR_NONE) { LPHOSTENT lphost; lphost = gethostbyname(lpszAscii); if (lphost != NULL) sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; else { WSASetLastError(WSAEINVAL); return FALSE; } } sockAddr.sin_port = htons((u_short)nHostPort); return Connect((SOCKADDR*)&sockAddr, sizeof(sockAddr)); } int CAsyncSocket::Receive(void* lpBuf, int nBufLen, int nFlags) { return recv(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags); } int CAsyncSocket::ReceiveFrom(void* lpBuf, int nBufLen, CString& rSocketAddress, UINT& rSocketPort, int nFlags) { SOCKADDR_IN sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); int nSockAddrLen = sizeof(sockAddr); int nResult = ReceiveFrom(lpBuf, nBufLen, (SOCKADDR*)&sockAddr, &nSockAddrLen, nFlags); if(nResult != SOCKET_ERROR) { rSocketPort = ntohs(sockAddr.sin_port); rSocketAddress = inet_ntoa(sockAddr.sin_addr); } return nResult; } int CAsyncSocket::Send(const void* lpBuf, int nBufLen, int nFlags) { return send(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags); } int CAsyncSocket::SendTo(const void* lpBuf, int nBufLen, UINT nHostPort, LPCTSTR lpszHostAddress, int nFlags) { USES_CONVERSION_EX; SOCKADDR_IN sockAddr; memset(&sockAddr,0,sizeof(sockAddr)); LPSTR lpszAscii; if (lpszHostAddress != NULL) { lpszAscii = T2A_EX((LPTSTR)lpszHostAddress, _ATL_SAFE_ALLOCA_DEF_THRESHOLD); if (lpszAscii == NULL) { // OUT OF MEMORY WSASetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } } else { lpszAscii = NULL; } sockAddr.sin_family = AF_INET; if (lpszAscii == NULL) sockAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); else { sockAddr.sin_addr.s_addr = inet_addr(lpszAscii); if (sockAddr.sin_addr.s_addr == INADDR_NONE) { LPHOSTENT lphost; lphost = gethostbyname(lpszAscii); if (lphost != NULL) sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; else { WSASetLastError(WSAEINVAL); return SOCKET_ERROR; } } } sockAddr.sin_port = htons((u_short)nHostPort); return SendTo(lpBuf, nBufLen, (SOCKADDR*)&sockAddr, sizeof(sockAddr), nFlags); } BOOL CAsyncSocket::AsyncSelect(long lEvent) { ASSERT(m_hSocket != INVALID_SOCKET); _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; ASSERT(pState->m_hSocketWindow != NULL); #ifndef _WIN32_WCE return WSAAsyncSelect(m_hSocket, pState->m_hSocketWindow, WM_SOCKET_NOTIFY, lEvent) != SOCKET_ERROR; #else BOOL bSuccess = TRUE; EnterCriticalSection(&afxSocketInfo.m_sectData); TRY { WSAEVENT hEvent = (WSAEVENT)afxSocketInfo.m_pmapSocketEvent.GetValueAt((void*)m_hSocket); if (hEvent != NULL) { WSACloseEvent(hEvent); hEvent = NULL; } if ((m_lEvent = lEvent) != 0) { // based on MSDN, issuing a WSAAsyncSelect for a socket cancels any previous WSAAsyncSelect // or WSAEventSelect for the same socket...but even the most recent WSAAsyncSelect was failed? afxSocketInfo.m_pmapSocketEvent.RemoveKey((void*)m_hSocket); // create the event object to be used with this socket if ((hEvent = WSACreateEvent()) != WSA_INVALID_EVENT) { if (WSAEventSelect(m_hSocket, hEvent, lEvent) != SOCKET_ERROR) { afxSocketInfo.m_pmapSocketEvent.SetAt((void*)m_hSocket, (void*)hEvent); } else { WSACloseEvent(hEvent); bSuccess = FALSE; } } else { bSuccess = FALSE; } } else { afxSocketInfo.m_pmapSocketEvent.RemoveKey((void*)m_hSocket); } if (bSuccess) { // handle to the notification window afxSocketInfo.m_pmapSocketWindow.SetAt((void*)m_hSocket, (void*)pState->m_hSocketWindow); if (afxSocketInfo.m_pWorkerThread == NULL) { if (hEvent != NULL) { EnterCriticalSection(&afxSocketInfo.m_sectSocketTerm); TRY { if (afxSocketInfo.m_bSocketInitialized == TRUE) { afxSocketInfo.m_pWorkerThread = AfxBeginThread(CSocketInfo::WorkerThreadProc, (LPVOID)NULL, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL); if (afxSocketInfo.m_pWorkerThread != NULL) { ResetEvent(afxSocketInfo.m_hThreadTerminatedEvent); afxSocketInfo.m_pWorkerThread->ResumeThread(); } else { bSuccess = FALSE; } } else { TRACE(traceSocket, 0, _T("Error: Socket has been terminated.\n")); bSuccess = FALSE; } } CATCH_ALL(e) { LeaveCriticalSection(&afxSocketInfo.m_sectSocketTerm); THROW_LAST(); } END_CATCH_ALL LeaveCriticalSection(&afxSocketInfo.m_sectSocketTerm); } } // let the worker thread know about this change... SetEvent(afxSocketInfo.m_hMapUpdatedEvent); } } CATCH_ALL(e) { LeaveCriticalSection(&afxSocketInfo.m_sectData); THROW_LAST(); } END_CATCH_ALL LeaveCriticalSection(&afxSocketInfo.m_sectData); return bSuccess; #endif // !_WIN32_WCE } ///////////////////////////////////////////////////////////////////////////// // CAsyncSocket Overridable callbacks void CAsyncSocket::OnReceive(int /*nErrorCode*/) { } void CAsyncSocket::OnSend(int /*nErrorCode*/) { } void CAsyncSocket::OnOutOfBandData(int /*nErrorCode*/) { } void CAsyncSocket::OnAccept(int /*nErrorCode*/) { } void CAsyncSocket::OnConnect(int /*nErrorCode*/) { } void CAsyncSocket::OnClose(int /*nErrorCode*/) { } ///////////////////////////////////////////////////////////////////////////// // CAsyncSocket Implementation CAsyncSocket::~CAsyncSocket() { if (m_hSocket != INVALID_SOCKET) Close(); } CAsyncSocket* PASCAL CAsyncSocket::LookupHandle(SOCKET hSocket, BOOL bDead) { CAsyncSocket* pSocket; _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; if (!bDead) { pSocket = (CAsyncSocket*) pState->m_pmapSocketHandle->GetValueAt((void*)hSocket); if (pSocket != NULL) return pSocket; } else { pSocket = (CAsyncSocket*) pState->m_pmapDeadSockets->GetValueAt((void*)hSocket); if (pSocket != NULL) return pSocket; } return NULL; } void PASCAL CAsyncSocket::AttachHandle( SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead) { _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; BOOL bEnable = AfxEnableMemoryTracking(FALSE); if (!bDead) { ASSERT(CAsyncSocket::LookupHandle(hSocket, bDead) == NULL); if (pState->m_pmapSocketHandle->IsEmpty()) { ASSERT(pState->m_pmapDeadSockets->IsEmpty()); ASSERT(pState->m_hSocketWindow == NULL); CSocketWnd* pWnd = new CSocketWnd; pWnd->m_hWnd = NULL; if (!pWnd->CreateEx(0, AfxRegisterWndClass(0), _T("Socket Notification Sink"), WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL)) { TRACE(traceSocket, 0, _T("Warning: unable to create socket notify window!\n")); AfxThrowResourceException(); } ASSERT(pWnd->m_hWnd != NULL); ASSERT(CWnd::FromHandlePermanent(pWnd->m_hWnd) == pWnd); pState->m_hSocketWindow = pWnd->m_hWnd; } pState->m_pmapSocketHandle->SetAt((void*)hSocket, pSocket); } else { void* pvCount; INT_PTR nCount; if (pState->m_pmapDeadSockets->Lookup((void*)hSocket, pvCount)) { nCount = (INT_PTR)pvCount; nCount++; } else nCount = 1; pState->m_pmapDeadSockets->SetAt((void*)hSocket, (void*)nCount); } AfxEnableMemoryTracking(bEnable); } void PASCAL CAsyncSocket::DetachHandle(SOCKET hSocket, BOOL bDead) { ASSERT(CAsyncSocket::LookupHandle(hSocket, bDead) != NULL); _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; if (!bDead) { pState->m_pmapSocketHandle->RemoveKey((void*)hSocket); if (pState->m_pmapSocketHandle->IsEmpty()) { ASSERT(pState->m_hSocketWindow != NULL); CWnd* pWnd = CWnd::FromHandlePermanent(pState->m_hSocketWindow); ASSERT_VALID(pWnd); if (pWnd != NULL) { pWnd->DestroyWindow(); delete pWnd; } pState->m_hSocketWindow = NULL; pState->m_pmapDeadSockets->RemoveAll(); while (!pState->m_plistSocketNotifications->IsEmpty()) delete pState->m_plistSocketNotifications->RemoveHead(); } } else { void* pvCount; INT_PTR nCount; if (pState->m_pmapDeadSockets->Lookup((void*)hSocket, pvCount)) { nCount = (INT_PTR)pvCount; nCount--; if (nCount == 0) pState->m_pmapDeadSockets->RemoveKey((void*)hSocket); else pState->m_pmapDeadSockets->SetAt((void*)hSocket, (void*)nCount); } } } void PASCAL CAsyncSocket::KillSocket(SOCKET hSocket, CAsyncSocket* pSocket) { ASSERT(CAsyncSocket::LookupHandle(hSocket, FALSE) != NULL); _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; CAsyncSocket::DetachHandle(hSocket, FALSE); if (pState->m_hSocketWindow != NULL) { ::PostMessage(pState->m_hSocketWindow, WM_SOCKET_DEAD, (WPARAM)hSocket, 0L); CAsyncSocket::AttachHandle(hSocket, pSocket, TRUE); } } void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam) { if (wParam == 0 && lParam == 0) return; // Has the socket be closed - lookup in dead handle list CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE); // If yes ignore message if (pSocket != NULL) return; pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, FALSE); if (pSocket == NULL) { // Must be in the middle of an Accept call pSocket = CAsyncSocket::LookupHandle(INVALID_SOCKET, FALSE); ENSURE(pSocket != NULL); if(pSocket == NULL) return; pSocket->m_hSocket = (SOCKET)wParam; CAsyncSocket::DetachHandle(INVALID_SOCKET, FALSE); CAsyncSocket::AttachHandle(pSocket->m_hSocket, pSocket, FALSE); } int nErrorCode = WSAGETSELECTERROR(lParam); switch (WSAGETSELECTEVENT(lParam)) { case FD_READ: { fd_set fds; int nReady; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&fds); FD_SET(pSocket->m_hSocket, &fds); nReady = select(0, &fds, NULL, NULL, &timeout); if (nReady == SOCKET_ERROR) nErrorCode = WSAGetLastError(); if ((nReady == 1) || (nErrorCode != 0)) pSocket->OnReceive(nErrorCode); } break; case FD_WRITE: pSocket->OnSend(nErrorCode); break; case FD_OOB: pSocket->OnOutOfBandData(nErrorCode); break; case FD_ACCEPT: pSocket->OnAccept(nErrorCode); break; case FD_CONNECT: pSocket->OnConnect(nErrorCode); break; case FD_CLOSE: pSocket->OnClose(nErrorCode); break; } } BOOL CAsyncSocket::Socket(int nSocketType, long lEvent, int nProtocolType, int nAddressFormat) { ASSERT(m_hSocket == INVALID_SOCKET); m_hSocket = socket(nAddressFormat,nSocketType,nProtocolType); if (m_hSocket != INVALID_SOCKET) { CAsyncSocket::AttachHandle(m_hSocket, this, FALSE); return AsyncSelect(lEvent); } return FALSE; } #ifdef _DEBUG void CAsyncSocket::AssertValid() const { CObject::AssertValid(); ASSERT(m_hSocket == INVALID_SOCKET || CAsyncSocket::FromHandle(m_hSocket) != NULL); } #ifndef _WIN32_WCE void CAsyncSocket::Dump(CDumpContext& dc) const { CObject::Dump(dc); dc << "m_hSocket = "; if (m_hSocket == INVALID_SOCKET) dc << "INVALID_SOCKET\n"; else dc << (void*)m_hSocket << "\n"; } #endif // !_WIN32_WCE #endif //_DEBUG int CAsyncSocket::ReceiveFromHelper(void* lpBuf, int nBufLen, SOCKADDR* lpSockAddr, int* lpSockAddrLen, int nFlags) { return recvfrom(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags, lpSockAddr, lpSockAddrLen); } int CAsyncSocket::SendToHelper(const void* lpBuf, int nBufLen, const SOCKADDR* lpSockAddr, int nSockAddrLen, int nFlags) { return sendto(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags, lpSockAddr, nSockAddrLen); } BOOL CAsyncSocket::ConnectHelper(const SOCKADDR* lpSockAddr, int nSockAddrLen) { return connect(m_hSocket, lpSockAddr, nSockAddrLen) != SOCKET_ERROR; } ///////////////////////////////////////////////////////////////////////////// // CSocket Construction CSocket::CSocket() { m_pbBlocking = NULL; m_nConnectError = -1; m_nTimeOut = 2000; } ///////////////////////////////////////////////////////////////////////////// // CSocket Operations void CSocket::CancelBlockingCall() { if (m_pbBlocking != NULL) { *m_pbBlocking = FALSE; m_pbBlocking = NULL; } } ///////////////////////////////////////////////////////////////////////////// // CSocket Overridable callbacks BOOL CSocket::OnMessagePending() { MSG msg; if (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE)) { ::DispatchMessage(&msg); return FALSE; // usually return TRUE, but OnIdle usually causes WM_PAINTs } return FALSE; } ///////////////////////////////////////////////////////////////////////////// // CSocket Implementation CSocket::~CSocket() { if (m_hSocket != INVALID_SOCKET) Close(); } BOOL CSocket::Accept(CAsyncSocket& rConnectedSocket, SOCKADDR* lpSockAddr, int* lpSockAddrLen) { if (m_pbBlocking != NULL) { WSASetLastError(WSAEINPROGRESS); return FALSE; } while (!CAsyncSocket::Accept(rConnectedSocket, lpSockAddr, lpSockAddrLen)) { if (GetLastError() == WSAEWOULDBLOCK) { if (!PumpMessages(FD_ACCEPT)) return FALSE; } else return FALSE; } return TRUE; } void CSocket::Close() { if (m_hSocket != INVALID_SOCKET) { CancelBlockingCall(); VERIFY(AsyncSelect(0)); CAsyncSocket::Close(); m_hSocket = INVALID_SOCKET; } } int CSocket::Receive(void* lpBuf, int nBufLen, int nFlags) { if (m_pbBlocking != NULL) { WSASetLastError(WSAEINPROGRESS); return FALSE; } int nResult; while ((nResult = CAsyncSocket::Receive(lpBuf, nBufLen, nFlags)) == SOCKET_ERROR) { if (GetLastError() == WSAEWOULDBLOCK) { if (!PumpMessages(FD_READ)) return SOCKET_ERROR; } else return SOCKET_ERROR; } return nResult; } int CSocket::Send(const void* lpBuf, int nBufLen, int nFlags) { if (m_pbBlocking != NULL) { WSASetLastError(WSAEINPROGRESS); return FALSE; } int nLeft, nWritten; PBYTE pBuf = (PBYTE)lpBuf; nLeft = nBufLen; while (nLeft > 0) { nWritten = SendChunk(pBuf, nLeft, nFlags); if (nWritten == SOCKET_ERROR) return nWritten; nLeft -= nWritten; pBuf += nWritten; } return nBufLen - nLeft; } int CSocket::SendChunk(const void* lpBuf, int nBufLen, int nFlags) { int nResult; while ((nResult = CAsyncSocket::Send(lpBuf, nBufLen, nFlags)) == SOCKET_ERROR) { if (GetLastError() == WSAEWOULDBLOCK) { if (!PumpMessages(FD_WRITE)) return SOCKET_ERROR; } else return SOCKET_ERROR; } return nResult; } BOOL CSocket::ConnectHelper(const SOCKADDR* lpSockAddr, int nSockAddrLen) { if (m_pbBlocking != NULL) { WSASetLastError(WSAEINPROGRESS); return FALSE; } m_nConnectError = -1; if (!CAsyncSocket::ConnectHelper(lpSockAddr, nSockAddrLen)) { if (GetLastError() == WSAEWOULDBLOCK) { while (PumpMessages(FD_CONNECT)) { if (m_nConnectError != -1) { WSASetLastError(m_nConnectError); return (m_nConnectError == 0); } } } return FALSE; } return TRUE; } int CSocket::ReceiveFromHelper(void* lpBuf, int nBufLen, SOCKADDR* lpSockAddr, int* lpSockAddrLen, int nFlags) { if (m_pbBlocking != NULL) { WSASetLastError(WSAEINPROGRESS); return FALSE; } int nResult; while ((nResult = CAsyncSocket::ReceiveFromHelper(lpBuf, nBufLen, lpSockAddr, lpSockAddrLen, nFlags)) == SOCKET_ERROR) { if (GetLastError() == WSAEWOULDBLOCK) { if (!PumpMessages(FD_READ)) return SOCKET_ERROR; } else return SOCKET_ERROR; } return nResult; } int CSocket::SendToHelper(const void* lpBuf, int nBufLen, const SOCKADDR* lpSockAddr, int nSockAddrLen, int nFlags) { if (m_pbBlocking != NULL) { WSASetLastError(WSAEINPROGRESS); return SOCKET_ERROR; } int nResult; while ((nResult = CAsyncSocket::SendToHelper(lpBuf, nBufLen, lpSockAddr, nSockAddrLen, nFlags)) == SOCKET_ERROR) { if (GetLastError() == WSAEWOULDBLOCK) { if (!PumpMessages(FD_WRITE)) return SOCKET_ERROR; } else return SOCKET_ERROR; } return nResult; } int PASCAL CSocket::ProcessAuxQueue() { _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; if (pState->m_plistSocketNotifications->IsEmpty()) return 0; int nCount = 0; while(!pState->m_plistSocketNotifications->IsEmpty()) { nCount++; MSG* pMsg = (MSG*)pState->m_plistSocketNotifications->RemoveHead(); ASSERT(pMsg != NULL); if (pMsg->message == WM_SOCKET_NOTIFY) { CAsyncSocket::DoCallBack(pMsg->wParam, pMsg->lParam); } else { ASSERT(CAsyncSocket::LookupHandle((SOCKET)pMsg->wParam, TRUE) != NULL); CAsyncSocket::DetachHandle((SOCKET)pMsg->wParam, TRUE); } delete pMsg; } return nCount; } void PASCAL CSocket::AuxQueueAdd(UINT message, WPARAM wParam, LPARAM lParam) { _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; MSG* pMsg = new MSG; pMsg->message = message; pMsg->wParam = wParam; pMsg->lParam = lParam; pState->m_plistSocketNotifications->AddTail(pMsg); } BOOL CSocket::PumpMessages(UINT uStopFlag) { // The same socket better not be blocking in more than one place. ASSERT(m_pbBlocking == NULL); _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; ASSERT(pState->m_hSocketWindow != NULL); BOOL bBlocking = TRUE; m_pbBlocking = &bBlocking; CWinThread* pThread = AfxGetThread(); // This is not a timeout in the WinSock sense, but more // like a WM_KICKIDLE to keep message pumping alive UINT_PTR nTimerID = ::SetTimer(pState->m_hSocketWindow, 1, m_nTimeOut, NULL); if (nTimerID == 0) AfxThrowResourceException(); BOOL bPeek = TRUE; while (bBlocking) { TRY { MSG msg; if (::PeekMessage(&msg, pState->m_hSocketWindow, WM_SOCKET_NOTIFY, WM_SOCKET_DEAD, PM_REMOVE)) { if (msg.message == WM_SOCKET_NOTIFY && (SOCKET)msg.wParam == m_hSocket) { if (WSAGETSELECTEVENT(msg.lParam) == FD_CLOSE) { break; } if (WSAGETSELECTEVENT(msg.lParam) == uStopFlag) { if (uStopFlag == FD_CONNECT) m_nConnectError = WSAGETSELECTERROR(msg.lParam); break; } } #ifdef _WIN32_WCE else if (msg.message == WM_SOCKET_DEAD) { CSocketInfo::OnSocketDead((SOCKET)msg.wParam); } #endif // _WIN32_WCE if (msg.wParam != 0 || msg.lParam != 0) CSocket::AuxQueueAdd(msg.message, msg.wParam, msg.lParam); bPeek = TRUE; } else if (::PeekMessage(&msg, pState->m_hSocketWindow, WM_TIMER, WM_TIMER, PM_REMOVE)) { break; } if (bPeek && ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (OnMessagePending()) { // allow user-interface updates ASSERT(pThread); pThread->OnIdle(-1); } else { bPeek = FALSE; } } else { // no work to do -- allow CPU to sleep #ifndef _WIN32_WCE WaitMessage(); #else while (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) Sleep(100); #endif // !_WIN32_WCE bPeek = TRUE; } } CATCH_ALL(e) { TRACE(traceSocket, 0, _T("Error: caught exception in PumpMessage - continuing.\n")); DELETE_EXCEPTION(e); bPeek = TRUE; } END_CATCH_ALL } ::KillTimer(pState->m_hSocketWindow, nTimerID); if (!bBlocking) { WSASetLastError(WSAEINTR); return FALSE; } m_pbBlocking = NULL; ::PostMessage(pState->m_hSocketWindow, WM_SOCKET_NOTIFY, 0, 0); return TRUE; } #ifdef _DEBUG void CSocket::AssertValid() const { CAsyncSocket::AssertValid(); } #ifndef _WIN32_WCE void CSocket::Dump(CDumpContext& dc) const { CAsyncSocket::Dump(dc); dc << "m_pbBlocking = " << m_pbBlocking <<"\n"; dc << "m_nConnectError = " << m_nConnectError <<"\n"; } #endif // !_WIN32_WCE #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CSocketFile Construction CSocketFile::CSocketFile(CSocket* pSocket, BOOL bArchiveCompatible) { m_pSocket = pSocket; m_bArchiveCompatible = bArchiveCompatible; #ifdef _DEBUG ASSERT(m_pSocket != NULL); ASSERT(m_pSocket->m_hSocket != INVALID_SOCKET); int nType = 0; int nTypeLen = sizeof(int); ASSERT(m_pSocket->GetSockOpt(SO_TYPE,&nType,&nTypeLen)); ASSERT(nType == SOCK_STREAM); #endif // _DEBUG if (pSocket == NULL) { AfxThrowInvalidArgException(); } } ///////////////////////////////////////////////////////////////////////////// // CSocketFile Implementation CSocketFile::~CSocketFile() { } UINT CSocketFile::Read(void* lpBuf, UINT nCount) { ENSURE(m_pSocket != NULL); if (lpBuf == NULL) { AfxThrowInvalidArgException(); } int nRead; if (!m_bArchiveCompatible) { int nLeft = nCount; PBYTE pBuf = (PBYTE)lpBuf; while(nLeft > 0) { nRead = m_pSocket->Receive(pBuf, nLeft); if (nRead == SOCKET_ERROR) { int nError = m_pSocket->GetLastError(); AfxThrowFileException(CFileException::genericException, nError); } else if (nRead == 0) { return nCount - nLeft; } nLeft -= nRead; pBuf += nRead; } return nCount - nLeft; } nRead = m_pSocket->Receive(lpBuf, nCount, 0); if (nRead == SOCKET_ERROR) { int nError = m_pSocket->GetLastError(); AfxThrowFileException(CFileException::genericException, nError); } return nRead; } void CSocketFile::Write(const void* lpBuf, UINT nCount) { ENSURE(m_pSocket!=NULL); if (lpBuf == NULL) { AfxThrowInvalidArgException(); } int nWritten = m_pSocket->Send(lpBuf, nCount); if (nWritten == SOCKET_ERROR) { int nError = m_pSocket->GetLastError(); AfxThrowFileException(CFileException::genericException, nError); } } void CSocketFile::Close() { m_pSocket = NULL; } BOOL CSocketFile::Open( LPCTSTR /*lpszFileName*/, UINT /*nOpenFlags*/, CFileException* /*pError*/) { AfxThrowNotSupportedException(); } CFile* CSocketFile::Duplicate() const { AfxThrowNotSupportedException(); } ULONGLONG CSocketFile::GetPosition() const { AfxThrowNotSupportedException(); } ULONGLONG CSocketFile::Seek(LONGLONG lOff, UINT nFrom) { if (lOff != 0L || nFrom != current) TRACE(traceSocket, 0, _T("Warning - Attempt made to seek on a CSocketFile\n")); return 0; } void CSocketFile::SetLength(ULONGLONG /*dwNewLen*/) { AfxThrowNotSupportedException(); } ULONGLONG CSocketFile::GetLength() const { AfxThrowNotSupportedException(); } void CSocketFile::LockRange(ULONGLONG /*dwPos*/, ULONGLONG /*dwCount*/) { AfxThrowNotSupportedException(); } void CSocketFile::UnlockRange(ULONGLONG /*dwPos*/, ULONGLONG /*dwCount*/) { AfxThrowNotSupportedException(); } void CSocketFile::Flush() { } void CSocketFile::Abort() { AfxThrowNotSupportedException(); } UINT CSocketFile::GetBufferPtr(UINT nCommand, UINT /*nCount*/, void** /*ppBufStart*/, void** /*ppBufMax*/) { ASSERT(nCommand == bufferCheck); UNUSED(nCommand); return bufferBlocking; } #ifdef _DEBUG void CSocketFile::AssertValid() const { CFile::AssertValid(); if (m_pSocket != NULL) ASSERT_VALID(m_pSocket); } #ifndef _WIN32_WCE void CSocketFile::Dump(CDumpContext& dc) const { CFile::Dump(dc); if (dc.GetDepth() > 0) { if (m_pSocket != NULL) dc << "with no socket\n"; else dc << "with socket: " << m_pSocket; } } #endif // !_WIN32_WCE #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CSocketWnd implementation CSocketWnd::CSocketWnd() { } LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam) { CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam); CSocket::ProcessAuxQueue(); return 0L; } LRESULT CSocketWnd::OnSocketDead(WPARAM wParam, LPARAM lParam) { CSocket::AuxQueueAdd(WM_SOCKET_DEAD, wParam, lParam); CSocket::ProcessAuxQueue(); return 0L; } ///////////////////////////////////////////////////////////////////////////// // Message table implementation BEGIN_MESSAGE_MAP(CSocketWnd, CWnd) //{{AFX_MSG_MAP(CWnd) ON_MESSAGE(WM_SOCKET_NOTIFY, &CSocketWnd::OnSocketNotify) ON_MESSAGE(WM_SOCKET_DEAD, &CSocketWnd::OnSocketDead) //}}AFX_MSG_MAP END_MESSAGE_MAP() #ifdef _WIN32_WCE ///////////////////////////////////////////////////////////////////////////// // CAsyncCSocketInfo CAsyncSocket::CSocketInfo CAsyncSocket::afxSocketInfo; BOOL CAsyncSocket::CSocketInfo::Init() { // synchronization objects CAsyncSocket::afxSocketInfo.m_hMapUpdatedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); CAsyncSocket::afxSocketInfo.m_hThreadTerminatedEvent = CreateEvent(NULL, FALSE, TRUE, NULL); CAsyncSocket::afxSocketInfo.m_hSocketTerminatedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (CAsyncSocket::afxSocketInfo.m_hMapUpdatedEvent == NULL || CAsyncSocket::afxSocketInfo.m_hThreadTerminatedEvent == NULL || CAsyncSocket::afxSocketInfo.m_hSocketTerminatedEvent == NULL) { TRACE(traceSocket, 0, _T("Warning: CreateEvent failed in CAsyncSocket::CSocketInfo::Init.\n")); if (CAsyncSocket::afxSocketInfo.m_hMapUpdatedEvent != NULL) { CloseHandle(CAsyncSocket::afxSocketInfo.m_hMapUpdatedEvent); } if (CAsyncSocket::afxSocketInfo.m_hThreadTerminatedEvent != NULL) { CloseHandle(CAsyncSocket::afxSocketInfo.m_hThreadTerminatedEvent); } if (CAsyncSocket::afxSocketInfo.m_hSocketTerminatedEvent != NULL) { CloseHandle(CAsyncSocket::afxSocketInfo.m_hSocketTerminatedEvent); } return FALSE; } CAsyncSocket::afxSocketInfo.m_bSocketInitialized = TRUE; CAsyncSocket::afxSocketInfo.m_pWorkerThread = NULL; return TRUE; } void CAsyncSocket::CSocketInfo::Term() { EnterCriticalSection(&CAsyncSocket::afxSocketInfo.m_sectSocketTerm); // signal the termination of socket SetEvent(CAsyncSocket::afxSocketInfo.m_hSocketTerminatedEvent); DWORD dwResult = ::WaitForSingleObject(CAsyncSocket::afxSocketInfo.m_hThreadTerminatedEvent, WSA_INFINITE); if (dwResult == WAIT_OBJECT_0) { // assume all the socket have been closed ASSERT(CAsyncSocket::afxSocketInfo.m_pmapSocketEvent.GetSize() == 0); ASSERT(CAsyncSocket::afxSocketInfo.m_pWorkerThread == NULL); // clean up CloseHandle(CAsyncSocket::afxSocketInfo.m_hMapUpdatedEvent); CloseHandle(CAsyncSocket::afxSocketInfo.m_hThreadTerminatedEvent); CloseHandle(CAsyncSocket::afxSocketInfo.m_hSocketTerminatedEvent); CAsyncSocket::afxSocketInfo.m_bSocketInitialized = FALSE; } LeaveCriticalSection(&CAsyncSocket::afxSocketInfo.m_sectSocketTerm); } void CAsyncSocket::CSocketInfo::OnSocketDead(SOCKET hSocket) { EnterCriticalSection(&afxSocketInfo.m_sectData); // close the event handle WSAEVENT hEvent = (WSAEVENT)afxSocketInfo.m_pmapSocketEvent.GetValueAt((void*)hSocket); ASSERT(hEvent != NULL); WSACloseEvent(hEvent); // we don't need to service this socket anymore afxSocketInfo.m_pmapSocketEvent.RemoveKey((void*)hSocket); // let the worker thread know about this change... SetEvent(afxSocketInfo.m_hMapUpdatedEvent); LeaveCriticalSection(&afxSocketInfo.m_sectData); } UINT CAsyncSocket::CSocketInfo::WorkerThreadProc(LPVOID /*param*/) { CMapPtrToPtr pmapSocketEvent; WSAEVENT* hEvents = NULL; DWORD dwSocketEvents = 0; DWORD dwTotalEvents = 2; const LONG sleepThreshold = 3000; LONG sleepInterval = 100; // we start from the two events... hEvents = new WSAEVENT[dwTotalEvents]; if (hEvents != NULL) { hEvents[0] = afxSocketInfo.m_hSocketTerminatedEvent; hEvents[1] = afxSocketInfo.m_hMapUpdatedEvent; } else { TRACE(traceSocket, 0, _T("Error: failed allocating memory for events array.\n")); return 0; } // start the while loop to wait on, enumerate and dispatch events while (TRUE) { // discover occurrences of network event for each socket WSAEVENT hEvent; SOCKET hSocket; DWORD dwResult = ::WaitForMultipleObjects(dwTotalEvents, hEvents, FALSE, WSA_INFINITE); if (dwResult == WAIT_OBJECT_0) /* m_hSocketTerminatedEvent */ { break; } else if (dwResult == WAIT_OBJECT_0 + 1) /* m_hMapUpdatedEvent */ { POSITION pos; EnterCriticalSection(&afxSocketInfo.m_sectData); TRY { pmapSocketEvent.RemoveAll(); pos = afxSocketInfo.m_pmapSocketEvent.GetStartPosition(); while (pos) { afxSocketInfo.m_pmapSocketEvent.GetNextAssoc(pos, (void*&)hSocket, (void*)hEvent); ASSERT(hSocket != NULL && hEvent != NULL); // note: the key for this map is WSAEVENT handle pmapSocketEvent.SetAt((void*)hEvent, (void*)hSocket); } // When there is no more event to wait for, we need to terminate this thread if ((dwSocketEvents = pmapSocketEvent.GetSize()) <= 0) { LeaveCriticalSection(&afxSocketInfo.m_sectData); break; } // get back the memory allocated if (hEvents != NULL) { delete[] hEvents; } dwTotalEvents = dwSocketEvents + 2; hEvents = new WSAEVENT[dwTotalEvents]; if (hEvents != NULL) { hEvents[0] = afxSocketInfo.m_hSocketTerminatedEvent; hEvents[1] = afxSocketInfo.m_hMapUpdatedEvent; DWORD dwIndex = 2; pos = pmapSocketEvent.GetStartPosition(); while (pos && (dwIndex < dwTotalEvents)) { pmapSocketEvent.GetNextAssoc(pos, (void*)hEvent, (void*&)hSocket); ASSERT(hEvent != NULL && hSocket != NULL); hEvents[dwIndex++] = hEvent; } } } CATCH_ALL(e) { DELETE_EXCEPTION(e); LeaveCriticalSection(&afxSocketInfo.m_sectData); // is the socket going to be terminated? if (WAIT_OBJECT_0 == WaitForSingleObject(afxSocketInfo.m_hSocketTerminatedEvent, 0)) { break; } Sleep(sleepInterval); if (sleepInterval < sleepThreshold) { sleepInterval *= 2; } continue; } END_CATCH_ALL // if we can successfully allocate memory needed, then we reset the sleepInterval sleepInterval = 100; LeaveCriticalSection(&afxSocketInfo.m_sectData); } else if (dwResult >= (WAIT_OBJECT_0 + 2) && dwResult < (WAIT_OBJECT_0 + dwTotalEvents)) { hEvent = hEvents[dwResult - WAIT_OBJECT_0]; hSocket = (SOCKET)pmapSocketEvent.GetValueAt((void*)hEvent); WSANETWORKEVENTS events; dwResult = WSAEnumNetworkEvents(hSocket, hEvent, &events); if (dwResult == 0) { HWND hSocketWindow = NULL; EnterCriticalSection(&afxSocketInfo.m_sectData); hSocketWindow = (HWND)afxSocketInfo.m_pmapSocketWindow.GetValueAt((void*)hSocket); LeaveCriticalSection(&afxSocketInfo.m_sectData); // we don't want to send out this message if the socket had dead or the handle to the target // socket Window was NULL if (hSocketWindow == NULL) { continue; } // Handle events - FD_READ, FD_WRITE, FD_OOB, FD_ACCEPT, FD_CONNECT, FD_CLOSE int iErrorCode = 0; if (events.lNetworkEvents & FD_READ) { iErrorCode = events.iErrorCode[FD_READ_BIT]; ::PostMessage(hSocketWindow, WM_SOCKET_NOTIFY, (WPARAM)(SOCKET)hSocket, (LPARAM)FD_READ); } if (events.lNetworkEvents & FD_WRITE) { iErrorCode = events.iErrorCode[FD_WRITE_BIT]; ::PostMessage(hSocketWindow, WM_SOCKET_NOTIFY, (WPARAM)(SOCKET)hSocket, (LPARAM)FD_WRITE); } if (events.lNetworkEvents & FD_OOB) { iErrorCode = events.iErrorCode[FD_OOB_BIT]; ::PostMessage(hSocketWindow, WM_SOCKET_NOTIFY, (WPARAM)(SOCKET)hSocket, (LPARAM)FD_OOB); } if (events.lNetworkEvents & FD_ACCEPT) { iErrorCode = events.iErrorCode[FD_ACCEPT_BIT]; ::PostMessage(hSocketWindow, WM_SOCKET_NOTIFY, (WPARAM)(SOCKET)hSocket, (LPARAM)FD_ACCEPT); } if (events.lNetworkEvents & FD_CONNECT) { iErrorCode = events.iErrorCode[FD_CONNECT_BIT]; ::PostMessage(hSocketWindow, WM_SOCKET_NOTIFY, (WPARAM)(SOCKET)hSocket, (LPARAM)FD_CONNECT); } if (events.lNetworkEvents & FD_CLOSE) { iErrorCode = events.iErrorCode[FD_CLOSE_BIT]; ::PostMessage(hSocketWindow, WM_SOCKET_NOTIFY, (WPARAM)(SOCKET)hSocket, (LPARAM)FD_CLOSE); } } } else { TRACE(traceSocket, 0, _T("Error: failed on waiting multiple events.\n")); } } EnterCriticalSection(&afxSocketInfo.m_sectData); afxSocketInfo.m_pWorkerThread = NULL; SetEvent(afxSocketInfo.m_hThreadTerminatedEvent); LeaveCriticalSection(&afxSocketInfo.m_sectData); // clean up if (hEvents) { delete[] hEvents; } return 0; } #endif // _WIN32_WCE ////////////////////////////////////////////////////////////////////////////// // Inline function declarations expanded out-of-line #ifndef _AFX_ENABLE_INLINES #define _AFXSOCK_INLINE #include "afxsock.inl" #undef _AFXSOCK_INLINE #endif IMPLEMENT_DYNAMIC(CAsyncSocket, CObject) IMPLEMENT_DYNAMIC(CSocket, CAsyncSocket) IMPLEMENT_DYNAMIC(CSocketFile, CFile) #pragma warning(disable: 4074) #pragma init_seg(lib) PROCESS_LOCAL(_AFX_SOCK_STATE, _afxSockState) /////////////////////////////////////////////////////////////////////////////