* -------------------------------------------------------------------------
*/
-#if defined(__WATCOMC__)
#include "wx/wxprec.h"
-#include <errno.h>
-#include <nerrno.h>
-#endif
-#ifndef __GSOCKET_STANDALONE__
-#include "wx/defs.h"
+#if wxUSE_SOCKETS
+
+#include "wx/gsocket.h"
+
+#include "wx/private/fd.h"
+#include "wx/private/socket.h"
#include "wx/private/gsocketiohandler.h"
-#include "wx/thread.h" // for wxThread::IsMain() used in assert
-#endif
#if defined(__VISAGECPP__)
#define BSD_SELECT /* use Berkeley Sockets select */
#endif
-#if wxUSE_SOCKETS || defined(__GSOCKET_STANDALONE__)
+#if defined(__WATCOMC__)
+#include <errno.h>
+#include <nerrno.h>
+#endif
#include <assert.h>
#include <sys/types.h>
#define SOCKOPTLEN_T WX_SOCKLEN_T
#endif
-/*
- * MSW defines this, Unices don't.
- */
-#ifndef INVALID_SOCKET
-#define INVALID_SOCKET -1
-#endif
-
/* UnixWare reportedly needs this for FIONBIO definition */
#ifdef __UNIXWARE__
#include <sys/filio.h>
# define GSOCKET_MSG_NOSIGNAL 0
#endif /* MSG_NOSIGNAL */
-#ifndef __GSOCKET_STANDALONE__
-# include "wx/unix/gsockunx.h"
-# include "wx/unix/private.h"
-# include "wx/gsocket.h"
#if wxUSE_THREADS && (defined(HAVE_GETHOSTBYNAME) || defined(HAVE_GETSERVBYNAME))
# include "wx/thread.h"
#endif
-#else
-# include "gsockunx.h"
-# include "gsocket.h"
-# ifndef WXUNUSED
-# define WXUNUSED(x)
-# endif
-#endif /* __GSOCKET_STANDALONE__ */
#if defined(HAVE_GETHOSTBYNAME)
static struct hostent * deepCopyHostent(struct hostent *h,
# define GSocket_Debug(args)
#endif /* __GSOCKET_DEBUG__ */
-#if wxUSE_IPV6
-typedef struct sockaddr_storage wxSockAddr;
-#else
-typedef struct sockaddr wxSockAddr;
-#endif
-
/* Table of GUI-related functions. We must call them indirectly because
* of wxBase and GUI separation: */
/* Constructors / Destructors for GSocket */
-GSocket::GSocket()
+GSocket::GSocket(wxSocketBase& wxsocket)
+ : GSocketBase(wxsocket)
{
- int i;
-
- m_fd = INVALID_SOCKET;
m_handler = NULL;
- for (i=0;i<GSOCK_MAX_EVENT;i++)
- {
- m_cbacks[i] = NULL;
- }
- m_detected = 0;
- m_local = NULL;
- m_peer = NULL;
- m_error = GSOCK_NOERROR;
- m_server = false;
- m_stream = true;
m_gui_dependent = NULL;
- m_non_blocking = false;
- m_reusable = false;
- m_broadcast = false;
- m_dobind = true;
- m_timeout = 10*60*1000;
- /* 10 minutes * 60 sec * 1000 millisec */
- m_establishing = false;
m_use_events = false;
- m_initialRecvBufferSize = -1;
- m_initialSendBufferSize = -1;
-
- m_ok = GSocketManager::Get()->Init_Socket(this);
-}
-
-void GSocket::Close()
-{
- if (m_use_events)
- DisableEvents();
-
- /* When running on OS X, the gsockosx implementation of GSocketGUIFunctionsTable
- will close the socket during Disable_Events. However, it will only do this
- if it is being used. That is, it won't do it in a console program. To
- ensure we get the right behavior, we have gsockosx set m_fd = INVALID_SOCKET
- if it has closed the socket which indicates to us (at runtime, instead of
- at compile time as this had been before) that the socket has already
- been closed.
- */
- if(m_fd != INVALID_SOCKET)
- close(m_fd);
- m_fd = INVALID_SOCKET;
}
GSocket::~GSocket()
{
- assert(this);
-
- /* Check that the socket is really shutdowned */
- if (m_fd != INVALID_SOCKET)
- Shutdown();
-
- GSocketManager::Get()->Destroy_Socket(this);
-
delete m_handler;
-
- /* Destroy private addresses */
- if (m_local)
- GAddress_destroy(m_local);
-
- if (m_peer)
- GAddress_destroy(m_peer);
-
}
/* GSocket_Shutdown:
*/
void GSocket::Shutdown()
{
- int evt;
-
- assert(this);
-
- /* Don't allow events to fire after socket has been closed */
- if (m_use_events)
+ /* Don't allow events to fire after socket has been closed */
DisableEvents();
- /* If socket has been created, shutdown it */
- if (m_fd != INVALID_SOCKET)
- {
- shutdown(m_fd, 1);
- Close();
- }
-
- /* Disable GUI callbacks */
- for (evt = 0; evt < GSOCK_MAX_EVENT; evt++)
- m_cbacks[evt] = NULL;
-
- m_detected = GSOCK_LOST_FLAG;
-}
-
-/* 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(GAddress *address)
-{
- assert(this);
-
- /* the socket must be initialized, or it must be a server */
- if ((m_fd != INVALID_SOCKET && !m_server))
- {
- m_error = GSOCK_INVSOCK;
- return GSOCK_INVSOCK;
- }
-
- /* check address */
- if (address == NULL || address->m_family == GSOCK_NOFAMILY)
- {
- m_error = GSOCK_INVADDR;
- return GSOCK_INVADDR;
- }
-
- if (m_local)
- GAddress_destroy(m_local);
-
- m_local = GAddress_copy(address);
-
- return GSOCK_NOERROR;
-}
-
-GSocketError GSocket::SetPeer(GAddress *address)
-{
- assert(this);
-
- /* check address */
- if (address == NULL || address->m_family == GSOCK_NOFAMILY)
- {
- m_error = GSOCK_INVADDR;
- return GSOCK_INVADDR;
- }
-
- if (m_peer)
- GAddress_destroy(m_peer);
-
- m_peer = GAddress_copy(address);
-
- return GSOCK_NOERROR;
-}
-
-GAddress *GSocket::GetLocal()
-{
- GAddress *address;
- wxSockAddr addr;
- WX_SOCKLEN_T size = sizeof(addr);
- GSocketError err;
-
- assert(this);
-
- /* try to get it from the m_local var first */
- if (m_local)
- return GAddress_copy(m_local);
-
- /* else, if the socket is initialized, try getsockname */
- if (m_fd == INVALID_SOCKET)
- {
- m_error = GSOCK_INVSOCK;
- return NULL;
- }
-
- if (getsockname(m_fd, (sockaddr*)&addr, (WX_SOCKLEN_T *) &size) < 0)
- {
- m_error = GSOCK_IOERR;
- return NULL;
- }
-
- /* got a valid address from getsockname, create a GAddress object */
- address = GAddress_new();
- if (address == NULL)
- {
- m_error = GSOCK_MEMERR;
- return NULL;
- }
-
- err = _GAddress_translate_from(address, (sockaddr*)&addr, size);
- if (err != GSOCK_NOERROR)
- {
- GAddress_destroy(address);
- m_error = err;
- return NULL;
- }
-
- return address;
-}
-
-GAddress *GSocket::GetPeer()
-{
- assert(this);
-
- /* try to get it from the m_peer var */
- if (m_peer)
- return GAddress_copy(m_peer);
-
- return NULL;
+ GSocketBase::Shutdown();
}
/* Server specific parts */
#endif
ioctl(m_fd, FIONBIO, &arg);
- if (m_use_events)
- EnableEvents();
+ EnableEvents();
/* allow a socket to re-bind if the socket is in the TIME_WAIT
state after being previously closed.
* GSOCK_MEMERR - couldn't allocate memory.
* GSOCK_IOERR - low-level error.
*/
-GSocket *GSocket::WaitConnection()
+GSocket *GSocket::WaitConnection(wxSocketBase& wxsocket)
{
wxSockAddr from;
WX_SOCKLEN_T fromlen = sizeof(from);
}
/* Create a GSocket object for the new connection */
- connection = GSocket_new();
+ connection = GSocket::Create(wxsocket);
if (!connection)
{
connection->m_fd = accept(m_fd, (sockaddr*)&from, (WX_SOCKLEN_T *) &fromlen);
/* Reenable CONNECTION events */
- Enable(GSOCK_CONNECTION);
+ EnableEvent(GSOCK_CONNECTION);
if (connection->m_fd == INVALID_SOCKET)
{
{
if (flag == m_use_events)
return;
-#if wxUSE_THREADS
- // it is not safe to attach or detach i/o descriptor in child thread
- wxASSERT_MSG( wxThread::IsMain(), "should be called in main thread only" );
-#endif
m_use_events = flag;
- EnableEvents(flag);
+ DoEnableEvents(flag);
}
-void GSocket::EnableEvents(bool flag)
+void GSocket::DoEnableEvents(bool flag)
{
- if (flag)
- GSocketManager::Get()->Enable_Events(this);
- else
- GSocketManager::Get()->Disable_Events(this);
+ GSocketManager * const manager = GSocketManager::Get();
+ if ( flag )
+ {
+ manager->Install_Callback(this, GSOCK_INPUT);
+ manager->Install_Callback(this, GSOCK_OUTPUT);
+ }
+ else // off
+ {
+ manager->Uninstall_Callback(this, GSOCK_INPUT);
+ manager->Uninstall_Callback(this, GSOCK_OUTPUT);
+ }
}
bool GSocket::SetReusable()
assert(this);
/* Enable CONNECTION events (needed for nonblocking connections) */
- Enable(GSOCK_CONNECTION);
+ EnableEvent(GSOCK_CONNECTION);
if (m_fd != INVALID_SOCKET)
{
/* Connect it to the peer address, with a timeout (see below) */
ret = connect(m_fd, m_peer->m_addr, m_peer->m_len);
- /* We only call Enable_Events if we know we aren't shutting down the socket.
- * NB: Enable_Events needs to be called whether the socket is blocking or
+ /* We only call EnableEvents() if we know we aren't shutting down the socket.
+ * NB: EnableEvents() needs to be called whether the socket is blocking or
* non-blocking, it just shouldn't be called prior to knowing there is a
* connection _if_ blocking sockets are being used.
* If connect above returns 0, we are already connected and need to make the
- * call to Enable_Events now.
+ * call to EnableEvents() now.
*/
-
- if (m_use_events && (m_non_blocking || ret == 0))
+ if ( m_non_blocking || (ret == 0) )
EnableEvents();
if (ret == -1)
SOCKOPTLEN_T len = sizeof(error);
getsockopt(m_fd, SOL_SOCKET, SO_ERROR, (char*) &error, &len);
- if (m_use_events)
- EnableEvents();
+ EnableEvents();
if (!error)
return GSOCK_NOERROR;
#else
ioctl(m_fd, FIONBIO, &arg);
#endif
- if (m_use_events)
- EnableEvents();
+ EnableEvents();
if (m_reusable)
{
}
/* Disable events during query of socket status */
- Disable(GSOCK_INPUT);
+ DisableEvent(GSOCK_INPUT);
/* If the socket is blocking, wait for data (with a timeout) */
if (Input_Timeout() == GSOCK_TIMEDOUT) {
else
ret = Recv_Dgram(buffer, size);
- /* If recv returned zero, then the connection has been gracefully closed.
- * Otherwise, recv has returned an error (-1), in which case we have lost the
- * socket only if errno does _not_ indicate that there may be more data to read.
+ /*
+ * If recv returned zero for a TCP socket (if m_stream == NULL, it's an UDP
+ * socket and empty datagrams are possible), then the connection has been
+ * gracefully closed.
+ *
+ * Otherwise, recv has returned an error (-1), in which case we have lost
+ * the socket only if errno does _not_ indicate that there may be more data
+ * to read.
*/
- if (ret == 0)
+ if ((ret == 0) && m_stream)
{
/* Make sure wxSOCKET_LOST event gets sent and shut down the socket */
if (m_use_events)
}
/* Enable events again now that we are done processing */
- Enable(GSOCK_INPUT);
+ EnableEvent(GSOCK_INPUT);
return ret;
}
* that the socket is writable until a read operation fails. Only then
* will further OUTPUT events be posted.
*/
- Enable(GSOCK_OUTPUT);
+ EnableEvent(GSOCK_OUTPUT);
return -1;
}
return ret;
}
-/* GSocket_Select:
- * Polls the socket to determine its status. This function will
- * check for the events specified in the 'flags' parameter, and
- * it will return a mask indicating which operations can be
- * performed. This function won't block, regardless of the
- * mode (blocking | nonblocking) of the socket.
- */
-GSocketEventFlags GSocket::Select(GSocketEventFlags flags)
-{
- assert(this);
-
- return flags & m_detected;
-}
-
/* Flags */
/* GSocket_SetNonBlocking:
m_non_blocking = non_block;
}
-/* GSocket_SetTimeout:
- * Sets the timeout for blocking calls. Time is expressed in
- * milliseconds.
- */
-void GSocket::SetTimeout(unsigned long millisec)
-{
- assert(this);
-
- m_timeout = millisec;
-}
-
/* GSocket_GetError:
* Returns the last error occurred for this socket. Note that successful
* operations do not clear this back to GSOCK_NOERROR, so use it only
return 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(GSocketEventFlags flags,
- GSocketCallback callback, char *cdata)
-{
- int count;
-
- assert(this);
-
- for (count = 0; count < GSOCK_MAX_EVENT; count++)
- {
- if ((flags & (1 << count)) != 0)
- {
- m_cbacks[count] = callback;
- 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(GSocketEventFlags flags)
-{
- int count;
-
- assert(this);
-
- for (count = 0; count < GSOCK_MAX_EVENT; count++)
- {
- if ((flags & (1 << count)) != 0)
- {
- m_cbacks[count] = NULL;
- m_data[count] = NULL;
- }
- }
-}
-
GSocketError GSocket::GetSockOpt(int level, int optname,
void *optval, int *optlen)
{
return GSOCK_OPTERR;
}
-#define CALL_CALLBACK(socket, event) { \
- socket->Disable(event); \
- if (socket->m_cbacks[event]) \
- socket->m_cbacks[event](socket, event, socket->m_data[event]); \
-}
-
-
-void GSocket::Enable(GSocketEvent event)
+void GSocket::EnableEvent(GSocketEvent event)
{
if (m_use_events)
{
}
}
-void GSocket::Disable(GSocketEvent event)
+void GSocket::DisableEvent(GSocketEvent event)
{
if (m_use_events)
{
*/
GSocketError GSocket::Input_Timeout()
{
- struct timeval tv;
fd_set readfds;
int ret;
- /* Linux select() will overwrite the struct on return */
- tv.tv_sec = (m_timeout / 1000);
- tv.tv_usec = (m_timeout % 1000) * 1000;
+ // Linux select() will overwrite the struct on return so make a copy
+ struct timeval tv = m_timeout;
if (!m_non_blocking)
{
*/
GSocketError GSocket::Output_Timeout()
{
- struct timeval tv;
fd_set writefds;
int ret;
- /* Linux select() will overwrite the struct on return */
- tv.tv_sec = (m_timeout / 1000);
- tv.tv_usec = (m_timeout % 1000) * 1000;
+ // Linux select() will overwrite the struct on return so make a copy
+ struct timeval tv = m_timeout;
GSocket_Debug( ("m_non_blocking has: %d\n", (int)m_non_blocking) );
return ret;
}
+void GSocket::OnStateChange(GSocketEvent event)
+{
+ DisableEvent(event);
+ NotifyOnStateChange(event);
+
+ if ( event == GSOCK_LOST )
+ Shutdown();
+}
+
void GSocket::Detected_Read()
{
char c;
{
m_establishing = false;
- CALL_CALLBACK(this, GSOCK_LOST);
- Shutdown();
+ OnStateChange(GSOCK_LOST);
return;
}
if (num > 0)
{
- CALL_CALLBACK(this, GSOCK_INPUT);
+ OnStateChange(GSOCK_INPUT);
}
else
{
if (m_server && m_stream)
{
- CALL_CALLBACK(this, GSOCK_CONNECTION);
+ OnStateChange(GSOCK_CONNECTION);
}
else if (num == 0)
{
- /* graceful shutdown */
- CALL_CALLBACK(this, GSOCK_LOST);
- Shutdown();
+ if (m_stream)
+ {
+ /* graceful shutdown */
+ OnStateChange(GSOCK_LOST);
+ }
+ else
+ {
+ /* Empty datagram received */
+ OnStateChange(GSOCK_INPUT);
+ }
}
else
{
/* Do not throw a lost event in cases where the socket isn't really lost */
if ((errno == EWOULDBLOCK) || (errno == EAGAIN) || (errno == EINTR))
{
- CALL_CALLBACK(this, GSOCK_INPUT);
+ OnStateChange(GSOCK_INPUT);
}
else
{
- CALL_CALLBACK(this, GSOCK_LOST);
- Shutdown();
+ OnStateChange(GSOCK_LOST);
}
}
}
{
m_establishing = false;
- CALL_CALLBACK(this, GSOCK_LOST);
- Shutdown();
+ OnStateChange(GSOCK_LOST);
return;
}
if (error)
{
- CALL_CALLBACK(this, GSOCK_LOST);
- Shutdown();
+ OnStateChange(GSOCK_LOST);
}
else
{
- CALL_CALLBACK(this, GSOCK_CONNECTION);
+ OnStateChange(GSOCK_CONNECTION);
/* We have to fire this event by hand because CONNECTION (for clients)
* and OUTPUT are internally the same and we just disabled CONNECTION
* events with the above macro.
*/
- CALL_CALLBACK(this, GSOCK_OUTPUT);
+ OnStateChange(GSOCK_OUTPUT);
}
}
else
{
- CALL_CALLBACK(this, GSOCK_OUTPUT);
+ OnStateChange(GSOCK_OUTPUT);
}
}
-/* Compatibility functions for GSocket */
-GSocket *GSocket_new(void)
-{
- GSocket *newsocket = new GSocket();
- if (newsocket->IsOk())
- return newsocket;
-
- delete newsocket;
-
- return NULL;
-}
-
/*
* -------------------------------------------------------------------------
* GAddress
return GSOCK_NOERROR;
}
#endif /* !defined(__VISAGECPP__) */
-#endif /* wxUSE_SOCKETS || defined(__GSOCKET_STANDALONE__) */
+#endif /* wxUSE_SOCKETS */