You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1237 lines
33 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/*****************************************************************************
*
* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xSocket.cpp
*
***/
#include "../../Pch.h"
#pragma hdrstop
#include "pnAceW9xInt.h"
namespace W9x {
/*****************************************************************************
*
* Private data
*
***/
static HWND s_window;
/****************************************************************************
*
* CSocketMapper
*
***/
class CSocket;
class CSocketMapper {
private:
struct Map {
AsyncCancelId sequence;
SOCKET sock;
UINT message;
CSocket * object;
FAsyncNotifySocketProc notifyProc;
};
ARRAY(Map) m_mapTable;
public:
void Add (
AsyncCancelId sequence,
CSocket * object,
UINT message,
SOCKET sock,
FAsyncNotifySocketProc notifyProc
);
void Delete (CSocket * object);
CSocket * Find (AsyncCancelId sequence) const;
CSocket * Find (CSocket * object) const;
CSocket * Find (UINT message, SOCKET sock) const;
CSocket * Find (FAsyncNotifySocketProc notifyProc, unsigned index) const;
};
//===========================================================================
void CSocketMapper::Add (
AsyncCancelId sequence,
CSocket * object,
UINT message,
SOCKET sock,
FAsyncNotifySocketProc notifyProc
) {
Map * mapping = m_mapTable.New();
mapping->sequence = sequence;
mapping->object = object;
mapping->message = message;
mapping->sock = sock;
mapping->notifyProc = notifyProc;
}
//===========================================================================
void CSocketMapper::Delete (CSocket * object) {
for (unsigned index = m_mapTable.Count() - 1; index < m_mapTable.Count(); )
if (m_mapTable[index].object == object)
if (index + 1 < m_mapTable.Count())
m_mapTable[index] = m_mapTable.Pop();
else
m_mapTable.Pop();
else
--index;
}
//===========================================================================
CSocket * CSocketMapper::Find (AsyncCancelId sequence) const {
for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term();
curr != term;
++curr)
if (curr->sequence == sequence)
return curr->object;
return nil;
}
//===========================================================================
CSocket * CSocketMapper::Find (CSocket * object) const {
for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term();
curr != term;
++curr)
if (curr->object == object)
return curr->object;
return nil;
}
//===========================================================================
CSocket * CSocketMapper::Find (UINT message, SOCKET sock) const {
for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term();
curr != term;
++curr)
if ((curr->message == message) && (curr->sock == sock))
return curr->object;
return nil;
}
//===========================================================================
CSocket * CSocketMapper::Find (FAsyncNotifySocketProc notifyProc, unsigned index) const {
for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term();
curr != term;
++curr)
if ((curr->notifyProc == notifyProc) && !index--)
return curr->object;
return nil;
}
static CSocketMapper s_mapper;
/****************************************************************************
*
* CSocket
*
***/
class CSocket {
private:
// These constants are used to track whether a socket has been connected,
// and whether it has been disconnected. We do not track whether we have
// dispatched a connection failure notification, because socket objects
// are deleted immediately after dispatching that notification.
enum { kDispatchedConnectSuccess = (1 << 0) };
enum { kDispatchedDisconnect = (1 << 1) };
struct Command {
LINK(Command) link;
enum Code {
CONNECT,
WRITE,
} code;
void * param;
union {
struct {
byte connType;
} connect;
struct {
const void * data; // pointer to application's data
unsigned bytes;
} write;
};
};
// These variables are protected by the critical section
CCritSect m_critSect;
LISTDECL(Command, link) m_commandList;
ARRAY(byte) m_sendQueue;
// These variables are never modified outside the constructor and
// destructor
UINT m_message;
FAsyncNotifySocketProc m_notifyProc;
AsyncCancelId m_sequence;
SOCKET m_sock;
// These variables are only ever touched during a callback from the
// window procedure, which is single threaded
unsigned m_dispatched;
byte m_readBuffer[1460 * 2];
unsigned m_readBytes;
void * m_userState;
public:
CSocket (
AsyncCancelId sequence,
UINT message,
SOCKET sock,
FAsyncNotifySocketProc notifyProc
);
~CSocket ();
void EnableNagling (bool enable);
void Disconnect (bool hardClose);
inline AsyncCancelId GetSequence () const;
inline bool IsConnected () const;
inline bool IsDisconnected () const;
void OnClose ();
void OnConnect ();
void OnReadReady ();
void OnWriteReady ();
void ProcessQueue ();
void QueueConnect (
void * param,
byte connType
);
void QueueWrite (
void * param,
const void * data,
unsigned bytes
);
bool Send ( // returns false if disconnected
const void * data,
unsigned bytes
);
};
//===========================================================================
CSocket::CSocket (
AsyncCancelId sequence,
UINT message,
SOCKET sock,
FAsyncNotifySocketProc notifyProc
) :
m_dispatched(0),
m_notifyProc(notifyProc),
m_readBytes(0),
m_sequence(sequence),
m_message(message),
m_sock(sock),
m_userState(nil)
{
// Create a mapping for this socket
s_mapper.Add(sequence, this, message, sock, notifyProc);
}
//===========================================================================
CSocket::~CSocket () {
// If we dispatched a connect notification, verify that we also
// dispatched a disconnect notification
ASSERT(IsDisconnected() || !IsConnected());
// Delete the mapping for this socket
s_mapper.Delete(this);
// Close the socket
closesocket(m_sock);
m_sock = INVALID_SOCKET;
// Free memory
m_commandList.Clear();
}
//===========================================================================
void CSocket::EnableNagling (bool enable) {
BOOL arg = !enable;
int result = setsockopt(
m_sock,
IPPROTO_TCP,
TCP_NODELAY,
(const char *)&arg,
sizeof(arg)
);
if (result)
LogMsg(kLogFatal, "failed: setsockopt");
}
//===========================================================================
void CSocket::Disconnect (bool hardClose) {
// This function is called outside the critical section, which is
// necessary to avoid deadlocks, so it must not modify the object state.
// The purpose of this function is to cause WinSock to generate a
// FD_CLOSE notification, which will initiate synchronous destruction
// of the socket.
if (m_sock == INVALID_SOCKET)
return;
// Shutdown the socket
// const unsigned SD_BOTH = 2;
shutdown(m_sock, SD_BOTH);
// Windows always waits for an acknowledgement back from the other end
// of the connection, so we post our own FD_CLOSE notification if we need
// to terminate this socket immediately. Our notification processing code
// is robust against receiving the eventual FD_CLOSE from Windows even
// if another socket has been allocated in the interrim with the same
// socket handle.
if (hardClose)
PostMessage(s_window, m_message, m_sock, MAKELONG(FD_CLOSE, 0));
}
//===========================================================================
AsyncCancelId CSocket::GetSequence () const {
return m_sequence;
}
//===========================================================================
bool CSocket::IsConnected () const {
return (m_dispatched & kDispatchedConnectSuccess) != 0;
}
//===========================================================================
bool CSocket::IsDisconnected () const {
return (m_dispatched & kDispatchedDisconnect) != 0;
}
//===========================================================================
void CSocket::OnClose () {
// If we haven't yet dispatched a connection notification, then
// dispatch a failure to connect rather than dispatching a disconnect
// notification
if (!IsConnected()) {
OnConnect();
return;
}
// Verify that we haven't already dispatched a disconnect notification
if (IsDisconnected())
return;
// Process any remaining queued commands
ProcessQueue();
// Remove the mapping for this object so that we will never dispatch
// another event to it
s_mapper.Delete(this);
// Dispatch the disconnection notificaton
m_dispatched |= kDispatchedDisconnect;
m_notifyProc(
(AsyncSocket)this,
kNotifySocketDisconnect,
nil, // notify
&m_userState
);
// This function must not perform any processing after returning from
// the notification, because the application may have deleted the socket
// out from under us
}
//===========================================================================
void CSocket::OnConnect () {
// Verify that we haven't already dispatched a connection success
// message. We know that we haven't already dispatched a connection
// failure message, because the socket object would have already been
// deleted in that case.
if (IsConnected())
return;
// Verify that a connect command is queued
m_critSect.Enter();
Command * command = m_commandList.Head();
if (command && (command->code == command->CONNECT))
m_commandList.Unlink(command);
else
command = nil;
m_critSect.Leave();
if (!command)
return;
// Check for an error
int error;
int errorLen = sizeof(error);
int result = getsockopt(
m_sock,
SOL_SOCKET,
SO_ERROR,
(char *)&error,
&errorLen
);
if (result)
LogMsg(kLogFatal, "failed: getsockopt");
// Get addresses for the connection notification
AsyncNotifySocketConnect notify;
ZERO(notify.localAddr);
ZERO(notify.remoteAddr);
int nameLen = sizeof(notify.localAddr);
if (getsockname(m_sock, (sockaddr *)&notify.localAddr, &nameLen))
if (GetLastError() == WSAENOTCONN)
error = WSAENOTCONN;
else
LogMsg(kLogFatal, "failed: getsockname");
nameLen = sizeof(notify.remoteAddr);
if (getpeername(m_sock, (sockaddr *)&notify.remoteAddr, &nameLen))
if (GetLastError() == WSAENOTCONN)
error = WSAENOTCONN;
else
LogMsg(kLogFatal, "failed: getpeername");
// Dispatch the connection notification
notify.param = command->param;
notify.asyncId = 0;
notify.connType = command->connect.connType;
bool notifyResult = m_notifyProc(
error ? nil : (AsyncSocket)this,
error ? kNotifySocketConnectFailed : kNotifySocketConnectSuccess,
&notify,
&m_userState
);
// Delete the connect command
DEL(command);
// Handle failure to connect
if (error) {
// Destroy the socket
DEL(this);
}
// Handle a successful connection
else {
// Mark the socket as connected
m_dispatched |= kDispatchedConnectSuccess;
// If the application requested the socket closed, disconnect it
if (!notifyResult) {
Disconnect(true);
OnClose();
}
}
}
//===========================================================================
void CSocket::OnReadReady () {
for (;;) {
// Issue the read
int recvResult = recv(
m_sock,
(char *)(m_readBuffer + m_readBytes),
sizeof(m_readBuffer) - m_readBytes,
0 // flags
);
if (recvResult == SOCKET_ERROR)
if (GetLastError() == WSAEWOULDBLOCK)
return;
else
LogMsg(kLogFatal, "failed: recv");
// If the socket reported disconnection, close it
if ((recvResult == SOCKET_ERROR) || !recvResult) {
Disconnect(true);
OnClose();
return;
}
// Update the read buffer state
m_readBytes += recvResult;
// Dispatch a read notification
AsyncNotifySocketRead notify;
notify.param = nil;
notify.asyncId = 0;
notify.buffer = m_readBuffer;
notify.bytes = m_readBytes;
notify.bytesProcessed = 0;
bool notifyResult = m_notifyProc(
(AsyncSocket)this,
kNotifySocketRead,
&notify,
&m_userState
);
// If the application processed data, remove it from the read buffer
if (notify.bytesProcessed >= m_readBytes)
m_readBytes = 0;
else if (notify.bytesProcessed) {
MemMove(
&m_readBuffer[0],
&m_readBuffer[notify.bytesProcessed],
m_readBytes - notify.bytesProcessed
);
m_readBytes -= notify.bytesProcessed;
}
// If the application returned false from its notification procedure,
// disconnect
if (!notifyResult) {
Disconnect(false);
return;
}
}
}
//===========================================================================
void CSocket::OnWriteReady () {
// Verify that the socket is ready to send data
if (IsDisconnected() || !IsConnected())
return;
// Enter the critical section and verify that data is queued
m_critSect.Enter();
if (!m_sendQueue.Bytes()) {
m_critSect.Leave();
return;
}
// Attempt to send queued data
int result = send(
m_sock,
(const char *)m_sendQueue.Ptr(),
m_sendQueue.Bytes(),
0
);
if (result == SOCKET_ERROR)
if (GetLastError() == WSAEWOULDBLOCK)
result = 0;
else
LogMsg(kLogFatal, "failed: send");
// Dequeue sent bytes
if (result != SOCKET_ERROR)
if ((unsigned)result == m_sendQueue.Bytes())
m_sendQueue.Clear();
else if (result) {
MemMove(
&m_sendQueue[0],
&m_sendQueue[result],
m_sendQueue.Bytes() - result
);
COMPILER_ASSERT(sizeof(m_sendQueue[0]) == sizeof(byte));
m_sendQueue.SetCount(m_sendQueue.Count() - result);
}
// Leave the critical section
m_critSect.Leave();
// If the send failed, close the socket
if (result == SOCKET_ERROR) {
Disconnect(true);
OnClose();
}
}
//===========================================================================
void CSocket::ProcessQueue () {
// Verify that the socket is connected
if (IsDisconnected() || !IsConnected())
return;
// Dispatch each command in the queue
for (;;) {
// Remove the next command from the queue
m_critSect.Enter();
Command * command = m_commandList.Head();
if (command)
m_commandList.Unlink(command);
m_critSect.Leave();
if (!command)
break;
// Dispatch it
switch (command->code) {
case command->WRITE: {
AsyncNotifySocketWrite notify;
notify.param = command->param;
notify.asyncId = 0;
notify.buffer = (byte *)command->write.data;
notify.bytes = command->write.bytes;
notify.bytesProcessed = 0;
bool notifyResult = m_notifyProc(
(AsyncSocket)this,
kNotifySocketWrite,
&notify,
&m_userState
);
if (!notifyResult)
Disconnect(false);
}
break;
DEFAULT_FATAL(command->code);
}
// Delete the command
DEL(command);
}
}
//===========================================================================
void CSocket::QueueConnect (
void * param,
byte connType
) {
ASSERT(!IsConnected() && !IsDisconnected());
Command * command = NEW(Command);
command->code = command->CONNECT;
command->param = param;
command->connect.connType = connType;
m_commandList.Link(command);
}
//===========================================================================
void CSocket::QueueWrite (
void * param,
const void * data,
unsigned bytes
) {
ASSERT(!IsDisconnected());
Command * command = NEW(Command);
command->code = command->CONNECT;
command->param = param;
command->write.data = data;
command->write.bytes = bytes;
m_commandList.Link(command);
}
//===========================================================================
bool CSocket::Send ( // returns false if disconnected
const void * data,
unsigned bytes
) {
// Verify that the socket has not been disconnected
if (IsDisconnected())
return false;
// Enter the critical section
m_critSect.Enter();
// Attempt to send the data immediately
int result;
if (IsConnected() && !m_sendQueue.Bytes()) {
result = send(
m_sock,
(const char *)data,
bytes,
0
);
if ((result == SOCKET_ERROR) && (GetLastError() != WSAEWOULDBLOCK))
LogMsg(kLogFatal, "failed: send");
}
else
result = 0;
// If we were unable to send the entire message, queue the unsent portion
if ((unsigned)result < bytes) {
m_sendQueue.Add(
(const byte *)data + result,
bytes - result
);
}
// Leave the critical section
m_critSect.Leave();
return true;
}
/****************************************************************************
*
* Window procedure
*
***/
#define CLASS_NAME "AsyncSelectWindow"
#define WM_CANCEL_CONNECT (WM_USER + 0)
#define WM_PROCESS_QUEUE (WM_USER + 1)
static CCritSect s_critSect;
static HANDLE s_readyEvent;
static HANDLE s_thread;
static unsigned THREADCALL W9xSocketThreadProc (AsyncThread *);
static LRESULT CALLBACK WndProc (
HWND window,
UINT message,
WPARAM wParam,
LPARAM lParam
);
//===========================================================================
static void OnCancelConnect (
AsyncCancelId sequence
) {
// Find the associated object
s_critSect.Enter();
CSocket * object = s_mapper.Find(sequence);
s_critSect.Leave();
// Force the object to dispatch connect success or failure. We must not
// reference this object after the call to OnConnect() returns, because
// it may have been deleted.
if (object)
object->OnConnect();
}
//===========================================================================
static void OnProcessQueue (
AsyncCancelId sequence
) {
// Find the associated object
s_critSect.Enter();
CSocket * object = s_mapper.Find(sequence);
s_critSect.Leave();
// Process the object's dispatch queue
if (object)
object->ProcessQueue();
}
//===========================================================================
static void OnSocketEvent (
UINT message,
SOCKET sock,
long event
) {
// Enter the critical section
s_critSect.Enter();
// Find the associated object
CSocket * object = s_mapper.Find(message, sock);
// Verify that the object has not been disconnected. An object that
// has been disconnected can be deleted at any time, and therefore
// can't be referenced outside the critical section. However, the
// object cannot possibly have been disconnected here because objects
// once connected are only disconnected during a call to OnClose(),
// which is the last event we process for an object.
ASSERT(!object || !object->IsDisconnected());
// Leave the critical section
s_critSect.Leave();
// Dispatch the event
if (object)
switch (event) {
case FD_CLOSE: object->OnClose(); break;
case FD_CONNECT: object->OnConnect(); break;
case FD_READ: object->OnReadReady(); break;
case FD_WRITE: object->OnWriteReady(); break;
}
// We must not reference the object after dispatching the event,
// because the event processor may have deleted it
}
//===========================================================================
static void PostCancelConnect (AsyncCancelId sequence) {
s_critSect.Enter();
if (s_window)
PostMessage(s_window, WM_CANCEL_CONNECT, 0, (LPARAM)sequence);
s_critSect.Leave();
}
//===========================================================================
static void PostProcessQueue (AsyncCancelId sequence) {
s_critSect.Enter();
if (s_window)
PostMessage(s_window, WM_PROCESS_QUEUE, 0, (LPARAM)sequence);
s_critSect.Leave();
}
//===========================================================================
static void ShutdownWindow () {
s_critSect.Enter();
// Close the window
if (s_window) {
PostMessage(s_window, WM_CLOSE, 0, 0);
s_window = 0;
}
// Close the thread
if (s_thread) {
HANDLE thread = s_thread;
s_thread = 0;
s_critSect.Leave();
WaitForSingleObject(thread, INFINITE);
s_critSect.Enter();
}
// Close the ready event
if (s_readyEvent) {
CloseHandle(s_readyEvent);
s_readyEvent = 0;
}
s_critSect.Leave();
}
//===========================================================================
static HWND StartupWindow () {
s_critSect.Enter();
// Check whether we need to create the window
if (!s_window) {
// Check whether we need to create the thread
if (!s_thread) {
// Create an object which the thread can use to signal creation
// of the window
ASSERT(!s_readyEvent);
s_readyEvent = CreateEvent(nil, TRUE, FALSE, nil);
// Create a thread which will create the window and run its
// message loop
s_thread = (HANDLE) AsyncThreadCreate(
W9xSocketThreadProc,
nil,
L"W9xSockThread"
);
}
// Wait for the thread to create the window
HANDLE event = s_readyEvent;
s_critSect.Leave();
WaitForSingleObject(event, INFINITE);
s_critSect.Enter();
}
// Return the window handle
HWND window = s_window;
s_critSect.Leave();
return window;
}
//===========================================================================
static unsigned THREADCALL W9xSocketThreadProc (AsyncThread *) {
ASSERT(!s_window);
// Enter the critical section
s_critSect.Enter();
// Register the window class
HINSTANCE instance = (HINSTANCE)GetModuleHandle(nil);
WNDCLASS wndClass;
ZERO(wndClass);
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = instance;
wndClass.lpszClassName = CLASS_NAME;
RegisterClass(&wndClass);
// Create the window
s_window = CreateWindow(
CLASS_NAME,
CLASS_NAME,
WS_OVERLAPPED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0, // parent window
0, // menu
instance,
nil // param
);
// Leave the critical section
s_critSect.Leave();
// Trigger an event to allow anyone blocking on window creation to proceed
SetEvent(s_readyEvent);
// Dispatch messages
MSG msg;
while (GetMessage(&msg, nil, 0, 0))
if (msg.message == WM_CLOSE)
break;
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Destroy the window
s_critSect.Enter();
if (s_window) {
DestroyWindow(s_window);
s_window = 0;
}
s_critSect.Leave();
return 0;
}
//===========================================================================
static LRESULT CALLBACK WndProc (
HWND window,
UINT message,
WPARAM wParam,
LPARAM lParam
) {
switch (message) {
case WM_CANCEL_CONNECT:
OnCancelConnect((AsyncCancelId)lParam);
return 0;
case WM_PROCESS_QUEUE:
OnProcessQueue((AsyncCancelId)lParam);
return 0;
default:
if ((message >= WM_APP) && (message < 0xc000))
OnSocketEvent(message, (SOCKET)wParam, WSAGETSELECTEVENT(lParam));
else
return DefWindowProc(window, message, wParam, lParam);
break;
}
return 0;
}
/****************************************************************************
*
* Exported functions
*
***/
//===========================================================================
void W9xSocketConnect (
AsyncCancelId * cancelId,
const NetAddress & netAddr,
FAsyncNotifySocketProc notifyProc,
void * param,
const void * sendData,
unsigned sendBytes,
unsigned connectMs,
unsigned localPort
) {
// Not supported for W9X
REF(connectMs);
REF(localPort);
// If necessary, startup the window and message queue
HWND window = StartupWindow();
// Create a socket
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (!sock)
LogMsg(kLogFatal, "failed: socket");
// Assign a sequence number
static unsigned s_sequence;
AsyncCancelId sequence = (AsyncCancelId)++s_sequence;
if (!sequence)
sequence = (AsyncCancelId)++s_sequence;
*cancelId = sequence;
// Assign a message id
static UINT s_message;
UINT message = (s_message++ & 0x3fff) | WM_APP; // range 0x8000 - 0xbfff
// Create a socket object
CSocket * object = NEW(CSocket)(
sequence,
message,
sock,
notifyProc
);
// Queue a connect notification for the socket
object->QueueConnect(
param,
sendBytes ? ((const byte *)sendData)[0] : (byte)0
);
// Queue sending data
if (sendBytes)
object->Send(sendData, sendBytes);
// Setup notifications for the socket
int result = WSAAsyncSelect(
sock,
window,
message,
FD_CLOSE | FD_CONNECT | FD_READ | FD_WRITE
);
if (result)
LogMsg(kLogFatal, "failed: WSAAsyncSelect");
// Start the connection
if ( connect(sock, (const sockaddr *)&netAddr, sizeof(sockaddr)) &&
(GetLastError() != WSAEWOULDBLOCK) )
LogMsg(kLogFatal, "failed: connect. err=%u", GetLastError());
}
//===========================================================================
void W9xSocketConnectCancel (
FAsyncNotifySocketProc notifyProc,
AsyncCancelId cancelId
) {
// Enter the critical section
s_critSect.Enter();
// If a cancel id was given, cancel that one connection
if (cancelId)
PostCancelConnect(cancelId);
// Otherwise, cancel all connections using the specified notification
// procedure
else
for (unsigned index = 0; ; ++index) {
// Find the next socket object
CSocket * object = s_mapper.Find(notifyProc, index);
if (!object)
break;
// If the object exists and is not yet connected, cancel it
if (object)
PostCancelConnect(object->GetSequence());
}
// Leave the critical section
s_critSect.Leave();
}
//===========================================================================
void W9xSocketDelete (
AsyncSocket sock
) {
// Dereference the object
CSocket * object = (CSocket *)sock;
// Delete the object
s_critSect.Enter();
DEL(object);
s_critSect.Leave();
}
//===========================================================================
void W9xSocketDestroy () {
// Shutdown the window and message queue
ShutdownWindow();
}
//===========================================================================
void W9xSocketEnableNagling (
AsyncSocket sock,
bool enable
) {
// Dereference the object
CSocket * object = (CSocket *)sock;
// set nagling state
object->EnableNagling(enable);
}
//===========================================================================
void W9xSocketDisconnect (
AsyncSocket sock,
bool hardClose
) {
// Dereference the object
CSocket * object = (CSocket *)sock;
// Disconnect
object->Disconnect(hardClose);
}
//===========================================================================
unsigned W9xSocketStartListening (
const NetAddress & listenAddr,
FAsyncNotifySocketProc notifyProc
) {
REF(listenAddr);
REF(notifyProc);
return 0;
}
//===========================================================================
void W9xSocketStopListening (
const NetAddress & listenAddr,
FAsyncNotifySocketProc notifyProc
) {
REF(listenAddr);
REF(notifyProc);
}
//===========================================================================
bool W9xSocketSend (
AsyncSocket sock,
const void * data,
unsigned bytes
) {
// Dereference the object
CSocket * object = (CSocket *)sock;
// Send the data
return object->Send(data, bytes);
}
//===========================================================================
void W9xSocketSetNotifyProc (
AsyncSocket sock,
FAsyncNotifySocketProc notifyProc
) {
REF(sock);
REF(notifyProc);
// This provider does not allow changing the notification procedure
FATAL("SocketSetNotifyProc");
}
//===========================================================================
void W9xSocketSetBacklogAlloc (
AsyncSocket sock,
unsigned bufferSize
) {
// This provider does not limit the maximum backlog allocation
REF(sock);
REF(bufferSize);
}
//===========================================================================
bool W9xSocketWrite (
AsyncSocket sock,
const void * data,
unsigned bytes,
void * param
) {
// Dereference the object
CSocket * object = (CSocket *)sock;
// Send the data
if (!object->Send(data, bytes))
return false;
// Queue a completion notification
object->QueueWrite(param, data, bytes);
// Trigger processing the queue
PostProcessQueue(object->GetSequence());
return true;
}
} // namespace W9x