///////////////////////////////////////////////////////////////////////////// // 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: cfsocket.cpp 40943 2006-08-31 19:31:43Z ABX $ // 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" #include "wx/module.h" #endif #include "wx/apptrait.h" #include "wx/sckaddr.h" #include "wx/mac/carbon/private.h" #include #include #include #include #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;im_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