www.pudn.com > NetPaw.rar > IocpSocket.cpp


/* 
 SocketNT.cpp: implementation of the CIocpSocket class 
 
 Written by Robert Simpson (robert@blackcastlesoft.com) 
 Created 5/11/2004 
 Version 1.01 -- Last Modified 6/10/2004 
 
 1.01 
   - Made the class compile clean at level 4 
 
 1.0 
 Initial release 
*/ 
////////////////////////////////////////////////////////////////////// 
 
#include "StdAfx.h" 
#include "iocpsocket.h" 
#include ".\iocpsocket.h" 
 
// Overridables 
 
/* Default OnAccept event creates a new wrapper around the socket and exits, 
   thereby ensuring the socket is properly disposed of since nobody cared 
   that we got it. */ 
void CIocpSocket::OnAccept(BOOL bSuccess, SOCKET s, CEventCallback* /*pc*/) 
{ 
	if (bSuccess) 
		CIocpSocket sockNew(*this, s); 
} 
 
void CIocpSocket::OnReceive(BOOL /*bSuccess*/, LPWSABUF /*pBuf*/, DWORD /*dwBuffers*/, DWORD /*dwBytesRecvd*/, DWORD /*dwFlags*/, CEventCallback * /*pc*/) 
{ 
} 
 
void CIocpSocket::OnReceiveFrom(BOOL /*bSuccess*/, LPWSABUF /*pBuf*/, DWORD /*dwBuffers*/, DWORD /*dwBytesRecvd*/, DWORD /*dwFlags*/, LPSOCKADDR /*pAddrFrom*/, INT /*nFromLen*/, CEventCallback * /*pc*/) 
{ 
} 
 
void CIocpSocket::OnSend(BOOL /*bSuccess*/, LPWSABUF /*pBuf*/, DWORD /*dwBuffers*/, DWORD /*dwBytesSent*/, CEventCallback * /*pc*/) 
{ 
} 
 
void CIocpSocket::OnConnect(BOOL /*bSuccess*/, CEventCallback * /*pc*/) 
{ 
} 
 
// get-only access to the underlying socket 
CIocpSocket::operator SOCKET() const 
{ 
	return m_hSocket; 
} 
 
// Primary constructor 
CIocpSocket::CIocpSocket() 
{ 
	m_hSocket    = INVALID_SOCKET; 
	m_pcAccept   = NULL; 
	m_pcRead     = NULL; 
	m_pcWrite    = NULL; 
} 
 
// Accepted socket constructor, requires passing the listening CIocpSocket 
// Also useful for creating a new socket that is a clone of the src socket 
CIocpSocket::CIocpSocket(const CIocpSocket& src, SOCKET s) 
{ 
	m_hSocket    = INVALID_SOCKET; 
	m_pcAccept   = NULL; 
	m_pcRead     = NULL; 
	m_pcWrite    = NULL; 
 
	Create(s, src.m_af, src.m_type, src.m_proto, src.m_pwpi, src.m_g, src.m_dwFlags); 
} 
 
CIocpSocket::~CIocpSocket(void) 
{ 
	Close(); 
} 
 
// Create/utilize a socket and associate it with the threadpool. 
BOOL CIocpSocket::Create(SOCKET s, INT af, INT type, INT proto, LPWSAPROTOCOL_INFO lpProtoInfo, GROUP g, DWORD dwFlags) 
{ 
	SOCKET sNew = s; 
	INT nError; 
 
	// Create a new socket if one wasn't specified 
	if (sNew == INVALID_SOCKET) 
		sNew = WSASocket(af, type, proto, lpProtoInfo, g, dwFlags); 
 
	_ASSERTE(sNew != INVALID_SOCKET); 
	if (sNew == INVALID_SOCKET) return FALSE; 
 
	// Add our new socket handle to the IOCP 
	if( !Attach((HANDLE)sNew) ) 
	{ 
		nError = WSAGetLastError(); 
 
		// Close the temporary socket if we made a new one 
		if (s == INVALID_SOCKET && sNew != INVALID_SOCKET) 
			closesocket(sNew); 
 
		WSASetLastError(nError); 
 
		return FALSE; 
	} 
 
	// Success, so close the existing socket if present 
	Close(); 
 
	m_hSocket    = sNew; 
 
	m_pcAccept   = NULL; 
	m_pcRead     = NULL; 
	m_pcWrite    = NULL; 
 
	m_af      = af; 
	m_type    = type; 
	m_proto   = proto; 
	m_g       = g; 
	m_dwFlags = dwFlags; 
 
	if (lpProtoInfo) 
	{ 
		m_wpi = lpProtoInfo[0]; 
		m_pwpi = &m_wpi; 
	} 
	else 
	{ 
		m_pwpi = NULL; 
	} 
 
	// get socket2 extension function 
	GetAcceptEx(); 
	GetConnectEx(); 
 
	return TRUE; 
} 
 
// Closes the socket, safely waiting until all pending operations are complete 
void CIocpSocket::Close(void) 
{ 
	SOCKET s = INVALID_SOCKET; 
 
	// Get the current socket handle while simultaneously clearing it. 
	// Ensures multiple threads can't close the socket 
	s = (SOCKET)InterlockedExchangePointer((LPVOID *)&m_hSocket, (LPVOID)s); 
 
	if (s != INVALID_SOCKET) 
	{ 
		shutdown(s, 2); 
		closesocket(s); 
 
		// All pending overlapped operations must complete before we tear down 
		WaitForPending(); 
 
		if (m_pcAccept) delete m_pcAccept; 
		if (m_pcRead)   delete m_pcRead; 
		if (m_pcWrite)  delete m_pcWrite; 
 
		m_pcAccept  = NULL; 
		m_pcRead    = NULL; 
		m_pcWrite   = NULL; 
	} 
} 
 
// Get a pointer to Winsock's AcceptEx() function 
void CIocpSocket::GetAcceptEx(void) 
{ 
	GUID  gufn = WSAID_ACCEPTEX; 
	DWORD dwBytes; 
 
	m_fnAcceptEx = NULL; 
	WSAIoctl(m_hSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &gufn, sizeof(gufn), &m_fnAcceptEx, sizeof(m_fnAcceptEx), &dwBytes, NULL, NULL); 
} 
 
// Get a pointer to Winsock's ConnectEx (XP/2003 only) function 
void CIocpSocket::GetConnectEx(void) 
{ 
	GUID  gufn = WSAID_CONNECTEX; 
	DWORD dwBytes; 
 
	m_fnConnectEx = NULL; 
	WSAIoctl(m_hSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &gufn, sizeof(gufn), &m_fnConnectEx, sizeof(m_fnConnectEx), &dwBytes, NULL, NULL); 
 
	// Point the function to our built-in fake function, for pre-XP OS's 
	if (m_fnConnectEx == NULL) 
		m_fnConnectEx = _FakeConnectEx; 
} 
 
/* Event callback for handling asynchronous (fake) ConnectEx calls on pre-XP OS's. 
   LPVOID pvcbUser 
          Pointer to a (CEventCallback *) class to post once the connection completes 
   LPVOID pvSocket 
          Cast to (SOCKET), the socket to connect on 
   LPVOID pvAddrTo 
          Cast to (LPSOCKADDR), the address to connect to 
   LPVOID piToLen 
          Cast to (INT), the size of the address 
*/ 
void CIocpSocket::_OnFakeConnectEx(CEventSource * /*pvThis*/, LPVOID pvcbUser,  LPVOID pvSocket, LPVOID pvAddrTo, LPVOID piToLen, CEventCallback *pc) 
{ 
	CEventCallback *pcbUser = (CEventCallback *)pvcbUser; 
	SOCKET          s       = (SOCKET)pvSocket; 
	INT             n; 
 
	delete pc; // Was a one-shot deal anyway, see _FakeConnectEx 
 
	// Try and connect 
	n = connect(s, (LPSOCKADDR)pvAddrTo, (INT)piToLen); 
 
	// Report success or failure in the callback class 
	pcbUser->m_Param4 = (n == SOCKET_ERROR) ? (LPVOID)WSAGetLastError() : NULL; 
 
	// Post a completion event 
	pcbUser->Post(); 
} 
 
/* Standard callback for accepting/connecting 
   LPVOID pvThis 
          Pointer to a  object, used to make in-context callbacks 
   LPVOID pvSocket 
          Cast to type (SOCKET), the newly-connected socket 
   LPVOID pvFake 
          Used only if we're using our fake ConnectEx function, to force an error 
*/ 
void CIocpSocket::_OnAcceptOrConnect(CEventSource *pvThis, LPVOID pvSocket, LPVOID, LPVOID, LPVOID pvFake, CEventCallback *pc) 
{ 
	CIocpSocket *p = static_cast(pvThis); 
	SOCKET     s = (SOCKET)pvSocket; 
	INT        nError; 
	DWORD      dwBytes; 
	DWORD      dwFlags; 
	BOOL       bSuccess; 
 
	bSuccess = WSAGetOverlappedResult(p->m_hSocket, pc, &dwBytes, FALSE, &dwFlags); 
 
	if (s == INVALID_SOCKET) // Connected socket, not an accepted socket 
	{ 
		if (pvFake != NULL) 
		{ 
			bSuccess = FALSE; 
			WSASetLastError((INT)pvFake); 
		} 
		p->OnConnect(bSuccess, pc); 
	} 
	else 
	{ 
		// Record the last winsock error 
		nError = WSAGetLastError(); 
 
		// Attempt to update the socket's context if we succeeded at accepting a connection 
		if (bSuccess) 
		{ 
			if (setsockopt(s, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&p->m_hSocket, sizeof(p->m_hSocket))) 
			{ 
				bSuccess = FALSE; 
				nError = WSAGetLastError(); 
			} 
		} 
 
		// If we failed for any reason, close the new socket down 
		if (!bSuccess) 
		{ 
			shutdown(s, 2); 
			closesocket(s); 
			s = INVALID_SOCKET; 
		} 
 
		// Restore the winsock error -- ensures subsequent callers get the proper error code 
		WSASetLastError(nError); 
 
		p->OnAccept(bSuccess, s, pc); 
	} 
} 
 
/* Standard callback for receiving socket data 
   LPVOID pvThis 
          Pointer to a  object, used to make in-context callbacks 
   LPVOID pvBuffer 
          Cast to type (LPWSABUF), pointer to the WSABUF structures 
   LPVOID pvdwBuffers 
          Cast to type (DWORD), count of the buffers specified in pvBuffer 
   LPVOID pvAddrFrom 
          Cast to type (LPSOCKADDR), for RecvFrom events only, the sender's IP address 
   LPVOID piFromLen 
          Cast to type (INT), for RecvFrom only, the size of pvAddrFrom 
*/ 
void CIocpSocket::_OnRecv(CEventSource *pvThis, LPVOID pvBuffer, LPVOID pvdwBuffers, LPVOID pvAddrFrom, LPVOID piFromLen, CEventCallback *pc) 
{ 
	CIocpSocket *p = static_cast(pvThis); 
	DWORD      dwBytesRecvd = 0; 
	DWORD      dwFlags      = 0; 
	BOOL       bSuccess; 
 
	bSuccess = WSAGetOverlappedResult(p->m_hSocket, pc, &dwBytesRecvd, FALSE, &dwFlags); 
 
	if (pvAddrFrom) 
		p->OnReceiveFrom(bSuccess, (LPWSABUF)pvBuffer, (DWORD)pvdwBuffers, dwBytesRecvd, dwFlags, (LPSOCKADDR)pvAddrFrom, ((LPINT)piFromLen)[0], pc); 
	else 
		p->OnReceive(bSuccess, (LPWSABUF)pvBuffer, (DWORD)pvdwBuffers, dwBytesRecvd, dwFlags, pc); 
} 
 
/* Standard callback for sending socket data 
   LPVOID pvThis 
          Pointer to a  object, used to make in-context callbacks 
   LPVOID pvBuffer 
          Cast to type (LPWSABUF), pointer to the WSABUF structures 
   LPVOID pvdwBuffers 
          Cast to type (DWORD), count of the buffers specified in pvBuffer 
*/ 
void CIocpSocket::_OnSend(CEventSource *pvThis, LPVOID pvBuffer, LPVOID pvdwBuffers, LPVOID, LPVOID, CEventCallback *pc) 
{ 
	CIocpSocket *p = static_cast(pvThis); 
	DWORD      dwBytesSent = 0; 
	DWORD      dwFlags     = 0; 
	BOOL       bSuccess; 
 
	bSuccess = WSAGetOverlappedResult(p->m_hSocket, pc, &dwBytesSent, FALSE, &dwFlags); 
 
	p->OnSend(bSuccess, (LPWSABUF)pvBuffer, (DWORD)pvdwBuffers, dwBytesSent, pc); 
} 
 
// if szAddress is null 
BOOL CIocpSocket::Bind(WORD wPort, LPCTSTR szAddress) 
{ 
	int nRet = 0; 
 
	if( szAddress ) 
	{ 
		struct addrinfo hints; 
		struct addrinfo *addrlocal = NULL; 
		char szPort[6]; 
 
		// Resolve the interface 
		memset(&hints, 0, sizeof(hints)); 
		hints.ai_flags  = AI_PASSIVE; 
		hints.ai_family = AF_INET; 
		hints.ai_socktype = SOCK_STREAM; 
		hints.ai_protocol = IPPROTO_IP; 
		_snprintf(szPort, 5, "%u", wPort); 
 
		if( getaddrinfo(szAddress, szPort, &hints, &addrlocal) != 0 ) 
		{ 
			return FALSE; 
		} 
 
		if( addrlocal == NULL ) 
		{ 
			return FALSE; 
		} 
 
		nRet = bind(m_hSocket, addrlocal->ai_addr, (INT)addrlocal->ai_addrlen); 
		if( nRet == SOCKET_ERROR ) 
		{ 
			freeaddrinfo(addrlocal); 
			return FALSE; 
		} 
 
		freeaddrinfo(addrlocal); 
	} 
	else 
	{ 
		SOCKADDR_IN addr; 
		ZeroMemory(&addr, sizeof(addr)); 
 
		addr.sin_family = (SHORT)m_af; 
		addr.sin_addr.s_addr = htonl(INADDR_ANY); 
		addr.sin_port = htons(wPort); 
 
		nRet = bind(m_hSocket, (LPSOCKADDR)&addr, sizeof(addr)); 
		if( nRet == SOCKET_ERROR ) 
		{ 
			return FALSE; 
		} 
	} 
 
	return TRUE; 
} 
 
// Puts the socket into listen mode 
INT CIocpSocket::Listen(INT nBacklog) 
{ 
	return listen(m_hSocket, nBacklog); 
} 
 
// Overlapped method of accepting a connection 
BOOL CIocpSocket::Accept(SOCKET *psockConn, BOOL bOverlapped, CEventCallback *pcb) 
{ 
	static CHAR chOutput[(sizeof(SOCKADDR_IN) + 16) * 2]; 
 
	INT    nError; 
	DWORD  dwBytes; 
	BOOL   nRet = FALSE; 
	SOCKET s    = INVALID_SOCKET; 
 
	// Reset caller's socket if present 
	if (psockConn) *psockConn = s; 
 
	// If not overlapped, caller must supply a pointer to a socket so we can fill it in! 
	if (!bOverlapped && !psockConn) 
	{ 
		WSASetLastError(WSAEINVAL); 
		return FALSE; 
	} 
 
	// Create an async callback class if necessary 
	if (!m_pcAccept && bOverlapped && !pcb) 
		m_pcAccept = new CAcceptCallback(*this); 
 
	// If overlapped and caller didn't specify a callback, use our built-in one 
	if (bOverlapped && !pcb) pcb = m_pcAccept; 
 
	// Try and acquire use of the callback 
	if (pcb) 
	{ 
		if (!pcb->Acquire()) return FALSE; 
	} 
 
	// Create a socket to accept a connection on 
	s = WSASocket(m_af, m_type, m_proto, m_pwpi, m_g, m_dwFlags); 
	nError = WSAGetLastError(); 
 
	if (s != INVALID_SOCKET) 
	{ 
		// Assign the socket to the extra data for the async callback 
		if (pcb) 
			pcb->m_Param1 = (LPVOID)s; 
 
		// Issue the command 
		nRet = m_fnAcceptEx(m_hSocket, s, chOutput, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, pcb); 
		nError = WSAGetLastError(); 
		if( (nRet == FALSE) && (nError == WSA_IO_PENDING) ) 
		{ 
			nRet = TRUE; 
		} 
	} 
 
	// If the call failed, cleanup and release the callback 
	if (nRet == FALSE) 
	{ 
		if (s != INVALID_SOCKET) closesocket(s); 
		if (pcb) pcb->UnAcquire(); 
 
		s = INVALID_SOCKET; 
	} 
 
	if (psockConn) *psockConn = s; 
 
	WSASetLastError(nError); 
 
	return nRet; 
} 
 
// Overlapped (or pseudo-overlapped for pre-XP OS's) connect method 
BOOL CIocpSocket::Connect(LPSOCKADDR pAddrTo, INT nToLen, BOOL bOverlapped, CEventCallback *pcb) 
{ 
	BOOL nRet = FALSE; 
	INT  nError; 
 
	// A socket can't be listening AND connecting at the same time, so we 
	// take the liberty of re-using the m_pcAccept callback here. 
	// Create an async callback class if necessary 
	if (!m_pcAccept && bOverlapped && !pcb) 
		m_pcAccept = new CAcceptCallback(*this); 
 
	// If overlapped and caller didn't specify a callback, use our built-in one 
	if (bOverlapped && !pcb) pcb = m_pcAccept; 
 
	// Try and acquire use of the callback 
	if (pcb) 
	{ 
		if (!pcb->Acquire()) 
			return FALSE; 
		pcb->m_Param1 = (LPVOID)(SOCKET)INVALID_SOCKET; // Tells _OnAcceptOrConnect we're connecting, not accepting 
	} 
 
	if (pcb) 
	{ 
		// Issue the command 
		nRet = m_fnConnectEx(m_hSocket, pAddrTo, nToLen, NULL, 0, NULL, pcb); 
		nError = WSAGetLastError(); 
 
		// ConnectEx differs from connect() in that the socket must be bound first. 
		// If the user forgot to bind the socket, we'll take care of it here. 
		if( (nRet == FALSE) && (nError == WSAEINVAL) ) 
		{ 
			Bind(); 
			nRet = m_fnConnectEx(m_hSocket, pAddrTo, nToLen, NULL, 0, NULL, pcb); 
			nError = WSAGetLastError(); 
		} 
	} 
	else 
	{ 
		nRet = connect(m_hSocket, pAddrTo, nToLen); 
		nRet = (nRet == SOCKET_ERROR) ? FALSE : TRUE; 
		nError = WSAGetLastError(); 
	} 
 
	if( (nRet == FALSE) && (nError != ERROR_IO_PENDING) ) 
	{ 
		if (pcb) 
			pcb->UnAcquire(); 
	} 
	else 
		nRet = TRUE; 
 
	WSASetLastError(nError); 
 
	return nRet; 
} 
 
BOOL CIocpSocket::Connect( LPCTSTR pszHostAddress, UINT nPort, BOOL bOverlapped, CEventCallback *pcb ) 
{ 
	ASSERT(pszHostAddress); //Must have a valid host 
	ASSERT(_tcslen(pszHostAddress)); //as above 
 
	//Work out the IP address of the machine we want to connect to 
	LPSTR lpszAsciiTarget = T2A((LPTSTR)pszHostAddress); 
 
	//Determine if the address is in dotted notation 
	SOCKADDR_IN sockAddr; 
	ZeroMemory(&sockAddr, sizeof(SOCKADDR_IN)); 
	sockAddr.sin_family = AF_INET; 
	sockAddr.sin_port = htons((u_short)nPort); 
	sockAddr.sin_addr.s_addr = inet_addr(lpszAsciiTarget); 
 
	//If the address is not dotted notation, then do a DNS  
	//lookup of it. 
	if( sockAddr.sin_addr.s_addr == INADDR_NONE ) 
	{ 
		LPHOSTENT lphost = gethostbyname(lpszAsciiTarget); 
		if (lphost == NULL) 
		{ 
			return FALSE; 
		} 
 
		sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; 
	} 
 
	return Connect( (LPSOCKADDR)&sockAddr, sizeof(sockAddr), bOverlapped, pcb ); 
} 
 
// pvBuffer must remain valid until the callback returns 
BOOL CIocpSocket::Receive(LPBYTE pvBuffer, DWORD dwBuffSize, LPDWORD pdwBytesRecvd, LPDWORD pdwFlags, BOOL bOverlapped) 
{ 
	if (!m_pcRead && bOverlapped) 
		m_pcRead = new CReceiveCallback(*this); 
 
	WSABUF buf; 
	buf.len = dwBuffSize; 
	buf.buf = (char *)pvBuffer; 
 
	return Receive(&buf, 1, pdwBytesRecvd, pdwFlags, (bOverlapped == TRUE) ? m_pcRead : NULL); 
} 
 
// Make sure the WSABUF structures remain valid until the overlapped operation completes! 
BOOL CIocpSocket::Receive(LPWSABUF pBuffers, DWORD dwBuffCount, LPDWORD pdwBytesRecvd, LPDWORD pdwFlags, CEventCallback *pcb) 
{ 
	INT nRet; 
	INT nError; 
 
	if (pcb) 
	{ 
		if (!pcb->Acquire()) return FALSE; 
 
		if (pcb == m_pcRead) 
		{ 
			CopyMemory(&m_bufRecv, pBuffers, sizeof(m_bufRecv)); 
			pBuffers = &m_bufRecv; 
		} 
 
		pcb->m_Param1 = pBuffers; 
		pcb->m_Param2 = (LPVOID)dwBuffCount; 
		pcb->m_Param3 = NULL; 
		pcb->m_Param4 = NULL; 
	} 
 
	nRet = WSARecv(m_hSocket, pBuffers, dwBuffCount, pdwBytesRecvd, pdwFlags, pcb, NULL); 
	nError = WSAGetLastError(); 
	if( (nRet == SOCKET_ERROR) && (nError != WSA_IO_PENDING) ) 
	{ 
		if (pcb) 
			pcb->UnAcquire(); 
	} 
	else 
		nRet = 0; 
 
	WSASetLastError(nError); 
 
	return (nRet == 0) ? TRUE : FALSE; 
} 
 
// If pAddrFrom and piFromLen are NULL, this function will use our built-in members to receive that information. 
// Make sure pvBuffer survives until the overlapped operation completes! 
BOOL CIocpSocket::ReceiveFrom(LPBYTE pvBuffer, DWORD dwBuffSize, LPDWORD pdwBytesRecvd, LPDWORD pdwFlags, LPSOCKADDR pAddrFrom, LPINT piFromLen, BOOL bOverlapped) 
{ 
	if (!m_pcRead && bOverlapped) 
		m_pcRead = new CReceiveCallback(*this); 
 
	WSABUF buf; 
	buf.len = dwBuffSize; 
	buf.buf = (char *)pvBuffer; 
 
	if (!pAddrFrom) pAddrFrom = (LPSOCKADDR)&m_addrFrom; 
	if (!piFromLen) piFromLen = &m_addrFromLen; 
 
	return ReceiveFrom(&buf, 1, pdwBytesRecvd, pdwFlags, pAddrFrom, piFromLen, (bOverlapped == TRUE) ? m_pcRead:NULL); 
} 
 
// Behavior is identical to WSARecv 
BOOL CIocpSocket::ReceiveFrom(LPWSABUF pBuffers, DWORD dwBuffCount, LPDWORD pdwBytesRecvd, LPDWORD pdwFlags, LPSOCKADDR pAddrFrom, LPINT piFromLen, CEventCallback * pcb) 
{ 
	INT nRet; 
	INT nError; 
 
	if (pcb) 
	{ 
		if (!pcb->Acquire()) return FALSE; 
 
		if (pcb == m_pcRead) 
		{ 
			CopyMemory(&m_bufRecv, pBuffers, sizeof(m_bufRecv)); 
			pBuffers = &m_bufRecv; 
 
			if (piFromLen == &m_addrFromLen) 
			m_addrFromLen = sizeof(m_addrFrom); 
		} 
 
		pcb->m_Param1 = pBuffers; 
		pcb->m_Param2 = (LPVOID)dwBuffCount; 
		pcb->m_Param3 = (LPVOID)pAddrFrom; 
		pcb->m_Param4 = (LPVOID)piFromLen; 
	} 
 
	nRet = WSARecvFrom(m_hSocket, pBuffers, dwBuffCount, pdwBytesRecvd, pdwFlags, pAddrFrom, piFromLen, pcb, NULL); 
	nError = WSAGetLastError(); 
	if( (nRet == SOCKET_ERROR) && (nError != WSA_IO_PENDING) ) 
	{ 
		if (pcb) 
			pcb->UnAcquire(); 
	} 
	else 
		nRet = 0; 
 
	WSASetLastError(nError); 
 
	return (nRet == 0) ? TRUE : FALSE; 
} 
 
BOOL CIocpSocket::Send(LPWSABUF pBuffers, DWORD dwBuffCount, LPDWORD pdwBytesSent, DWORD dwFlags, CEventCallback *pcb) 
{ 
	INT nRet; 
	INT nError; 
 
	if (pcb) 
	{ 
		if (!pcb->Acquire()) return FALSE; 
 
		if (pcb == m_pcWrite) 
		{ 
			CopyMemory(&m_bufSend, pBuffers, sizeof(m_bufSend)); 
			pBuffers = &m_bufSend; 
		} 
 
		pcb->m_Param1 = pBuffers; 
		pcb->m_Param2 = (LPVOID)dwBuffCount; 
		pcb->m_Param3 = NULL; 
		pcb->m_Param4 = NULL; 
	} 
 
	nRet = WSASend(m_hSocket, pBuffers, dwBuffCount, pdwBytesSent, dwFlags, pcb, NULL); 
	nError = WSAGetLastError(); 
	if( (nRet == SOCKET_ERROR) && (nError != WSA_IO_PENDING) ) 
	{ 
		if (pcb) pcb->UnAcquire(); 
	} 
	else 
		nRet = 0; 
 
	WSASetLastError(nError); 
 
	return (nRet == 0) ? TRUE : FALSE; 
} 
 
BOOL CIocpSocket::Send(LPBYTE pvBuffer, DWORD dwBuffSize, LPDWORD pdwBytesSent, DWORD dwFlags, BOOL bOverlapped) 
{ 
	if (!m_pcWrite && bOverlapped) 
		m_pcWrite = new CSendCallback(*this); 
 
	WSABUF buf; 
	buf.len = dwBuffSize; 
	buf.buf = (char *)pvBuffer; 
 
	return Send(&buf, 1, pdwBytesSent, dwFlags, (bOverlapped == TRUE) ? m_pcWrite : NULL); 
} 
 
BOOL CIocpSocket::SendTo(LPWSABUF pBuffers, DWORD dwBuffCount, LPSOCKADDR pAddrTo, INT nAddrToLen, LPDWORD pdwBytesSent, DWORD dwFlags, CEventCallback *pcb) 
{ 
	INT nRet; 
	INT nError; 
 
	if (pcb) 
	{ 
		if (!pcb->Acquire()) return FALSE; 
 
		if (pcb == m_pcWrite) 
		{ 
			CopyMemory(&m_bufSend, pBuffers, sizeof(m_bufSend)); 
			pBuffers = &m_bufSend; 
		} 
 
		pcb->m_Param1 = pBuffers; 
		pcb->m_Param2 = (LPVOID)dwBuffCount; 
		pcb->m_Param3 = NULL; 
		pcb->m_Param4 = NULL; 
	} 
 
	nRet = WSASendTo(m_hSocket, pBuffers, dwBuffCount, pdwBytesSent, dwFlags, pAddrTo, nAddrToLen, pcb, NULL); 
	nError = WSAGetLastError(); 
	if( (nRet == SOCKET_ERROR) && (nError != WSA_IO_PENDING) ) 
	{ 
		if(pcb) 
			pcb->UnAcquire(); 
	} 
	else 
		nRet = 0; 
 
	WSASetLastError(nError); 
 
	return (nRet == 0) ? TRUE : FALSE; 
} 
 
BOOL CIocpSocket::SendTo(LPBYTE pvBuffer, DWORD dwBuffSize, LPSOCKADDR pAddrTo, INT nAddrToLen, LPDWORD pdwBytesSent, DWORD dwFlags, BOOL bOverlapped) 
{ 
	if (!m_pcWrite && bOverlapped) 
		m_pcWrite = new CSendCallback(*this); 
 
	WSABUF buf; 
	buf.len = dwBuffSize; 
	buf.buf = (char *)pvBuffer; 
 
	return SendTo(&buf, 1, pAddrTo, nAddrToLen, pdwBytesSent, dwFlags, (bOverlapped == TRUE) ? m_pcWrite:NULL); 
} 
 
/* 
    TODO: This is a fake implementation of ConnectEx() available only in XP/2003.  It is a poor implementation 
    because it queues the connection to be initiated by one of the worker threads in the thread pool -- which means 
    for the duration of the connect() call, that thread is not available to process IOCP's. 
 
    The only reason I did it this way was to save time, and because the additional overhead of having to support 
    WSAEventSelect wasn't worth the effort. 
 
    The other reason for this method of implementation was because I wanted to avoid specifically putting the socket 
    into non-blocking mode, which is what I'd have to do to implement WSAEventSelect (in addition to implementing a whole 
    new thread pool for waiting on events) 
*/ 
BOOL CIocpSocket::_FakeConnectEx(SOCKET s, const struct sockaddr * pAddrTo, INT nAddrToLen, LPVOID /*pvSendBuffer*/, DWORD /*dwSendDataLen*/, LPDWORD /*pdwBytesSent*/, LPOVERLAPPED pOverlapped) 
{ 
	// If overlapped is requested, create a new callback object. 
	// The callback function itself will destroy our temporary object when the time comes. 
	if (pOverlapped) 
	{ 
		CEventCallback    *pcUser  = static_cast(pOverlapped); 
		CEventSource    *pEventSrc = pcUser[0]; 
		CEventCallback    *pc      = new CEventCallback(pEventSrc, _OnFakeConnectEx); 
 
		pc->m_Param1 = (LPVOID)pcUser; 
		pc->m_Param2 = (LPVOID)s; 
		pc->m_Param3 = (LPVOID)pAddrTo; 
		pc->m_Param4 = (LPVOID)nAddrToLen; 
 
		// Increment the pending I/O counter 
		pc->Acquire(); 
 
		// Post the connection event 
		if (pc->Post()) 
		{ 
			WSASetLastError(WSA_IO_PENDING); 
			return FALSE; 
		} 
		else // Failed to post the event, rollback time 
		{ 
			pc->UnAcquire(); 
			delete pc; 
		} 
 
		return FALSE; 
	} 
 
	INT n; 
	n = connect(s, pAddrTo, nAddrToLen); 
 
	return !(n == SOCKET_ERROR); 
} 
 
// to check if this socket is active 
BOOL CIocpSocket::IsActive(void) 
{ 
	return (m_pcAccept || m_pcRead || m_pcWrite); 
}