Files
wxWidgets/src/mac/carbon/cfsocket.cpp

2307 lines
55 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/mac/carbon/cfsocket.cpp
// Purpose: Socket handler classes
// Authors: Guilhem Lavaux, Guillermo Rodriguez Garcia
// Created: April 1997
// Copyright: (C) 1999-1997, Guilhem Lavaux
// (C) 2000-1999, Guillermo Rodriguez Garcia
// RCS_ID: $Id$
// License: see wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_SOCKETS
#include "wx/socket.h"
#ifndef WX_PRECOMP
#include "wx/object.h"
#include "wx/string.h"
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/event.h"
#include "wx/app.h"
#include "wx/utils.h"
#include "wx/timer.h"
#endif
#include "wx/apptrait.h"
#include "wx/module.h"
#include "wx/sckaddr.h"
#include "wx/mac/carbon/private.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define HAVE_INET_ATON
// DLL options compatibility check:
#include "wx/build.h"
WX_CHECK_BUILD_OPTIONS("wxNet")
// discard buffer
#define MAX_DISCARD_SIZE (10 * 1024)
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
// what to do within waits: we have 2 cases: from the main thread itself we
// have to call wxYield() to let the events (including the GUI events and the
// low-level (not wxWidgets) events from GSocket) be processed. From another
// thread it is enough to just call wxThread::Yield() which will give away the
// rest of our time slice: the explanation is that the events will be processed
// by the main thread anyhow, without calling wxYield(), but we don't want to
// eat the CPU time uselessly while sitting in the loop waiting for the data
#if wxUSE_THREADS
#define PROCESS_EVENTS() \
{ \
if ( wxThread::IsMain() ) \
wxYield(); \
else \
wxThread::Yield(); \
}
#else // !wxUSE_THREADS
#define PROCESS_EVENTS() wxYield()
#endif // wxUSE_THREADS/!wxUSE_THREADS
#define wxTRACE_Socket _T("wxSocket")
IMPLEMENT_CLASS(wxSocketBase, wxObject)
IMPLEMENT_CLASS(wxSocketServer, wxSocketBase)
IMPLEMENT_CLASS(wxSocketClient, wxSocketBase)
IMPLEMENT_CLASS(wxDatagramSocket, wxSocketBase)
IMPLEMENT_DYNAMIC_CLASS(wxSocketEvent, wxEvent)
// --------------------------------------------------------------------------
// private classes
// --------------------------------------------------------------------------
class wxSocketState : public wxObject
{
public:
wxSocketFlags m_flags;
wxSocketEventFlags m_eventmask;
bool m_notify;
void *m_clientData;
public:
wxSocketState() : wxObject() {}
DECLARE_NO_COPY_CLASS(wxSocketState)
};
struct _GSocket
{
CFSocketNativeHandle m_fd;
GAddress *m_local;
GAddress *m_peer;
GSocketError m_error;
int m_non_blocking;
int m_server;
int m_stream;
int m_oriented;
int m_establishing;
unsigned long m_timeout;
// Callbacks
GSocketEventFlags m_detected;
GSocketCallback m_cbacks[GSOCK_MAX_EVENT];
char *m_data[GSOCK_MAX_EVENT];
CFSocketRef m_cfSocket;
CFRunLoopSourceRef m_runLoopSource;
CFReadStreamRef m_readStream ;
CFWriteStreamRef m_writeStream ;
};
struct _GAddress
{
struct sockaddr *m_addr;
size_t m_len;
GAddressType m_family;
int m_realfamily;
GSocketError m_error;
int somethingElse ;
};
void wxMacCFSocketCallback(CFSocketRef s, CFSocketCallBackType callbackType,
CFDataRef address, const void* data, void* info) ;
void _GSocket_Enable(GSocket *socket, GSocketEvent event) ;
void _GSocket_Disable(GSocket *socket, GSocketEvent event) ;
// ==========================================================================
// wxSocketBase
// ==========================================================================
// --------------------------------------------------------------------------
// Initialization and shutdown
// --------------------------------------------------------------------------
// FIXME-MT: all this is MT-unsafe, of course, we should protect all accesses
// to m_countInit with a crit section
size_t wxSocketBase::m_countInit = 0;
bool wxSocketBase::IsInitialized()
{
return m_countInit > 0;
}
bool wxSocketBase::Initialize()
{
if ( !m_countInit++ )
{
#if 0
wxAppTraits *traits = wxAppConsole::GetInstance() ?
wxAppConsole::GetInstance()->GetTraits() : NULL;
GSocketGUIFunctionsTable *functions =
traits ? traits->GetSocketGUIFunctionsTable() : NULL;
GSocket_SetGUIFunctions(functions);
if ( !GSocket_Init() )
{
m_countInit--;
return false;
}
#endif
}
return true;
}
void wxSocketBase::Shutdown()
{
// we should be initialized
wxASSERT_MSG( m_countInit, wxT("extra call to Shutdown()") );
if ( !--m_countInit )
{
#if 0
GSocket_Cleanup();
#endif
}
}
// --------------------------------------------------------------------------
// Ctor and dtor
// --------------------------------------------------------------------------
void wxSocketBase::Init()
{
m_socket = NULL;
m_type = wxSOCKET_UNINIT;
// state
m_flags = 0;
m_connected =
m_establishing =
m_reading =
m_writing =
m_error = false;
m_lcount = 0;
m_timeout = 600;
m_beingDeleted = false;
// pushback buffer
m_unread = NULL;
m_unrd_size = 0;
m_unrd_cur = 0;
// events
m_id = -1;
m_handler = NULL;
m_clientData = NULL;
m_notify = false;
m_eventmask = 0;
if ( !IsInitialized() )
{
// this Initialize() will be undone by wxSocketModule::OnExit(), all the
// other calls to it should be matched by a call to Shutdown()
Initialize();
}
}
wxSocketBase::wxSocketBase()
{
Init();
}
wxSocketBase::wxSocketBase( wxSocketFlags flags, wxSocketType type)
{
Init();
m_flags = flags;
m_type = type;
}
wxSocketBase::~wxSocketBase()
{
// Just in case the app called Destroy() *and* then deleted
// the socket immediately: don't leave dangling pointers.
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( traits )
traits->RemoveFromPendingDelete(this);
// Shutdown and close the socket
if (!m_beingDeleted)
Close();
// Destroy the GSocket object
if (m_socket)
{
GSocket_destroy(m_socket);
}
// Free the pushback buffer
if (m_unread)
free(m_unread);
}
bool wxSocketBase::Destroy()
{
// Delayed destruction: the socket will be deleted during the next
// idle loop iteration. This ensures that all pending events have
// been processed.
m_beingDeleted = true;
// Shutdown and close the socket
Close();
// Supress events from now on
Notify(false);
// schedule this object for deletion
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( traits )
{
// let the traits object decide what to do with us
traits->ScheduleForDestroy(this);
}
else // no app or no traits
{
// in wxBase we might have no app object at all, don't leak memory
delete this;
}
return true;
}
// --------------------------------------------------------------------------
// Basic IO calls
// --------------------------------------------------------------------------
// The following IO operations update m_error and m_lcount:
// {Read, Write, ReadMsg, WriteMsg, Peek, Unread, Discard}
//
// TODO: Should Connect, Accept and AcceptWith update m_error?
bool wxSocketBase::Close()
{
// Interrupt pending waits
InterruptWait();
if (m_socket)
GSocket_Shutdown(m_socket);
m_connected = false;
m_establishing = false;
return true;
}
wxSocketBase& wxSocketBase::Read(void* buffer, wxUint32 nbytes)
{
// Mask read events
m_reading = true;
m_lcount = _Read(buffer, nbytes);
// If in wxSOCKET_WAITALL mode, all bytes should have been read.
if (m_flags & wxSOCKET_WAITALL)
m_error = (m_lcount != nbytes);
else
m_error = (m_lcount == 0);
// Allow read events from now on
m_reading = false;
return *this;
}
wxUint32 wxSocketBase::_Read(void* buffer, wxUint32 nbytes)
{
int total = 0;
// Try the pushback buffer first
total = GetPushback(buffer, nbytes, false);
nbytes -= total;
buffer = (char *)buffer + total;
// Return now in one of the following cases:
// - the socket is invalid,
// - we got all the data,
// - we got *some* data and we are not using wxSOCKET_WAITALL.
if ( !m_socket ||
!nbytes ||
((total != 0) && !(m_flags & wxSOCKET_WAITALL)) )
return total;
// Possible combinations (they are checked in this order)
// wxSOCKET_NOWAIT
// wxSOCKET_WAITALL (with or without wxSOCKET_BLOCK)
// wxSOCKET_BLOCK
// wxSOCKET_NONE
//
int ret;
if (m_flags & wxSOCKET_NOWAIT)
{
GSocket_SetNonBlocking(m_socket, 1);
ret = GSocket_Read(m_socket, (char *)buffer, nbytes);
GSocket_SetNonBlocking(m_socket, 0);
if (ret > 0)
total += ret;
}
else
{
bool more = true;
while (more)
{
if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForRead() )
break;
ret = GSocket_Read(m_socket, (char *)buffer, nbytes);
if (ret > 0)
{
total += ret;
nbytes -= ret;
buffer = (char *)buffer + ret;
}
// If we got here and wxSOCKET_WAITALL is not set, we can leave
// now. Otherwise, wait until we recv all the data or until there
// is an error.
//
more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL));
}
}
return total;
}
wxSocketBase& wxSocketBase::ReadMsg(void* buffer, wxUint32 nbytes)
{
wxUint32 len, len2, sig, total;
bool error;
int old_flags;
struct
{
unsigned char sig[4];
unsigned char len[4];
}
msg;
// Mask read events
m_reading = true;
total = 0;
error = true;
old_flags = m_flags;
SetFlags((m_flags & wxSOCKET_BLOCK) | wxSOCKET_WAITALL);
if (_Read(&msg, sizeof(msg)) != sizeof(msg))
goto exit;
sig = (wxUint32)msg.sig[0];
sig |= (wxUint32)(msg.sig[1] << 8);
sig |= (wxUint32)(msg.sig[2] << 16);
sig |= (wxUint32)(msg.sig[3] << 24);
if (sig != 0xfeeddead)
{
wxLogWarning( wxT("wxSocket: invalid signature in ReadMsg.") );
goto exit;
}
len = (wxUint32)msg.len[0];
len |= (wxUint32)(msg.len[1] << 8);
len |= (wxUint32)(msg.len[2] << 16);
len |= (wxUint32)(msg.len[3] << 24);
if (len > nbytes)
{
len2 = len - nbytes;
len = nbytes;
}
else
len2 = 0;
// Don't attemp to read if the msg was zero bytes long.
if (len)
{
total = _Read(buffer, len);
if (total != len)
goto exit;
}
if (len2)
{
char *discard_buffer = new char[MAX_DISCARD_SIZE];
long discard_len;
// NOTE: discarded bytes don't add to m_lcount.
do
{
discard_len = ((len2 > MAX_DISCARD_SIZE)? MAX_DISCARD_SIZE : len2);
discard_len = _Read(discard_buffer, (wxUint32)discard_len);
len2 -= (wxUint32)discard_len;
}
while ((discard_len > 0) && len2);
delete [] discard_buffer;
if (len2 != 0)
goto exit;
}
if (_Read(&msg, sizeof(msg)) != sizeof(msg))
goto exit;
sig = (wxUint32)msg.sig[0];
sig |= (wxUint32)(msg.sig[1] << 8);
sig |= (wxUint32)(msg.sig[2] << 16);
sig |= (wxUint32)(msg.sig[3] << 24);
if (sig != 0xdeadfeed)
{
wxLogWarning( wxT("wxSocket: invalid signature in ReadMsg.") );
goto exit;
}
// everything was OK
error = false;
exit:
m_error = error;
m_lcount = total;
m_reading = false;
SetFlags(old_flags);
return *this;
}
wxSocketBase& wxSocketBase::Peek(void* buffer, wxUint32 nbytes)
{
// Mask read events
m_reading = true;
m_lcount = _Read(buffer, nbytes);
Pushback(buffer, m_lcount);
// If in wxSOCKET_WAITALL mode, all bytes should have been read.
if (m_flags & wxSOCKET_WAITALL)
m_error = (m_lcount != nbytes);
else
m_error = (m_lcount == 0);
// Allow read events again
m_reading = false;
return *this;
}
wxSocketBase& wxSocketBase::Write(const void *buffer, wxUint32 nbytes)
{
// Mask write events
m_writing = true;
m_lcount = _Write(buffer, nbytes);
// If in wxSOCKET_WAITALL mode, all bytes should have been written.
if (m_flags & wxSOCKET_WAITALL)
m_error = (m_lcount != nbytes);
else
m_error = (m_lcount == 0);
// Allow write events again
m_writing = false;
return *this;
}
wxUint32 wxSocketBase::_Write(const void *buffer, wxUint32 nbytes)
{
wxUint32 total = 0;
// If the socket is invalid or parameters are ill, return immediately
if (!m_socket || !buffer || !nbytes)
return 0;
// Possible combinations (they are checked in this order)
// wxSOCKET_NOWAIT
// wxSOCKET_WAITALL (with or without wxSOCKET_BLOCK)
// wxSOCKET_BLOCK
// wxSOCKET_NONE
//
int ret;
if (m_flags & wxSOCKET_NOWAIT)
{
GSocket_SetNonBlocking(m_socket, 1);
ret = GSocket_Write(m_socket, (const char *)buffer, nbytes);
GSocket_SetNonBlocking(m_socket, 0);
if (ret > 0)
total = ret;
}
else
{
bool more = true;
while (more)
{
if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForWrite() )
break;
ret = GSocket_Write(m_socket, (const char *)buffer, nbytes);
if (ret > 0)
{
total += ret;
nbytes -= ret;
buffer = (const char *)buffer + ret;
}
// If we got here and wxSOCKET_WAITALL is not set, we can leave
// now. Otherwise, wait until we send all the data or until there
// is an error.
//
more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL));
}
}
return total;
}
wxSocketBase& wxSocketBase::WriteMsg(const void *buffer, wxUint32 nbytes)
{
wxUint32 total;
bool error;
struct
{
unsigned char sig[4];
unsigned char len[4];
}
msg;
// Mask write events
m_writing = true;
error = true;
total = 0;
SetFlags((m_flags & wxSOCKET_BLOCK) | wxSOCKET_WAITALL);
msg.sig[0] = (unsigned char) 0xad;
msg.sig[1] = (unsigned char) 0xde;
msg.sig[2] = (unsigned char) 0xed;
msg.sig[3] = (unsigned char) 0xfe;
msg.len[0] = (unsigned char) (nbytes & 0xff);
msg.len[1] = (unsigned char) ((nbytes >> 8) & 0xff);
msg.len[2] = (unsigned char) ((nbytes >> 16) & 0xff);
msg.len[3] = (unsigned char) ((nbytes >> 24) & 0xff);
if (_Write(&msg, sizeof(msg)) < sizeof(msg))
goto exit;
total = _Write(buffer, nbytes);
if (total < nbytes)
goto exit;
msg.sig[0] = (unsigned char) 0xed;
msg.sig[1] = (unsigned char) 0xfe;
msg.sig[2] = (unsigned char) 0xad;
msg.sig[3] = (unsigned char) 0xde;
msg.len[0] = msg.len[1] = msg.len[2] = msg.len[3] = (char) 0;
if ((_Write(&msg, sizeof(msg))) < sizeof(msg))
goto exit;
// everything was OK
error = false;
exit:
m_error = error;
m_lcount = total;
m_writing = false;
return *this;
}
wxSocketBase& wxSocketBase::Unread(const void *buffer, wxUint32 nbytes)
{
if (nbytes != 0)
Pushback(buffer, nbytes);
m_error = false;
m_lcount = nbytes;
return *this;
}
wxSocketBase& wxSocketBase::Discard()
{
char *buffer = new char[MAX_DISCARD_SIZE];
wxUint32 ret;
wxUint32 total = 0;
// Mask read events
m_reading = true;
SetFlags(wxSOCKET_NOWAIT);
do
{
ret = _Read(buffer, MAX_DISCARD_SIZE);
total += ret;
}
while (ret == MAX_DISCARD_SIZE);
delete[] buffer;
m_lcount = total;
m_error = false;
// Allow read events again
m_reading = false;
return *this;
}
// --------------------------------------------------------------------------
// Wait functions
// --------------------------------------------------------------------------
// All Wait functions poll the socket using GSocket_Select() to
// check for the specified combination of conditions, until one
// of these conditions become true, an error occurs, or the
// timeout elapses. The polling loop calls PROCESS_EVENTS(), so
// this won't block the GUI.
bool wxSocketBase::_Wait(long seconds,
long milliseconds,
wxSocketEventFlags flags)
{
GSocketEventFlags result;
long timeout;
// Set this to true to interrupt ongoing waits
m_interrupt = false;
// Check for valid socket
if (!m_socket)
return false;
// Check for valid timeout value.
if (seconds != -1)
timeout = seconds * 1000 + milliseconds;
else
timeout = m_timeout * 1000;
#if !defined(wxUSE_GUI) || !wxUSE_GUI
GSocket_SetTimeout(m_socket, timeout);
#endif
// Wait in an active polling loop.
//
// NOTE: We duplicate some of the code in OnRequest, but this doesn't
// hurt. It has to be here because the (GSocket) event might arrive
// a bit delayed, and it has to be in OnRequest as well because we
// don't know whether the Wait functions are being used.
//
// Do this at least once (important if timeout == 0, when
// we are just polling). Also, if just polling, do not yield.
wxStopWatch chrono;
bool done = false;
while (!done)
{
result = GSocket_Select(m_socket, flags | GSOCK_LOST_FLAG);
// Incoming connection (server) or connection established (client)
if (result & GSOCK_CONNECTION_FLAG)
{
m_connected = true;
m_establishing = false;
return true;
}
// Data available or output buffer ready
if ((result & GSOCK_INPUT_FLAG) || (result & GSOCK_OUTPUT_FLAG))
{
return true;
}
// Connection lost
if (result & GSOCK_LOST_FLAG)
{
m_connected = false;
m_establishing = false;
return (flags & GSOCK_LOST_FLAG) != 0;
}
// Wait more?
if ((!timeout) || (chrono.Time() > timeout) || (m_interrupt))
done = true;
else
PROCESS_EVENTS();
}
return false;
}
bool wxSocketBase::Wait(long seconds, long milliseconds)
{
return _Wait(seconds, milliseconds, GSOCK_INPUT_FLAG |
GSOCK_OUTPUT_FLAG |
GSOCK_CONNECTION_FLAG |
GSOCK_LOST_FLAG);
}
bool wxSocketBase::WaitForRead(long seconds, long milliseconds)
{
// Check pushback buffer before entering _Wait
if (m_unread)
return true;
// Note that GSOCK_INPUT_LOST has to be explicitly passed to
// _Wait becuase of the semantics of WaitForRead: a return
// value of true means that a GSocket_Read call will return
// immediately, not that there is actually data to read.
return _Wait(seconds, milliseconds, GSOCK_INPUT_FLAG | GSOCK_LOST_FLAG);
}
bool wxSocketBase::WaitForWrite(long seconds, long milliseconds)
{
return _Wait(seconds, milliseconds, GSOCK_OUTPUT_FLAG);
}
bool wxSocketBase::WaitForLost(long seconds, long milliseconds)
{
return _Wait(seconds, milliseconds, GSOCK_LOST_FLAG);
}
// --------------------------------------------------------------------------
// Miscellaneous
// --------------------------------------------------------------------------
//
// Get local or peer address
//
bool wxSocketBase::GetPeer(wxSockAddress& addr_man) const
{
GAddress *peer;
if (!m_socket)
return false;
peer = GSocket_GetPeer(m_socket);
// copying a null address would just trigger an assert anyway
if (!peer)
return false;
addr_man.SetAddress(peer);
GAddress_destroy(peer);
return true;
}
bool wxSocketBase::GetLocal(wxSockAddress& addr_man) const
{
#if 0
GAddress *local;
if (!m_socket)
return false;
local = GSocket_GetLocal(m_socket);
addr_man.SetAddress(local);
GAddress_destroy(local);
#endif
return true;
}
//
// Save and restore socket state
//
void wxSocketBase::SaveState()
{
wxSocketState *state;
state = new wxSocketState();
state->m_flags = m_flags;
state->m_notify = m_notify;
state->m_eventmask = m_eventmask;
state->m_clientData = m_clientData;
m_states.Append(state);
}
void wxSocketBase::RestoreState()
{
wxList::compatibility_iterator node;
wxSocketState *state;
node = m_states.GetLast();
if (!node)
return;
state = (wxSocketState *)node->GetData();
m_flags = state->m_flags;
m_notify = state->m_notify;
m_eventmask = state->m_eventmask;
m_clientData = state->m_clientData;
m_states.Erase(node);
delete state;
}
//
// Timeout and flags
//
void wxSocketBase::SetTimeout(long seconds)
{
m_timeout = seconds;
#if 0
if (m_socket)
GSocket_SetTimeout(m_socket, m_timeout * 1000);
#endif
}
void wxSocketBase::SetFlags(wxSocketFlags flags)
{
m_flags = flags;
}
// --------------------------------------------------------------------------
// Event handling
// --------------------------------------------------------------------------
// A note on how events are processed, which is probably the most
// difficult thing to get working right while keeping the same API
// and functionality for all platforms.
//
// When GSocket detects an event, it calls wx_socket_callback, which in
// turn just calls wxSocketBase::OnRequest in the corresponding wxSocket
// object. OnRequest does some housekeeping, and if the event is to be
// propagated to the user, it creates a new wxSocketEvent object and
// posts it. The event is not processed immediately, but delayed with
// AddPendingEvent instead. This is necessary in order to decouple the
// event processing from wx_socket_callback; otherwise, subsequent IO
// calls made from the user event handler would fail, as gtk callbacks
// are not reentrant.
//
// Note that, unlike events, user callbacks (now deprecated) are _not_
// decoupled from wx_socket_callback and thus they suffer from a variety
// of problems. Avoid them where possible and use events instead.
extern "C"
void LINKAGEMODE wx_socket_callback(GSocket * WXUNUSED(socket),
GSocketEvent notification,
char *cdata)
{
wxSocketBase *sckobj = (wxSocketBase *)cdata;
sckobj->OnRequest((wxSocketNotify) notification);
}
void wxSocketBase::OnRequest(wxSocketNotify notification)
{
// NOTE: We duplicate some of the code in _Wait, but this doesn't
// hurt. It has to be here because the (GSocket) event might arrive
// a bit delayed, and it has to be in _Wait as well because we don't
// know whether the Wait functions are being used.
switch (notification)
{
case wxSOCKET_CONNECTION:
m_establishing = false;
m_connected = true;
break;
// If we are in the middle of a R/W operation, do not
// propagate events to users. Also, filter 'late' events
// which are no longer valid.
case wxSOCKET_INPUT:
if (m_reading || !GSocket_Select(m_socket, GSOCK_INPUT_FLAG))
return;
break;
case wxSOCKET_OUTPUT:
if (m_writing || !GSocket_Select(m_socket, GSOCK_OUTPUT_FLAG))
return;
break;
case wxSOCKET_LOST:
m_connected = false;
m_establishing = false;
break;
default:
break;
}
// Schedule the event
wxSocketEventFlags flag = 0;
wxUnusedVar(flag);
switch (notification)
{
case GSOCK_INPUT:
flag = GSOCK_INPUT_FLAG;
break;
case GSOCK_OUTPUT:
flag = GSOCK_OUTPUT_FLAG;
break;
case GSOCK_CONNECTION:
flag = GSOCK_CONNECTION_FLAG;
break;
case GSOCK_LOST:
flag = GSOCK_LOST_FLAG;
break;
default:
wxLogWarning( wxT("wxSocket: unknown event!") );
return;
}
if (((m_eventmask & flag) == flag) && m_notify)
{
if (m_handler)
{
wxSocketEvent event(m_id);
event.m_event = notification;
event.m_clientData = m_clientData;
event.SetEventObject(this);
m_handler->AddPendingEvent(event);
}
}
}
void wxSocketBase::Notify(bool notify)
{
m_notify = notify;
}
void wxSocketBase::SetNotify(wxSocketEventFlags flags)
{
m_eventmask = flags;
}
void wxSocketBase::SetEventHandler(wxEvtHandler& handler, int id)
{
m_handler = &handler;
m_id = id;
}
// --------------------------------------------------------------------------
// Pushback buffer
// --------------------------------------------------------------------------
void wxSocketBase::Pushback(const void *buffer, wxUint32 size)
{
if (!size)
return;
if (m_unread == NULL)
m_unread = malloc(size);
else
{
void *tmp;
tmp = malloc(m_unrd_size + size);
memcpy((char *)tmp + size, m_unread, m_unrd_size);
free(m_unread);
m_unread = tmp;
}
m_unrd_size += size;
memcpy(m_unread, buffer, size);
}
wxUint32 wxSocketBase::GetPushback(void *buffer, wxUint32 size, bool peek)
{
if (!m_unrd_size)
return 0;
if (size > (m_unrd_size-m_unrd_cur))
size = m_unrd_size-m_unrd_cur;
memcpy(buffer, (char *)m_unread + m_unrd_cur, size);
if (!peek)
{
m_unrd_cur += size;
if (m_unrd_size == m_unrd_cur)
{
free(m_unread);
m_unread = NULL;
m_unrd_size = 0;
m_unrd_cur = 0;
}
}
return size;
}
// ==========================================================================
// wxSocketServer
// ==========================================================================
// --------------------------------------------------------------------------
// Ctor
// --------------------------------------------------------------------------
wxSocketServer::wxSocketServer(wxSockAddress& addr_man,
wxSocketFlags flags)
: wxSocketBase(flags, wxSOCKET_SERVER)
{
wxLogTrace( wxTRACE_Socket, wxT("Opening wxSocketServer") );
m_socket = GSocket_new();
if (!m_socket)
{
wxLogTrace( wxTRACE_Socket, wxT("*** GSocket_new failed") );
return;
}
// Setup the socket as server
#if 0
GSocket_SetLocal(m_socket, addr_man.GetAddress());
if (GSocket_SetServer(m_socket) != GSOCK_NOERROR)
{
GSocket_destroy(m_socket);
m_socket = NULL;
wxLogTrace( wxTRACE_Socket, wxT("*** GSocket_SetServer failed") );
return;
}
GSocket_SetTimeout(m_socket, m_timeout * 1000);
GSocket_SetCallback(m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
wx_socket_callback, (char *)this);
#endif
}
// --------------------------------------------------------------------------
// Accept
// --------------------------------------------------------------------------
bool wxSocketServer::AcceptWith(wxSocketBase& sock, bool wait)
{
GSocket *child_socket;
if (!m_socket)
return false;
// If wait == false, then the call should be nonblocking.
// When we are finished, we put the socket to blocking mode
// again.
#if 0
if (!wait)
GSocket_SetNonBlocking(m_socket, 1);
child_socket = GSocket_WaitConnection(m_socket);
if (!wait)
GSocket_SetNonBlocking(m_socket, 0);
if (!child_socket)
return false;
sock.m_type = wxSOCKET_BASE;
sock.m_socket = child_socket;
sock.m_connected = true;
GSocket_SetTimeout(sock.m_socket, sock.m_timeout * 1000);
GSocket_SetCallback(sock.m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
wx_socket_callback, (char *)&sock);
#endif
return true;
}
wxSocketBase *wxSocketServer::Accept(bool wait)
{
wxSocketBase* sock = new wxSocketBase();
sock->SetFlags(m_flags);
if (!AcceptWith(*sock, wait))
{
sock->Destroy();
sock = NULL;
}
return sock;
}
bool wxSocketServer::WaitForAccept(long seconds, long milliseconds)
{
return _Wait(seconds, milliseconds, GSOCK_CONNECTION_FLAG);
}
// ==========================================================================
// wxSocketClient
// ==========================================================================
// --------------------------------------------------------------------------
// Ctor and dtor
// --------------------------------------------------------------------------
wxSocketClient::wxSocketClient(wxSocketFlags flags)
: wxSocketBase(flags, wxSOCKET_CLIENT)
{
}
wxSocketClient::~wxSocketClient()
{
}
// --------------------------------------------------------------------------
// Connect
// --------------------------------------------------------------------------
bool wxSocketClient::Connect(wxSockAddress& addr_man, bool wait)
{
GSocketError err;
if (m_socket)
{
// Shutdown and destroy the socket
Close();
GSocket_destroy(m_socket);
}
m_socket = GSocket_new();
m_connected = false;
m_establishing = false;
if (!m_socket)
return false;
GSocket_SetTimeout(m_socket, m_timeout * 1000);
GSocket_SetCallback(m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
wx_socket_callback, (char *)this);
// If wait == false, then the call should be nonblocking.
// When we are finished, we put the socket to blocking mode
// again.
if (!wait)
GSocket_SetNonBlocking(m_socket, 1);
GSocket_SetPeer(m_socket, addr_man.GetAddress());
err = GSocket_Connect(m_socket, GSOCK_STREAMED);
if (!wait)
GSocket_SetNonBlocking(m_socket, 0);
if (err != GSOCK_NOERROR)
{
if (err == GSOCK_WOULDBLOCK)
m_establishing = true;
return false;
}
m_connected = true;
return true;
}
bool wxSocketClient::WaitOnConnect(long seconds, long milliseconds)
{
if (m_connected) // Already connected
return true;
if (!m_establishing || !m_socket) // No connection in progress
return false;
return _Wait(seconds, milliseconds, GSOCK_CONNECTION_FLAG | GSOCK_LOST_FLAG);
}
// ==========================================================================
// wxDatagramSocket
// ==========================================================================
/* NOTE: experimental stuff - might change */
wxDatagramSocket::wxDatagramSocket( wxSockAddress& addr,
wxSocketFlags flags )
: wxSocketBase( flags, wxSOCKET_DATAGRAM )
{
#if 0
// Create the socket
m_socket = GSocket_new();
if (!m_socket)
return;
// Setup the socket as non connection oriented
GSocket_SetLocal(m_socket, addr.GetAddress());
if( GSocket_SetNonOriented(m_socket) != GSOCK_NOERROR )
{
GSocket_destroy(m_socket);
m_socket = NULL;
return;
}
// Initialize all stuff
m_connected = false;
m_establishing = false;
GSocket_SetTimeout( m_socket, m_timeout );
GSocket_SetCallback( m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG,
wx_socket_callback, (char*)this );
#endif
}
wxDatagramSocket& wxDatagramSocket::RecvFrom( wxSockAddress& addr,
void* buf,
wxUint32 nBytes )
{
Read(buf, nBytes);
GetPeer(addr);
return (*this);
}
wxDatagramSocket& wxDatagramSocket::SendTo( wxSockAddress& addr,
const void* buf,
wxUint32 nBytes )
{
GSocket_SetPeer(m_socket, addr.GetAddress());
Write(buf, nBytes);
return (*this);
}
/*
* -------------------------------------------------------------------------
* GAddress
* -------------------------------------------------------------------------
*/
/* CHECK_ADDRESS verifies that the current address family is either
* GSOCK_NOFAMILY or GSOCK_*family*, and if it is GSOCK_NOFAMILY, it
* initalizes it to be a GSOCK_*family*. In other cases, it returns
* an appropiate error code.
*
* CHECK_ADDRESS_RETVAL does the same but returning 'retval' on error.
*/
#define CHECK_ADDRESS(address, family) \
{ \
if (address->m_family == GSOCK_NOFAMILY) \
if (_GAddress_Init_##family(address) != GSOCK_NOERROR) \
return address->m_error; \
if (address->m_family != GSOCK_##family) \
{ \
address->m_error = GSOCK_INVADDR; \
return GSOCK_INVADDR; \
} \
}
#define CHECK_ADDRESS_RETVAL(address, family, retval) \
{ \
if (address->m_family == GSOCK_NOFAMILY) \
if (_GAddress_Init_##family(address) != GSOCK_NOERROR) \
return retval; \
if (address->m_family != GSOCK_##family) \
{ \
address->m_error = GSOCK_INVADDR; \
return retval; \
} \
}
GAddress *GAddress_new(void)
{
GAddress *address;
if ((address = (GAddress *) malloc(sizeof(GAddress))) == NULL)
return NULL;
address->m_family = GSOCK_NOFAMILY;
address->m_addr = NULL;
address->m_len = 0;
return address;
}
GAddress *GAddress_copy(GAddress *address)
{
GAddress *addr2;
assert(address != NULL);
if ((addr2 = (GAddress *) malloc(sizeof(GAddress))) == NULL)
return NULL;
memcpy(addr2, address, sizeof(GAddress));
if (address->m_addr && address->m_len > 0)
{
addr2->m_addr = (struct sockaddr *)malloc(addr2->m_len);
if (addr2->m_addr == NULL)
{
free(addr2);
return NULL;
}
memcpy(addr2->m_addr, address->m_addr, addr2->m_len);
}
return addr2;
}
void GAddress_destroy(GAddress *address)
{
assert( address != NULL );
if (address->m_addr)
free(address->m_addr);
free(address);
}
void GAddress_SetFamily(GAddress *address, GAddressType type)
{
assert(address != NULL);
address->m_family = type;
}
GAddressType GAddress_GetFamily(GAddress *address)
{
assert( address != NULL );
return address->m_family;
}
GSocketError _GAddress_translate_from(GAddress *address,
struct sockaddr *addr, int len)
{
address->m_realfamily = addr->sa_family;
switch (addr->sa_family)
{
case AF_INET:
address->m_family = GSOCK_INET;
break;
case AF_UNIX:
address->m_family = GSOCK_UNIX;
break;
#ifdef AF_INET6
case AF_INET6:
address->m_family = GSOCK_INET6;
break;
#endif
default:
{
address->m_error = GSOCK_INVOP;
return GSOCK_INVOP;
}
}
if (address->m_addr)
free(address->m_addr);
address->m_len = len;
address->m_addr = (struct sockaddr *)malloc(len);
if (address->m_addr == NULL)
{
address->m_error = GSOCK_MEMERR;
return GSOCK_MEMERR;
}
memcpy(address->m_addr, addr, len);
return GSOCK_NOERROR;
}
GSocketError _GAddress_translate_to(GAddress *address,
struct sockaddr **addr, int *len)
{
if (!address->m_addr)
{
address->m_error = GSOCK_INVADDR;
return GSOCK_INVADDR;
}
*len = address->m_len;
*addr = (struct sockaddr *)malloc(address->m_len);
if (*addr == NULL)
{
address->m_error = GSOCK_MEMERR;
return GSOCK_MEMERR;
}
memcpy(*addr, address->m_addr, address->m_len);
return GSOCK_NOERROR;
}
/*
* -------------------------------------------------------------------------
* Internet address family
* -------------------------------------------------------------------------
*/
GSocketError _GAddress_Init_INET(GAddress *address)
{
address->m_len = sizeof(struct sockaddr_in);
address->m_addr = (struct sockaddr *) malloc(address->m_len);
if (address->m_addr == NULL)
{
address->m_error = GSOCK_MEMERR;
return GSOCK_MEMERR;
}
memset( address->m_addr , 0 , address->m_len ) ;
address->m_family = GSOCK_INET;
address->m_realfamily = PF_INET;
((struct sockaddr_in *)address->m_addr)->sin_family = AF_INET;
((struct sockaddr_in *)address->m_addr)->sin_addr.s_addr = INADDR_ANY;
return GSOCK_NOERROR;
}
GSocketError GAddress_INET_SetHostName(GAddress *address, const char *hostname)
{
struct hostent *he;
struct in_addr *addr;
assert( address != NULL );
CHECK_ADDRESS( address, INET );
addr = &(((struct sockaddr_in *)address->m_addr)->sin_addr);
// If it is a numeric host name, convert it now
#if defined(HAVE_INET_ATON)
if (inet_aton(hostname, addr) == 0)
{
#elif defined(HAVE_INET_ADDR)
if ( (addr->s_addr = inet_addr(hostname)) == -1 )
{
#else
// Use gethostbyname by default
#ifndef __WXMAC__
int val = 1; // VA doesn't like constants in conditional expressions
if (val)
#endif
{
#endif
struct in_addr *array_addr;
// It is a real name, we solve it
if ((he = gethostbyname(hostname)) == NULL)
{
// Reset to invalid address
addr->s_addr = INADDR_NONE;
address->m_error = GSOCK_NOHOST;
return GSOCK_NOHOST;
}
array_addr = (struct in_addr *) *(he->h_addr_list);
addr->s_addr = array_addr[0].s_addr;
}
return GSOCK_NOERROR;
}
GSocketError GAddress_INET_SetAnyAddress(GAddress *address)
{
return GAddress_INET_SetHostAddress(address, INADDR_ANY);
}
GSocketError GAddress_INET_SetHostAddress(GAddress *address,
unsigned long hostaddr)
{
struct in_addr *addr;
assert( address != NULL );
CHECK_ADDRESS( address, INET );
addr = &(((struct sockaddr_in *)address->m_addr)->sin_addr);
addr->s_addr = htonl(hostaddr) ;
return GSOCK_NOERROR;
}
GSocketError GAddress_INET_SetPortName(GAddress *address, const char *port,
const char *protocol)
{
struct servent *se;
struct sockaddr_in *addr;
assert( address != NULL );
CHECK_ADDRESS( address, INET );
if (!port)
{
address->m_error = GSOCK_INVPORT;
return GSOCK_INVPORT;
}
se = getservbyname(port, protocol);
if (!se)
{
// the cast to int suppresses compiler warnings
// about subscript having the type char
if (isdigit((int)port[0]))
{
int port_int;
port_int = atoi(port);
addr = (struct sockaddr_in *)address->m_addr;
addr->sin_port = htons(port_int);
return GSOCK_NOERROR;
}
address->m_error = GSOCK_INVPORT;
return GSOCK_INVPORT;
}
addr = (struct sockaddr_in *)address->m_addr;
addr->sin_port = se->s_port;
return GSOCK_NOERROR;
}
GSocketError GAddress_INET_SetPort(GAddress *address, unsigned short port)
{
struct sockaddr_in *addr;
assert( address != NULL );
CHECK_ADDRESS( address, INET );
addr = (struct sockaddr_in *)address->m_addr;
addr->sin_port = htons(port);
return GSOCK_NOERROR;
}
GSocketError GAddress_INET_GetHostName(GAddress *address, char *hostname, size_t sbuf)
{
struct hostent *he;
char *addr_buf;
struct sockaddr_in *addr;
assert( address != NULL );
CHECK_ADDRESS( address, INET );
addr = (struct sockaddr_in *)address->m_addr;
addr_buf = (char *)&(addr->sin_addr);
he = gethostbyaddr(addr_buf, sizeof(addr->sin_addr), AF_INET);
if (he == NULL)
{
address->m_error = GSOCK_NOHOST;
return GSOCK_NOHOST;
}
strncpy(hostname, he->h_name, sbuf);
return GSOCK_NOERROR;
}
unsigned long GAddress_INET_GetHostAddress(GAddress *address)
{
struct sockaddr_in *addr;
assert( address != NULL );
CHECK_ADDRESS_RETVAL( address, INET, 0 );
addr = (struct sockaddr_in *)address->m_addr;
return ntohl(addr->sin_addr.s_addr) ;
}
unsigned short GAddress_INET_GetPort(GAddress *address)
{
struct sockaddr_in *addr;
assert( address != NULL );
CHECK_ADDRESS_RETVAL( address, INET, 0 );
addr = (struct sockaddr_in *)address->m_addr;
return ntohs(addr->sin_port);
}
/*
* -------------------------------------------------------------------------
* Unix address family
* -------------------------------------------------------------------------
*/
GSocketError _GAddress_Init_UNIX(GAddress *address)
{
address->m_len = sizeof(struct sockaddr_un);
address->m_addr = (struct sockaddr *)malloc(address->m_len);
if (address->m_addr == NULL)
{
address->m_error = GSOCK_MEMERR;
return GSOCK_MEMERR;
}
address->m_family = GSOCK_UNIX;
address->m_realfamily = PF_UNIX;
((struct sockaddr_un *)address->m_addr)->sun_family = AF_UNIX;
((struct sockaddr_un *)address->m_addr)->sun_path[0] = 0;
return GSOCK_NOERROR;
}
#define UNIX_SOCK_PATHLEN (sizeof(addr->sun_path)/sizeof(addr->sun_path[0]))
GSocketError GAddress_UNIX_SetPath(GAddress *address, const char *path)
{
struct sockaddr_un *addr;
assert( address != NULL );
CHECK_ADDRESS( address, UNIX );
addr = ((struct sockaddr_un *)address->m_addr);
strncpy(addr->sun_path, path, UNIX_SOCK_PATHLEN);
addr->sun_path[UNIX_SOCK_PATHLEN - 1] = '\0';
return GSOCK_NOERROR;
}
GSocketError GAddress_UNIX_GetPath(GAddress *address, char *path, size_t sbuf)
{
struct sockaddr_un *addr;
assert( address != NULL );
CHECK_ADDRESS( address, UNIX );
addr = (struct sockaddr_un *)address->m_addr;
strncpy(path, addr->sun_path, sbuf);
return GSOCK_NOERROR;
}
/* Address handling */
/* GSocket_SetLocal:
* GSocket_GetLocal:
* GSocket_SetPeer:
* GSocket_GetPeer:
* Set or get the local or peer address for this socket. The 'set'
* functions return GSOCK_NOERROR on success, an error code otherwise.
* The 'get' functions return a pointer to a GAddress object on success,
* or NULL otherwise, in which case they set the error code of the
* corresponding GSocket.
*
* Error codes:
* GSOCK_INVSOCK - the socket is not valid.
* GSOCK_INVADDR - the address is not valid.
*/
GSocketError GSocket_SetLocal(GSocket *socket, GAddress *address)
{
assert( socket != NULL );
// the socket must be initialized, or it must be a server
if ((socket->m_fd != INVALID_SOCKET && !socket->m_server))
{
socket->m_error = GSOCK_INVSOCK;
return GSOCK_INVSOCK;
}
// check address
if (address == NULL || address->m_family == GSOCK_NOFAMILY)
{
socket->m_error = GSOCK_INVADDR;
return GSOCK_INVADDR;
}
if (socket->m_local)
GAddress_destroy(socket->m_local);
socket->m_local = GAddress_copy(address);
return GSOCK_NOERROR;
}
GSocketError GSocket_SetPeer(GSocket *socket, GAddress *address)
{
assert(socket != NULL);
// check address
if (address == NULL || address->m_family == GSOCK_NOFAMILY)
{
socket->m_error = GSOCK_INVADDR;
return GSOCK_INVADDR;
}
if (socket->m_peer)
GAddress_destroy(socket->m_peer);
socket->m_peer = GAddress_copy(address);
return GSOCK_NOERROR;
}
GAddress *GSocket_GetLocal(GSocket *socket)
{
GAddress *address;
struct sockaddr addr;
socklen_t size = sizeof(addr);
GSocketError err;
assert( socket != NULL );
// try to get it from the m_local var first
if (socket->m_local)
return GAddress_copy(socket->m_local);
// else, if the socket is initialized, try getsockname
if (socket->m_fd == INVALID_SOCKET)
{
socket->m_error = GSOCK_INVSOCK;
return NULL;
}
if (getsockname(socket->m_fd, &addr, (socklen_t *) &size) < 0)
{
socket->m_error = GSOCK_IOERR;
return NULL;
}
// got a valid address from getsockname, create a GAddress object
address = GAddress_new();
if (address == NULL)
{
socket->m_error = GSOCK_MEMERR;
return NULL;
}
err = _GAddress_translate_from(address, &addr, size);
if (err != GSOCK_NOERROR)
{
GAddress_destroy(address);
socket->m_error = err;
return NULL;
}
return address;
}
GAddress *GSocket_GetPeer(GSocket *socket)
{
assert(socket != NULL);
// try to get it from the m_peer var
if (socket->m_peer)
return GAddress_copy(socket->m_peer);
return NULL;
}
GSocket *GSocket_new(void)
{
GSocket *socket;
socket = (GSocket *)malloc(sizeof(GSocket));
if (socket == NULL)
return NULL;
socket->m_fd = INVALID_SOCKET;
for (int i=0;i<GSOCK_MAX_EVENT;i++)
{
socket->m_cbacks[i] = NULL;
}
socket->m_detected = 0;
socket->m_local = NULL;
socket->m_peer = NULL;
socket->m_error = GSOCK_NOERROR;
socket->m_non_blocking = false ;
socket->m_stream = true;
// socket->m_oriented = true;
socket->m_server = false;
socket->m_establishing = false;
socket->m_timeout = 10 * 60 * 1000;
// 10 minutes * 60 sec * 1000 millisec
socket->m_cfSocket = NULL ;
socket->m_runLoopSource = NULL ;
socket->m_readStream = NULL;
socket->m_writeStream = NULL;
return socket ;
}
void GSocket_close(GSocket *socket)
{
if ( socket->m_cfSocket != NULL )
{
if ( socket->m_readStream )
{
CFReadStreamClose(socket->m_readStream);
CFRelease( socket->m_readStream ) ;
socket->m_readStream = NULL ;
}
if ( socket->m_writeStream )
{
CFWriteStreamClose(socket->m_writeStream);
CFRelease( socket->m_writeStream ) ;
socket->m_writeStream = NULL ;
}
CFSocketInvalidate( socket->m_cfSocket ) ;
CFRelease( socket->m_cfSocket ) ;
socket->m_cfSocket = NULL ;
socket->m_fd = INVALID_SOCKET ;
}
}
void GSocket_Shutdown(GSocket *socket)
{
GSocket_close( socket );
// Disable GUI callbacks
for (int evt = 0; evt < GSOCK_MAX_EVENT; evt++)
socket->m_cbacks[evt] = NULL;
socket->m_detected = GSOCK_LOST_FLAG;
}
void GSocket_destroy(GSocket *socket)
{
assert( socket != NULL );
// Check that the socket is really shut down
if (socket->m_fd != INVALID_SOCKET)
GSocket_Shutdown(socket);
// Destroy private addresses
if (socket->m_local)
GAddress_destroy(socket->m_local);
if (socket->m_peer)
GAddress_destroy(socket->m_peer);
// Destroy the socket itself
free(socket);
}
GSocketError GSocket_Connect(GSocket *socket, GSocketStream stream)
{
assert( socket != NULL );
if (socket->m_fd != INVALID_SOCKET)
{
socket->m_error = GSOCK_INVSOCK;
return GSOCK_INVSOCK;
}
if (!socket->m_peer)
{
socket->m_error = GSOCK_INVADDR;
return GSOCK_INVADDR;
}
// Streamed or dgram socket?
socket->m_stream = (stream == GSOCK_STREAMED);
socket->m_oriented = true;
socket->m_server = false;
socket->m_establishing = false;
GSocketError returnErr = GSOCK_NOERROR ;
CFSocketError err ;
CFAllocatorRef alloc = kCFAllocatorDefault ;
CFSocketContext ctx ;
memset( &ctx , 0 , sizeof( ctx ) ) ;
ctx.info = socket ;
socket->m_cfSocket = CFSocketCreate( alloc , socket->m_peer->m_realfamily ,
stream == GSOCK_STREAMED ? SOCK_STREAM : SOCK_DGRAM , 0 ,
kCFSocketReadCallBack | kCFSocketWriteCallBack | kCFSocketConnectCallBack , wxMacCFSocketCallback , &ctx ) ;
_GSocket_Enable(socket, GSOCK_CONNECTION);
socket->m_fd = CFSocketGetNative( socket->m_cfSocket ) ;
CFStreamCreatePairWithSocket ( alloc , socket->m_fd , &socket->m_readStream , &socket->m_writeStream );
if ((socket->m_readStream == NULL) || (socket->m_writeStream == NULL))
{
GSocket_close(socket);
socket->m_error = GSOCK_IOERR;
return GSOCK_IOERR;
}
if ( !CFReadStreamOpen( socket->m_readStream ) || !CFWriteStreamOpen( socket->m_writeStream ) )
{
GSocket_close(socket);
socket->m_error = GSOCK_IOERR;
return GSOCK_IOERR;
}
CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(alloc , socket->m_cfSocket , 0);
CFRunLoopAddSource(CFRunLoopGetCurrent() , rls, kCFRunLoopCommonModes);
CFRelease(rls);
CFDataRef address = CFDataCreateWithBytesNoCopy(alloc, (const UInt8*) socket->m_peer->m_addr, socket->m_peer->m_len , kCFAllocatorNull);
if ( !address )
return GSOCK_MEMERR ;
err = CFSocketConnectToAddress( socket->m_cfSocket , address, socket->m_non_blocking ? -1 : socket->m_timeout / 1000 ) ;
CFRelease(address);
if (err != kCFSocketSuccess)
{
if ( err == kCFSocketTimeout )
{
GSocket_close(socket);
socket->m_error = GSOCK_TIMEDOUT ;
return GSOCK_TIMEDOUT ;
}
// we don't know whether a connect in progress will be issued like this
if ( err != kCFSocketTimeout && socket->m_non_blocking )
{
socket->m_establishing = true;
socket->m_error = GSOCK_WOULDBLOCK;
return GSOCK_WOULDBLOCK;
}
GSocket_close(socket);
socket->m_error = GSOCK_IOERR;
return GSOCK_IOERR;
}
return GSOCK_NOERROR;
}
/* Flags */
/* GSocket_SetNonBlocking:
* Sets the socket to non-blocking mode.
* All IO calls will return immediately.
*/
void GSocket_SetNonBlocking(GSocket *socket, int non_block)
{
assert( socket != NULL );
// GSocket_Debug( ("GSocket_SetNonBlocking: %d\n", (int)non_block) );
socket->m_non_blocking = non_block;
}
/*
* GSocket_SetTimeout:
* Sets the timeout for blocking calls. Time is expressed in
* milliseconds.
*/
void GSocket_SetTimeout(GSocket *socket, unsigned long millisec)
{
assert( socket != NULL );
socket->m_timeout = millisec;
}
/* GSocket_GetError:
* Returns the last error which occurred for this socket. Note that successful
* operations do not clear this back to GSOCK_NOERROR, so use it only
* after an error.
*/
GSocketError GSocket_GetError(GSocket *socket)
{
assert( socket != NULL );
return socket->m_error;
}
/* Callbacks */
/* GSOCK_INPUT:
* There is data to be read in the input buffer. If, after a read
* operation, there is still data available, the callback function will
* be called again.
* GSOCK_OUTPUT:
* The socket is available for writing. That is, the next write call
* won't block. This event is generated only once, when the connection is
* first established, and then only if a call failed with GSOCK_WOULDBLOCK,
* when the output buffer empties again. This means that the app should
* assume that it can write since the first OUTPUT event, and no more
* OUTPUT events will be generated unless an error occurs.
* GSOCK_CONNECTION:
* Connection successfully established, for client sockets, or incoming
* client connection, for server sockets. Wait for this event (also watch
* out for GSOCK_LOST) after you issue a nonblocking GSocket_Connect() call.
* GSOCK_LOST:
* The connection is lost (or a connection request failed); this could
* be due to a failure, or due to the peer closing it gracefully.
*/
/* GSocket_SetCallback:
* Enables the callbacks specified by 'flags'. Note that 'flags'
* may be a combination of flags OR'ed toghether, so the same
* callback function can be made to accept different events.
* The callback function must have the following prototype:
*
* void function(GSocket *socket, GSocketEvent event, char *cdata)
*/
void GSocket_SetCallback(GSocket *socket, GSocketEventFlags flags,
GSocketCallback callback, char *cdata)
{
int count;
assert( socket != NULL );
for (count = 0; count < GSOCK_MAX_EVENT; count++)
{
if ((flags & (1 << count)) != 0)
{
socket->m_cbacks[count] = callback;
socket->m_data[count] = cdata;
}
}
}
/* GSocket_UnsetCallback:
* Disables all callbacks specified by 'flags', which may be a
* combination of flags OR'ed toghether.
*/
void GSocket_UnsetCallback(GSocket *socket, GSocketEventFlags flags)
{
int count;
assert(socket != NULL);
for (count = 0; count < GSOCK_MAX_EVENT; count++)
{
if ((flags & (1 << count)) != 0)
{
socket->m_cbacks[count] = NULL;
socket->m_data[count] = NULL;
}
}
}
#define CALL_CALLBACK(socket, event) { \
_GSocket_Disable(socket, event); \
if (socket->m_cbacks[event]) \
socket->m_cbacks[event](socket, event, socket->m_data[event]); \
}
void _GSocket_Install_Callback(GSocket *socket, GSocketEvent event)
{
int c;
switch (event)
{
case GSOCK_CONNECTION:
if (socket->m_server)
c = kCFSocketReadCallBack;
else
c = kCFSocketConnectCallBack;
break;
case GSOCK_LOST:
case GSOCK_INPUT:
c = kCFSocketReadCallBack;
break;
case GSOCK_OUTPUT:
c = kCFSocketWriteCallBack;
break;
default:
c = 0;
}
CFSocketEnableCallBacks(socket->m_cfSocket, c);
}
void _GSocket_Uninstall_Callback(GSocket *socket, GSocketEvent event)
{
int c;
switch (event)
{
case GSOCK_CONNECTION:
if (socket->m_server)
c = kCFSocketReadCallBack;
else
c = kCFSocketConnectCallBack;
break;
case GSOCK_LOST:
case GSOCK_INPUT:
c = kCFSocketReadCallBack;
break;
case GSOCK_OUTPUT:
c = kCFSocketWriteCallBack;
break;
default:
c = 0;
break;
}
CFSocketDisableCallBacks(socket->m_cfSocket, c);
}
void _GSocket_Enable(GSocket *socket, GSocketEvent event)
{
socket->m_detected &= ~(1 << event);
_GSocket_Install_Callback(socket, event);
}
void _GSocket_Disable(GSocket *socket, GSocketEvent event)
{
socket->m_detected |= (1 << event);
_GSocket_Uninstall_Callback(socket, event);
}
void wxMacCFSocketCallback(CFSocketRef s, CFSocketCallBackType callbackType,
CFDataRef address, const void* data, void* info)
{
GSocket* socket = (GSocket*)info;
switch (callbackType)
{
case kCFSocketConnectCallBack:
if ( data )
{
SInt32 error = *((SInt32*)data) ;
CALL_CALLBACK( socket , GSOCK_LOST ) ;
GSocket_Shutdown(socket);
}
else
{
CALL_CALLBACK( socket , GSOCK_CONNECTION ) ;
}
break;
case kCFSocketReadCallBack:
CALL_CALLBACK( socket , GSOCK_INPUT ) ;
break;
case kCFSocketWriteCallBack:
CALL_CALLBACK( socket , GSOCK_OUTPUT ) ;
break;
default:
break; // We shouldn't get here.
}
}
int GSocket_Read(GSocket *socket, char *buffer, int size)
{
int ret = 0 ;
assert(socket != NULL);
// if ( !CFReadStreamHasBytesAvailable() )
ret = CFReadStreamRead( socket->m_readStream , (UInt8*) buffer , size ) ;
return ret;
}
int GSocket_Write(GSocket *socket, const char *buffer, int size)
{
int ret;
assert(socket != NULL);
ret = CFWriteStreamWrite( socket->m_writeStream , (UInt8*) buffer , size ) ;
return ret;
}
GSocketEventFlags GSocket_Select(GSocket *socket, GSocketEventFlags flags)
{
assert( socket != NULL );
return flags & socket->m_detected;
}
// ==========================================================================
// wxSocketModule
// ==========================================================================
class wxSocketModule : public wxModule
{
public:
virtual bool OnInit()
{
// wxSocketBase will call GSocket_Init() itself when/if needed
return true;
}
virtual void OnExit()
{
if ( wxSocketBase::IsInitialized() )
wxSocketBase::Shutdown();
}
private:
DECLARE_DYNAMIC_CLASS(wxSocketModule)
};
IMPLEMENT_DYNAMIC_CLASS(wxSocketModule, wxModule)
#endif
// wxUSE_SOCKETS