git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@39285 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2307 lines
55 KiB
C++
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
|