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);
}