/* -------------------------------------------------------------------------
* Project: GSocket (Generic Socket) for WX
* Name: gsocket.c
- * Authors: Guilhem Lavaux,
- * Guillermo Rodriguez Garcia <guille@iies.es> (maintainer)
+ * Authors: David Elliott (C++ conversion, maintainer)
+ * Guilhem Lavaux,
+ * Guillermo Rodriguez Garcia <guille@iies.es>
* Purpose: GSocket main Unix and OS/2 file
* Licence: The wxWindows licence
* CVSID: $Id$
* -------------------------------------------------------------------------
*/
-#ifndef __GSOCKET_STANDALONE__
-#include "wx/setup.h"
-#endif
+#include "wx/wxprec.h"
+#ifndef WX_PRECOMP
+ #include "wx/app.h"
+ #include "wx/apptrait.h"
+#endif //ndef WX_PRECOMP
#if defined(__VISAGECPP__)
/* Seems to be needed by Visual Age C++, though I don't see how it manages
# define GSocket_Debug(args)
#endif /* __GSOCKET_DEBUG__ */
-/* Table of GUI-related functions. We must call them indirectly because
- * of wxBase and GUI separation: */
-
-static struct GSocketGUIFunctionsTable *gs_gui_functions;
-
-#define USE_GUI() (gs_gui_functions != NULL)
-
-/* Define macros to simplify indirection: */
-#define _GSocket_GUI_Init() \
- if (gs_gui_functions) gs_gui_functions->GUI_Init()
-#define _GSocket_GUI_Cleanup() \
- if (gs_gui_functions) gs_gui_functions->GUI_Cleanup()
-#define _GSocket_GUI_Init_Socket(socket) \
- (gs_gui_functions ? gs_gui_functions->GUI_Init_Socket(socket) : 1)
-#define _GSocket_GUI_Destroy_Socket(socket) \
- if (gs_gui_functions) gs_gui_functions->GUI_Destroy_Socket(socket)
-#define _GSocket_Enable_Events(socket) \
- if (gs_gui_functions) gs_gui_functions->Enable_Events(socket)
-#define _GSocket_Disable_Events(socket) \
- if (gs_gui_functions) gs_gui_functions->Disable_Events(socket)
-#define _GSocket_Install_Callback(socket, event) \
- if (gs_gui_functions) gs_gui_functions->Install_Callback(socket, event)
-#define _GSocket_Uninstall_Callback(socket, event) \
- if (gs_gui_functions) gs_gui_functions->Uninstall_Callback(socket, event)
+///////////////////////////////////////////////////////////////////////////
+// GSocketBSDGUIShim
+struct GSocketGUIFunctionsTable *GSocketBSDGUIShim::ms_gui_functions = NULL;
+
+void GSocket_SetGUIFunctions(struct GSocketGUIFunctionsTable *guifunc)
+{
+ GSocketBSDGUIShim::ms_gui_functions = guifunc;
+}
+
+inline bool GSocketBSDGUIShim::UseGUI()
+{
+ return ms_gui_functions;
+}
+
+inline bool GSocketBSDGUIShim::GUI_Init()
+{
+ return (ms_gui_functions)?ms_gui_functions->GUI_Init():true;
+}
+
+inline void GSocketBSDGUIShim::GUI_Cleanup()
+{
+ if (ms_gui_functions) ms_gui_functions->GUI_Cleanup();
+}
+
+GSocketBSDGUIShim::GSocketBSDGUIShim()
+{
+ m_ok = (ms_gui_functions ? ms_gui_functions->GUI_Init_Socket(this) : true);
+}
+
+GSocketBSDGUIShim::~GSocketBSDGUIShim()
+{
+ if (ms_gui_functions) ms_gui_functions->GUI_Destroy_Socket(this);
+}
+
+void GSocketBSDGUIShim::EventLoop_Enable_Events()
+{
+ if (ms_gui_functions) ms_gui_functions->Enable_Events(this);
+}
+
+void GSocketBSDGUIShim::EventLoop_Disable_Events()
+{
+ if (ms_gui_functions) ms_gui_functions->Disable_Events(this);
+}
+
+void GSocketBSDGUIShim::EventLoop_Install_Callback(GSocketEvent event)
+{
+ if (ms_gui_functions) ms_gui_functions->Install_Callback(this, event);
+}
+
+void GSocketBSDGUIShim::EventLoop_Uninstall_Callback(GSocketEvent event)
+{
+ if (ms_gui_functions) ms_gui_functions->Uninstall_Callback(this, event);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// GSocketBSD
static struct GSocketBaseFunctionsTable gs_base_functions =
{
/* Global initialisers */
-void GSocket_SetGUIFunctions(struct GSocketGUIFunctionsTable *guifunc)
-{
- gs_gui_functions = guifunc;
-}
-
int GSocket_Init(void)
{
- if (gs_gui_functions)
- {
- if ( !gs_gui_functions->GUI_Init() )
- return 0;
- }
- return 1;
+ return GSocketBSDGUIShim::GUI_Init();
}
void GSocket_Cleanup(void)
{
- if (gs_gui_functions)
- {
- gs_gui_functions->GUI_Cleanup();
- }
+ GSocketBSDGUIShim::GUI_Cleanup();
}
GSocketBSD::GSocketBSD()
m_functions = &gs_base_functions;
- /* Per-socket GUI-specific initialization */
- m_ok = _GSocket_GUI_Init_Socket(this);
+ m_ok = true;
}
void GSocketBSD::Close()
{
- _GSocket_Disable_Events(this);
+ EventLoop_Disable_Events();
// gsockosx.c calls CFSocketInvalidate which closes the socket for us
#if !(defined(__DARWIN__) && (defined(__WXMAC__) || defined(__WXCOCOA__)))
close(m_fd);
GSocketBSD::~GSocketBSD()
{
- assert(this);
-
- /* Check that the socket is really shutdowned */
- if (m_fd != INVALID_SOCKET)
- Shutdown();
-
- /* Per-socket GUI-specific cleanup */
- _GSocket_GUI_Destroy_Socket(this);
-
/* Destroy private addresses */
if (m_local)
GAddress_destroy(m_local);
* Sets up this socket as a server. The local address must have been
* set with GSocket_SetLocal() before GSocket_SetServer() is called.
* Returns GSOCK_NOERROR on success, one of the following otherwise:
- *
+ *
* Error codes:
* GSOCK_INVSOCK - the socket is in use.
* GSOCK_INVADDR - the local address has not been set.
- * GSOCK_IOERR - low-level error.
+ * GSOCK_IOERR - low-level error.
*/
GSocketError GSocketBSD::SetServer()
{
/* Initialize all fields */
m_stream = TRUE;
m_server = TRUE;
- m_oriented = TRUE;
/* Create the socket */
m_fd = socket(m_local->m_realfamily, SOCK_STREAM, 0);
#else
ioctl(m_fd, FIONBIO, &arg);
#endif
- _GSocket_Enable_Events(this);
+ EventLoop_Enable_Events();
/* allow a socket to re-bind if the socket is in the TIME_WAIT
state after being previously closed.
*/
- setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&arg, sizeof(u_long));
+ if(m_reusable)
+ setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&arg, sizeof(u_long));
/* Bind to the local address,
* retrieve the actual address bound,
* GSOCK_TIMEDOUT - timeout, no incoming connections.
* GSOCK_WOULDBLOCK - the call would block and the socket is nonblocking.
* GSOCK_MEMERR - couldn't allocate memory.
- * GSOCK_IOERR - low-level error.
+ * GSOCK_IOERR - low-level error.
*/
GSocket *GSocketBSD::WaitConnection()
{
}
/* Create a GSocket object for the new connection */
- connection = new GSocketBSD();
+ connection = GSocket_new();
- if (!connection->IsOk())
+ if (!connection)
{
delete connection;
m_error = GSOCK_MEMERR;
/* Initialize all fields */
connection->m_server = FALSE;
connection->m_stream = TRUE;
- connection->m_oriented = TRUE;
/* Setup the peer address field */
connection->m_peer = GAddress_new();
#else
ioctl(connection->m_fd, FIONBIO, &arg);
#endif
- _GSocket_Enable_Events(connection);
+ connection->EventLoop_Enable_Events();
return connection;
}
+int GSocketBSD::SetReusable()
+{
+ /* socket must not be null, and must not be in use/already bound */
+ if (NULL != this && m_fd == INVALID_SOCKET) {
+ m_reusable = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
/* Client specific parts */
/* GSocket_Connect:
* GSOCK_TIMEDOUT - timeout, the connection failed.
* GSOCK_WOULDBLOCK - connection in progress (nonblocking sockets only)
* GSOCK_MEMERR - couldn't allocate memory.
- * GSOCK_IOERR - low-level error.
+ * GSOCK_IOERR - low-level error.
*/
GSocketError GSocketBSD::Connect(GSocketStream stream)
{
/* Streamed or dgram socket? */
m_stream = (stream == GSOCK_STREAMED);
- m_oriented = TRUE;
m_server = FALSE;
m_establishing = FALSE;
#else
ioctl(m_fd, FIONBIO, &arg);
#endif
- _GSocket_Enable_Events(this);
+ EventLoop_Enable_Events();
/* Connect it to the peer address, with a timeout (see below) */
ret = connect(m_fd, m_peer->m_addr, m_peer->m_len);
/* Initialize all fields */
m_stream = FALSE;
m_server = FALSE;
- m_oriented = FALSE;
/* Create the socket */
m_fd = socket(m_local->m_realfamily, SOCK_DGRAM, 0);
#else
ioctl(m_fd, FIONBIO, &arg);
#endif
- _GSocket_Enable_Events(this);
+ EventLoop_Enable_Events();
/* Bind to the local address,
* and retrieve the actual address bound.
assert(this);
- /* When using CFSocket we MUST NOT reenable events until we finish reading */
-#ifndef __DARWIN__
- /* Reenable INPUT events */
- Enable(GSOCK_INPUT);
-#endif
-
if (m_fd == INVALID_SOCKET || m_server)
{
m_error = GSOCK_INVSOCK;
return -1;
}
+ /* Disable events during query of socket status */
+ Disable(GSOCK_INPUT);
+
/* If the socket is blocking, wait for data (with a timeout) */
if (Input_Timeout() == GSOCK_TIMEDOUT)
- return -1;
+ /* We no longer return here immediately, otherwise socket events would not be re-enabled! */
+ ret = -1;
+ else {
+ /* Read the data */
+ if (m_stream)
+ ret = Recv_Stream(buffer, size);
+ else
+ ret = Recv_Dgram(buffer, size);
+ }
- /* Read the data */
- if (m_stream)
- ret = Recv_Stream(buffer, size);
- else
- ret = Recv_Dgram(buffer, size);
-
if (ret == -1)
{
if (errno == EWOULDBLOCK)
else
m_error = GSOCK_IOERR;
}
-
-#ifdef __DARWIN__
- /* Reenable INPUT events */
+
+ /* Enable events again now that we are done processing */
Enable(GSOCK_INPUT);
-#endif
return ret;
}
int GSocketBSD::Write(const char *buffer, int size)
-{
+{
int ret;
assert(this);
-
+
GSocket_Debug(( "GSocket_Write #1, size %d\n", size ));
if (m_fd == INVALID_SOCKET || m_server)
ret = Send_Stream(buffer, size);
else
ret = Send_Dgram(buffer, size);
-
+
GSocket_Debug(( "GSocket_Write #4, size %d\n", size ));
if (ret == -1)
Enable(GSOCK_OUTPUT);
return -1;
}
-
+
GSocket_Debug(( "GSocket_Write #5, size %d ret %d\n", size, ret ));
return ret;
*/
GSocketEventFlags GSocketBSD::Select(GSocketEventFlags flags)
{
- if (!USE_GUI())
+ if (!GSocketBSDGUIShim::UseGUI())
{
GSocketEventFlags result = 0;
fd_set exceptfds;
struct timeval tv;
+ assert(this);
+
/* Do not use a static struct, Linux can garble it */
tv.tv_sec = m_timeout / 1000;
tv.tv_usec = (m_timeout % 1000) / 1000;
- assert(this);
-
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_SET(m_fd, &readfds);
- if (flags & GSOCK_OUTPUT_FLAG)
+ if (flags & GSOCK_OUTPUT_FLAG || flags & GSOCK_CONNECTION_FLAG)
FD_SET(m_fd, &writefds);
FD_SET(m_fd, &exceptfds);
{
m_detected = GSOCK_LOST_FLAG;
m_establishing = FALSE;
-
+
/* LOST event: Abort any further processing */
return (GSOCK_LOST_FLAG & flags);
}
* 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
+ * 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
}
+GSocketError GSocketBSD::GetSockOpt(int level, int optname,
+ void *optval, int *optlen)
+{
+ if (getsockopt(m_fd, level, optname, optval, optlen) == 0)
+ {
+ return GSOCK_NOERROR;
+ }
+ return GSOCK_OPTERR;
+}
+
+GSocketError GSocketBSD::SetSockOpt(int level, int optname,
+ const void *optval, int optlen)
+{
+ if (setsockopt(m_fd, level, optname, optval, optlen) == 0)
+ {
+ return GSOCK_NOERROR;
+ }
+ return GSOCK_OPTERR;
+}
+
#define CALL_CALLBACK(event) { \
Disable(event); \
if (m_cbacks[event]) \
void GSocketBSD::Enable(GSocketEvent event)
{
m_detected &= ~(1 << event);
- _GSocket_Install_Callback(this, event);
+ EventLoop_Install_Callback(event);
}
void GSocketBSD::Disable(GSocketEvent event)
{
m_detected |= (1 << event);
- _GSocket_Uninstall_Callback(this, event);
+ EventLoop_Uninstall_Callback(event);
}
/* _GSocket_Input_Timeout:
/* Compatibility functions for GSocket */
GSocket *GSocket_new(void)
{
- GSocket *newsocket = new GSocketBSD();
+ GSocket *newsocket = new GSocketBSDGUIShim();
if(newsocket->IsOk())
return newsocket;
delete newsocket;
void GSocket_destroy(GSocket *socket)
{
+ assert(socket);
+
+ /* Check that the socket is really shutdowned */
+ if (socket->m_fd != INVALID_SOCKET)
+ socket->Shutdown();
+
delete socket;
}
{
#else
/* Use gethostbyname by default */
+#ifndef __WXMAC__
int val = 1; //VA doesn't like constants in conditional expressions at all
if (val)
+#endif
{
#endif
struct in_addr *array_addr;
CHECK_ADDRESS(address, INET);
addr = &(((struct sockaddr_in *)address->m_addr)->sin_addr);
- addr->s_addr = hostaddr;
+ addr->s_addr = htonl(hostaddr);
return GSOCK_NOERROR;
}
address->m_error = GSOCK_INVPORT;
return GSOCK_INVPORT;
}
-
+
se = getservbyname(port, protocol);
if (!se)
{
assert(address != NULL);
CHECK_ADDRESS(address, INET);
-
+
addr = (struct sockaddr_in *)address->m_addr;
addr->sin_port = htons(port);
char *addr_buf;
struct sockaddr_in *addr;
- assert(address != NULL);
+ assert(address != NULL);
CHECK_ADDRESS(address, INET);
addr = (struct sockaddr_in *)address->m_addr;
{
struct sockaddr_in *addr;
- assert(address != NULL);
- CHECK_ADDRESS_RETVAL(address, INET, 0);
+ assert(address != NULL);
+ CHECK_ADDRESS_RETVAL(address, INET, 0);
addr = (struct sockaddr_in *)address->m_addr;
- return addr->sin_addr.s_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);
+ assert(address != NULL);
+ CHECK_ADDRESS_RETVAL(address, INET, 0);
addr = (struct sockaddr_in *)address->m_addr;
return ntohs(addr->sin_port);
{
struct sockaddr_un *addr;
- assert(address != NULL);
+ assert(address != NULL);
- CHECK_ADDRESS(address, UNIX);
+ CHECK_ADDRESS(address, UNIX);
addr = ((struct sockaddr_un *)address->m_addr);
strncpy(addr->sun_path, path, UNIX_SOCK_PATHLEN);