]> Dogcows Code - chaz/yoink/blobdiff - src/stlplus/portability/ip_sockets.cpp
import stlplus 3.7
[chaz/yoink] / src / stlplus / portability / ip_sockets.cpp
index 0db880ee3752848ec75660a0455baf8df94ce20f..d5d40dedec7b470c49387c1182c81b591e20f615 100644 (file)
-////////////////////////////////////////////////////////////////////////////////
-
-// Author:    Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-//            (c) Andy Rushton           2004-2009
-// License:   BSD License, see ../docs/license.html
-
-// Contains all the platform-specific socket handling used by the TCP and UDP classes
-
-// TODO - any conversion required to support IPv6
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "ip_sockets.hpp"
-#include "dprintf.hpp"
-#include <string.h>
-
-#ifdef MSWINDOWS
-// Windoze-specific includes
-#include <winsock2.h>
-#define ERRNO WSAGetLastError()
-#define HERRNO WSAGetLastError()
-#define IOCTL ioctlsocket
-#define CLOSE closesocket
-#define SHUT_RDWR SD_BOTH
-#define EINPROGRESS WSAEINPROGRESS
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#define ECONNRESET WSAECONNRESET
-#define SOCKLEN_T int
-#else
-// Generic Unix includes
-// fix for older versions of Darwin?
-#define _BSD_SOCKLEN_T_ int
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <netinet/in.h>
-#include <errno.h>
-#include <netdb.h>
-#include <unistd.h>
-#define INVALID_SOCKET -1
-#define ERRNO errno
-#define HERRNO h_errno
-#define SOCKET int
-#define SOCKET_ERROR -1
-#define IOCTL ::ioctl
-#define CLOSE ::close
-#define SOCKLEN_T socklen_t
-#ifdef SOLARIS
-// Sun put some definitions in a different place
-#include <sys/filio.h>
-#endif
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // Utilities
-
-  // get an operating-system error message given an error code
-  static std::string error_string(int error)
-  {
-    std::string result = "error " + dformat("%d",error);
-#ifdef MSWINDOWS
-    char* message = 0;
-    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
-                  0,
-                  error,
-                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // "User default language"
-                  (LPTSTR)&message,
-                  0,0);
-    if (message) 
-    {
-      result = message;
-      LocalFree(message);
-    }
-    // the error message is for some perverse reason newline terminated - remove this
-    if (result[result.size()-1] == '\n')
-      result.erase(result.end()-1);
-    if (result[result.size()-1] == '\r')
-      result.erase(result.end()-1);
-#else
-    char* message = strerror(error);
-    if (message && message[0])
-      result = message;
-#endif
-    return result;
-  }
-
-  // convert address:port into a sockaddr
-  static void convert_address(unsigned long address, unsigned short port, sockaddr& sa)
-  {
-    sa.sa_family = AF_INET;
-    unsigned short network_port = htons(port);
-    memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));
-    unsigned long network_address = htonl(address);
-    memcpy(&sa.sa_data[2], &network_address, sizeof(network_address));
-  }
-
-//   // convert host:port into a sockaddr
-//   static void convert_host(hostent& host, unsigned short port, sockaddr& sa)
-//   {
-//     sa.sa_family = host.h_addrtype;
-//     unsigned short network_port = htons(port);
-//     memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));
-//     memcpy(&sa.sa_data[2], host.h_addr, host.h_length);
-//   }
-
-  // convert sockaddr to address:port
-  static void convert_sockaddr(const sockaddr& sa, unsigned long& address, unsigned short& port)
-  {
-    unsigned short network_port = 0;
-    memcpy(&network_port, &sa.sa_data[0], sizeof(network_port));
-    port = ntohs(network_port);
-    unsigned long network_address = 0;
-    memcpy(&network_address, &sa.sa_data[2], sizeof(network_address));
-    address = ntohl(network_address);
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // Initialisation
-  // Windows requires that Winsock is initialised before use and closed after
-  // These routines initialise once on first use and close on the destruction of the last object using it
-  // on non-windows platforms, I still increment/decrement the sockets count variable for diagnostic purposes
-
-  static int sockets_count = 0;
-
-  static int sockets_init(void)
-  {
-    int error = 0;
-    if (sockets_count++ == 0)
-    {
-#ifdef MSWINDOWS
-      WSAData winsock_info;
-      // request Winsock 2.0 or higher
-      error = WSAStartup(MAKEWORD(2,0),&winsock_info);
-#endif
-    }
-    return error;
-  }
-
-  static int sockets_close(void)
-  {
-    int error = 0;
-    if (--sockets_count == 0)
-    {
-#ifdef MSWINDOWS
-      if (WSACleanup() == SOCKET_ERROR)
-        error = ERRNO;
-#endif
-    }
-    return error;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // Socket Implementation - common code to manipulate a TCP socket
-
-  class IP_socket_internals
-  {
-  private:
-    IP_socket_type m_type;
-    SOCKET m_socket;
-    unsigned long m_remote_address;
-    unsigned short m_remote_port;
-    mutable int m_error;
-    mutable std::string m_message;
-    unsigned m_count;
-
-    // disable copying of the internals
-    IP_socket_internals(const IP_socket_internals&);
-    IP_socket_internals& operator=(const IP_socket_internals&);
-
-  public:
-
-    ////////////////////////////////////////////////////////////////////////////
-    // PIMPL alias counting 
-
-    void increment(void)
-      {
-        ++m_count;
-      }
-
-    bool decrement(void)
-      {
-        --m_count;
-        return m_count == 0;
-      }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // constructors/destructors
-
-    // construct an invalid socket
-    IP_socket_internals(void) : m_type(undefined_socket_type), m_socket(INVALID_SOCKET), m_error(0), m_count(1)
-      {
-        set_error(sockets_init());
-      }
-
-    // close on destroy
-    ~IP_socket_internals(void)
-      {
-        close();
-        set_error(sockets_close());
-      }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // initialisation, connection
-
-    bool initialised(void) const
-      {
-        return m_socket != INVALID_SOCKET;
-      }
-
-    // attach this object to a pre-opened socket
-    bool set(SOCKET socket, unsigned long remote_address, unsigned short remote_port)
-      {
-        if (initialised()) close();
-        clear_error();
-        m_socket = socket;
-        m_remote_address = remote_address;
-        m_remote_port = remote_port;
-        return true;
-      }
-
-    // create a raw socket attached to this object
-    bool initialise(IP_socket_type type)
-      {
-        if (initialised()) close();
-        clear_error();
-        if ((type != TCP) && (type != UDP))
-        {
-          set_error(-1, "Illegal socket type");
-          return false;
-        }
-        // create an anonymous socket
-        m_socket = ::socket(AF_INET, ((type == TCP) ? SOCK_STREAM : SOCK_DGRAM), 0);
-        if (m_socket == INVALID_SOCKET)
-        {
-          set_error(ERRNO);
-          close();
-          return false;
-        }
-        // record the type on success only
-        m_type = type;
-        // set the socket into non-blocking mode
-        unsigned long nonblocking = 1;
-        if (IOCTL(m_socket, FIONBIO, &nonblocking) == SOCKET_ERROR)
-        {
-          set_error(ERRNO);
-          return false;
-        }
-        return true;
-      }
-    
-    // function for performing IP lookup (i.e. gethostbyname)
-    // could be standalone but making it a member means that it can use the socket's error handler
-    // - remote_address: IP name or number
-    // - returns the IP address as a number - zero if there's an error
-    unsigned long ip_lookup(const std::string& remote_address)
-      {
-        unsigned long result = 0;
-        // Lookup the IP address to convert it into a host record
-        // this DOES lookup IP address names as well (not according to MS help !!)
-        // TODO - convert this to use ::getaddrinfo - ::gethostbyname is deprecated
-        hostent* host_info = ::gethostbyname(remote_address.c_str());
-        if (!host_info)
-        {
-          set_error(HERRNO);
-          return 0;
-        }
-        // extract the address from the host info
-        unsigned long network_address = 0;
-        memcpy(&network_address, host_info->h_addr, host_info->h_length);
-        result = ntohl(network_address);
-        return result;
-      }
-
-    // tests whether a socket is ready for communication
-    bool select(bool readable, bool writeable, unsigned wait)
-      {
-        if (!initialised()) return false;
-        // set up the readable set
-        fd_set readable_set;
-        fd_set* readable_set_ptr = 0;
-        if (readable)
-        {
-          FD_ZERO(&readable_set);
-          FD_SET(m_socket,&readable_set);
-          readable_set_ptr = &readable_set;
-        }
-        // set up the writeable set
-        fd_set writeable_set;
-        fd_set* writeable_set_ptr = 0;
-        if (writeable)
-        {
-          FD_ZERO(&writeable_set);
-          FD_SET(m_socket,&writeable_set);
-          writeable_set_ptr = &writeable_set;
-        }
-        // TODO - check the error set and lookup the error?
-        fd_set* error_set_ptr = 0;
-        // set up the timout value
-        // Note: a null pointer implements a blocking select
-        //       a pointer to a zero value implements a zero-wait poll
-        //       a pointer to a positive value implements a poll with a timeout
-        // I currently only implement polling with timeout which may be zero  - no blocking
-        timeval timeout;
-        timeval* timeout_ptr = 0;
-        timeout.tv_sec = wait/1000000;
-        timeout.tv_usec = wait%1000000;
-        timeout_ptr = &timeout;
-        // now test the socket
-        int select_result = ::select(m_socket+1, readable_set_ptr, writeable_set_ptr, error_set_ptr, timeout_ptr);
-        switch(select_result)
-        {
-        case SOCKET_ERROR:
-          // select failed with an error - trap the error
-          set_error(ERRNO);
-          return false;
-        case 0:
-          // timeout exceeded without a connection appearing
-          return false;
-        default:
-          // at least one connection is pending
-          // TODO - do we need to do the extra socket options checking on Posix?
-          // TODO - does this connect in any way to the error_set above?
-          return true;
-        }
-      }
-
-    // bind the socket to a port so that it can receive from specific address
-    bool bind(unsigned long remote_address, unsigned short local_port)
-      {
-        if (!initialised()) return false;
-        // name the socket and bind it to a port - this is a requirement for a server
-        sockaddr server;
-        convert_address(INADDR_ANY, local_port, server);
-        if (::bind(m_socket, &server, sizeof(server)) == SOCKET_ERROR)
-        {
-          set_error(ERRNO);
-          close();
-          return false;
-        }
-        return true;
-      }
-    
-    // bind the socket to a port so that it can receive from any address
-    bool bind_any(unsigned short local_port)
-      {
-        return bind(INADDR_ANY, local_port);
-      }
-
-    // set this socket up to be a listening port
-    // must have been bound to a local port already
-    // - length of backlog queue to manage - may be zero
-    // - returns success status
-    bool listen(unsigned short queue)
-      {
-        if (!initialised()) return false;
-        // set the port to listen for incoming connections
-        if (::listen(m_socket, (int)queue) == SOCKET_ERROR)
-        {
-          set_error(ERRNO);
-          close();
-          return false;
-        }
-        return true;
-      }
-
-    // test whether there's an incoming connection on the socket
-    // only applicable if it has been set up as a listening port
-    bool accept_ready(unsigned wait)
-      {
-        // the test for a connection being ready is the same as the test for whether the socket is readable
-        // see documentation for select
-        return select(true, false, wait);
-      }
-
-    // accept a connection on the socket
-    // only applicable if it has been set up as a listening port
-    // - returns socket filled in with the accepted connection's details - or with the error fields set
-    IP_socket accept(void)
-      {
-        if (!initialised()) return IP_socket();
-        IP_socket result;
-        // accept the connection, at the same time getting the address of the connecting client
-        sockaddr saddress;
-        SOCKLEN_T saddress_length = sizeof(saddress);
-        SOCKET socket = ::accept(m_socket, &saddress, &saddress_length);
-        if (socket == INVALID_SOCKET)
-        {
-          // only set the result socket with an error
-          result.m_impl->set_error(ERRNO);
-          return result;
-        }
-        // extract the contents of the address
-        unsigned long remote_address = 0;
-        unsigned short remote_port = 0;
-        convert_sockaddr(saddress, remote_address, remote_port);
-        result.m_impl->set(socket, remote_address, remote_port);
-        return result;
-      }
-
-    // client connect to a server
-    // - remote_address: IP number of remote address to connect to
-    // - remote_port: port to connect to
-    bool connect(unsigned long remote_address, unsigned short remote_port)
-      {
-        if (!initialised()) return false;
-        // fill in the connection data structure
-        sockaddr connect_data;
-        convert_address(remote_address, remote_port, connect_data);
-        // connect binds the socket to a local address
-        // if connectionless it simply sets the default remote address
-        // if connectioned it makes the connection
-        if (::connect(m_socket, &connect_data, sizeof(connect_data)) == SOCKET_ERROR)
-        {
-          // the socket is non-blocking, so connect will almost certainly fail with EINPROGRESS which is not an error
-          // only catch real errors
-          int error = ERRNO;
-          if (error != EINPROGRESS && error != EWOULDBLOCK)
-          {
-            set_error(error);
-            return false;
-          }
-        }
-        // extract the remote connection details for local storage
-        convert_sockaddr(connect_data, m_remote_address, m_remote_port);
-        return true;
-      }
-
-    // test whether a socket is connected and ready to communicate
-    bool connected(unsigned wait)
-      {
-        if (!initialised()) return false;
-        // Linux and Windows docs say test with select for whether socket is
-        // writable. However, a problem has been reported with Linux whereby
-        // the OS will report a socket as writable when it isn't
-        // first use the select method
-        if (!select(false, true, wait))
-          return false;
-#ifdef MSWINDOWS
-        // Windows needs no further processing - select method works
-        return true;
-#else
-        // Posix version needs further checking using the socket options
-        // DJDM: socket has returned EINPROGRESS on the first attempt at connection
-        // it has also returned that it can be written to
-        // we must now ask it if it has actually connected - using getsockopt
-        int error = 0;
-        socklen_t serror = sizeof(int);
-        if (::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &error, &serror)==0)
-          // handle the error value - one of them means that the socket has connected
-          if (!error || error == EISCONN)
-            return true;
-        return false;
-#endif
-      }
-
-    bool close(void)
-      {
-        bool result = true;
-        if (initialised())
-        {
-          if (shutdown(m_socket,SHUT_RDWR) == SOCKET_ERROR)
-          {
-            set_error(ERRNO);
-            result = false;
-          }
-          if (CLOSE(m_socket) == SOCKET_ERROR)
-          {
-            set_error(ERRNO);
-            result = false;
-          }
-        }
-        m_socket = INVALID_SOCKET;
-        m_remote_address = 0;
-        m_remote_port = 0;
-        return result;
-      }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // sending/receiving
-
-    bool send_ready(unsigned wait)
-      {
-        // determines whether the socket is ready to send by testing whether it is writable
-        return select(false, true, wait);
-      }
-
-    bool send (std::string& data)
-      {
-        if (!initialised()) return false;
-        // send the data - this will never block but may not send all the data
-        int bytes = ::send(m_socket, data.c_str(), data.size(), 0);
-        if (bytes == SOCKET_ERROR)
-        {
-          set_error(ERRNO);
-          return false;
-        }
-        // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent
-        data.erase(0,bytes);
-        return true;
-      }
-
-    bool send_packet(std::string& data, unsigned long address = 0, unsigned short port = 0)
-      {
-        if (!initialised()) return false;
-        // if no address specified, rely on the socket having been connected (can I test this?)
-        // so use the standard send, otherwise use the sendto function
-        int bytes = 0;
-        if (!address)
-        {
-          bytes = ::send(m_socket, data.c_str(), data.size(), 0);
-        }
-        else
-        {
-          sockaddr saddress;
-          convert_address(address, port, saddress);
-          bytes = ::sendto(m_socket, data.c_str(), data.size(), 0, &saddress, sizeof(saddress));
-        }
-        if (bytes == SOCKET_ERROR)
-        {
-          set_error(ERRNO);
-          return false;
-        }
-        // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent
-        data.erase(0,bytes);
-        return true;
-      }
-
-    bool receive_ready(unsigned wait)
-      {
-        // determines whether the socket is ready to receive by testing whether it is readable
-        return select(true, false, wait);
-      }
-
-    bool receive (std::string& data)
-      {
-        if (!initialised()) return false;
-        // determine how much data is available to read
-        unsigned long bytes = 0;
-        if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)
-        {
-          set_error(ERRNO);
-          return false;
-        }
-        // get the data up to the amount claimed to be present - this is non-blocking
-        char* buffer = new char[bytes+1];
-        int read = ::recv(m_socket, buffer, bytes, 0);
-        if (read == SOCKET_ERROR)
-        {
-          delete[] buffer;
-          set_error(ERRNO);
-          close();
-          return false;
-        }
-        if (read == 0)
-        {
-          // TODO - check whether this is an appropriate conditon to close the socket
-          close();
-        }
-        else
-        {
-          // this is binary data so copy the bytes including nulls
-          data.append(buffer,read);
-        }
-        delete[] buffer;
-        return true;
-      }
-
-    bool receive_packet(std::string& data, unsigned long& address, unsigned short& port)
-      {
-        if (!initialised()) return false;
-        // determine how much data is available to read
-        unsigned long bytes = 0;
-        if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)
-        {
-          set_error(ERRNO);
-          return false;
-        }
-        // get the data up to the amount claimed to be present - this is non-blocking
-        // also get the sender's details
-        char* buffer = new char[bytes+1];
-        sockaddr saddress;
-        SOCKLEN_T saddress_length = sizeof(saddress);
-        int read = ::recvfrom(m_socket, buffer, bytes, 0, &saddress, &saddress_length);
-        if (read == SOCKET_ERROR)
-        {
-          // UDP connection reset means that a previous sent failed to deliver cos the address was unknown
-          // this is NOT an error with the sending server socket, which IS still usable
-          int error = ERRNO;
-          if (error != ECONNRESET)
-          {
-            delete[] buffer;
-            set_error(error);
-            close();
-            return false;
-          }
-        }
-        // this is binary data so copy the bytes including nulls
-        data.append(buffer,read);
-        // also retrieve the sender's details
-        convert_sockaddr(saddress, address, port);
-        delete[] buffer;
-        return true;
-      }
-
-    bool receive_packet(std::string& data)
-      {
-        // call the above and then discard the address details
-        unsigned long address = 0;
-        unsigned short port = 0;
-        return receive_packet(data, address, port);
-      }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // informational
-
-    IP_socket_type type(void) const
-      {
-        return m_type;
-      }
-
-    unsigned short local_port(void) const
-      {
-        if (!initialised()) return 0;
-        sockaddr saddress;
-        SOCKLEN_T saddress_length = sizeof(saddress);
-        if (::getsockname(m_socket, &saddress, &saddress_length) != 0)
-        {
-          set_error(ERRNO);
-          return 0;
-        }
-        unsigned long address = 0;
-        unsigned short port = 0;
-        convert_sockaddr(saddress, address, port);
-        return port;
-      }
-
-    unsigned long remote_address(void) const
-      {
-        return m_remote_address;
-      }
-
-    unsigned short remote_port(void) const
-      {
-        return m_remote_port;
-      }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // error handling
-
-    void set_error (int error, const char* message = 0) const
-      {
-        if (error != 0)
-        {
-          m_error = error;
-          if (message && (message[0] != 0))
-            m_message = message;
-          else
-            m_message = error_string(error);
-        }
-      }
-
-    int error(void) const
-      {
-        return m_error;
-      }
-
-    void clear_error (void) const
-      {
-        m_error = 0;
-        m_message.erase();
-      }
-
-    std::string message(void) const
-      {
-        return m_message;
-      }
-
-  };
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // Socket - common code to manipulate a socket
-
-  // create an uninitialised socket
-  IP_socket::IP_socket(void) : m_impl(new IP_socket_internals)
-  {
-  }
-
-  // create an initialised socket
-  // - type: create either a TCP or UDP socket - if neither, creates an uninitialised socket
-  IP_socket::IP_socket(IP_socket_type type) : m_impl(new IP_socket_internals)
-  {
-    initialise(type);
-  }
-
-  // destroy the socket, closing it if open
-  IP_socket::~IP_socket(void)
-  {
-    if (m_impl->decrement())
-      delete m_impl;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////
-  // copying is implemented as aliasing
-
-  IP_socket::IP_socket(const IP_socket& right) : m_impl(0)
-  {
-    // make this an alias of right
-    m_impl = right.m_impl;
-    m_impl->increment();
-  }
-
-  IP_socket& IP_socket::operator=(const IP_socket& right)
-  {
-    // make self-copy safe
-    if (m_impl == right.m_impl) return *this;
-    // first dealias the existing implementation
-    if (m_impl->decrement())
-      delete m_impl;
-    // now make this an alias of right
-    m_impl = right.m_impl;
-    m_impl->increment();
-    return *this;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////
-  // initialisation, connection
-
-  // initialise the socket
-  // - type: create either a TCP or UDP socket
-  // - returns success status
-  bool IP_socket::initialise(IP_socket_type type)
-  {
-    return m_impl->initialise(type);
-  }
-
-  // test whether this is an initialised socket
-  // - returns whether this is initialised
-  bool IP_socket::initialised(void) const
-  {
-    return m_impl->initialised();
-  }
-
-  // close, i.e. disconnect the socket
-  // - returns a success flag
-  bool IP_socket::close(void)
-  {
-    return m_impl->close();
-  }
-
-  // function for performing IP lookup (i.e. gethostbyname)
-  // could be standalone but making it a member means that it can use the socket's error handler
-  // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
-  // - returns the IP address as a long integer - zero if there's an error
-  unsigned long IP_socket::ip_lookup(const std::string& remote_address)
-  {
-    return m_impl->ip_lookup(remote_address);
-  }
-
-  // test whether a socket is ready to communicate
-  // - readable: test whether socket is ready to read
-  // - writeable: test whether a socket is ready to write
-  // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait
-  // returns false if not ready or error - use error() method to tell - true if ready
-  bool IP_socket::select(bool readable, bool writeable, unsigned timeout)
-  {
-    return m_impl->select(readable, writeable, timeout);
-  }
-
-  // bind the socket to a port so that it can receive from specific address - typically used by a client
-  // - remote_address: IP number of remote server to send/receive to/from
-  // - local_port: port on local machine to bind to the address
-  // - returns success flag
-  bool IP_socket::bind(unsigned long remote_address, unsigned short local_port)
-  {
-    return m_impl->bind(remote_address, local_port);
-  }
-
-  // bind the socket to a port so that it can receive from any address - typically used by a server
-  // - local_port: port on local machine to bind to the address
-  // - returns success flag
-  bool IP_socket::bind_any(unsigned short local_port)
-  {
-    return m_impl->bind_any(local_port);
-  }
-
-  // initialise a socket and set this socket up to be a listening port
-  // - queue: length of backlog queue to manage - may be zero
-  // - returns success status
-  bool IP_socket::listen(unsigned short queue)
-  {
-    return m_impl->listen(queue);
-  }
-
-  // test for a connection on the object's socket - only applicable if it has been set up as a listening port
-  // - returns true if a connection is ready to be accepted
-  bool IP_socket::accept_ready(unsigned timeout) const
-  {
-    return m_impl->accept_ready(timeout);
-  }
-
-  // accept a connection on the object's socket - only applicable if it has been set up as a listening port
-  // - returns the connection as a new socket
-  IP_socket IP_socket::accept(void)
-  {
-    return m_impl->accept();
-  }
-
-  // client connect to a server
-  // - address: IP number already lookup up with ip_lookup
-  // - port: port to connect to
-  // - returns a success flag
-  bool IP_socket::connect(unsigned long address, unsigned short port)
-  {
-    return m_impl->connect(address, port);
-  }
-
-  // test whether a socket is connected and ready to communicate, returns on successful connect or timeout
-  // - timeout: how long to wait in microseconds if not connected yet
-  // - returns success flag
-  bool IP_socket::connected(unsigned timeout)
-  {
-    return m_impl->connected(timeout);
-  }
-
-  ////////////////////////////////////////////////////////////////////////////
-  // sending/receiving
-
-  // test whether a socket is connected and ready to send data, returns if ready or on timeout
-  // - timeout: how long to wait in microseconds if not connected yet (blocking)
-  // - returns status
-  bool IP_socket::send_ready(unsigned timeout)
-  {
-    return m_impl->send_ready(timeout);
-  }
-
-  // send data through the socket - if the data is long only part of it may
-  // be sent. The sent part is removed from the data, so the same string can
-  // be sent again and again until it is empty.
-  // - data: string containing data to be sent - any data successfully sent is removed
-  // - returns success flag
-  bool IP_socket::send (std::string& data)
-  {
-    return m_impl->send(data);
-  }
-
-  // send data through a connectionless (UDP) socket
-  // the data will be sent as a single packet
-  // - packet: string containing data to be sent - any data successfully sent is removed
-  // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote
-  // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote
-  // - returns success flag
-  bool IP_socket::send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port)
-  {
-    return m_impl->send_packet(packet, remote_address, remote_port);
-  }
-
-  // send data through a connectionless (UDP) socket
-  // the data will be sent as a single packet
-  // only works if the socket has been connected to remote
-  // - packet: string containing data to be sent - any data successfully sent is removed
-  // - returns success flag
-  bool IP_socket::send_packet(std::string& packet)
-  {
-    return m_impl->send_packet(packet);
-  }
-
-  // test whether a socket is connected and ready to receive data, returns if ready or on timeout
-  // - timeout: how long to wait in microseconds if not connected yet (blocking)
-  // - returns status
-  bool IP_socket::receive_ready(unsigned timeout)
-  {
-    return m_impl->receive_ready(timeout);
-  }
-
-  // receive data through a connection-based (TCP) socket
-  // if the data is long only part of it may be received. The received data
-  // is appended to the string, building it up in stages, so the same string
-  // can be received again and again until all information has been
-  // received.
-  // - data: string receiving data from socket - any data successfully received is appended
-  // - returns success flag
-  bool IP_socket::receive (std::string& data)
-  {
-    return m_impl->receive(data);
-  }
-
-  // receive data through a connectionless (UDP) socket
-  // - packet: string receiving data from socket - any data successfully received is appended
-  // - remote_address: returns the address of the remote host received from
-  // - remote_port: returns the port of the remote host received from
-  // - returns success flag
-  bool IP_socket::receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)
-  {
-    return m_impl->receive_packet(packet, remote_address, remote_port);
-  }
-
-  // variant of above which does not give back the address and port of the sender
-  // receive data through a connectionless (UDP) socket
-  // - packet: string receiving data from socket - any data successfully received is appended
-  // - returns success flag
-  bool IP_socket::receive_packet(std::string& packet)
-  {
-    return m_impl->receive_packet(packet);
-  }
-
-  ////////////////////////////////////////////////////////////////////////////
-  // informational
-
-  IP_socket_type IP_socket::type(void) const
-  {
-    return m_impl->type();
-  }
-
-  unsigned short IP_socket::local_port(void) const
-  {
-    return m_impl->local_port();
-  }
-
-  unsigned long IP_socket::remote_address(void) const
-  {
-    return m_impl->remote_address();
-  }
-
-  unsigned short IP_socket::remote_port(void) const
-  {
-    return m_impl->remote_port();
-  }
-
-  ////////////////////////////////////////////////////////////////////////////
-  // error handling
-
-  void IP_socket::set_error (int error, const std::string& message) const
-  {
-    m_impl->set_error(error, message.c_str());
-  }
-
-  void IP_socket::clear_error (void) const
-  {
-    m_impl->clear_error();
-  }
-
-  int IP_socket::error(void) const
-  {
-    return m_impl->error();
-  }
-
-  std::string IP_socket::message(void) const
-  {
-    return m_impl->message();
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author:    Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+//            (c) Andy Rushton           2004 onwards\r
+// License:   BSD License, see ../docs/license.html\r
+\r
+// Contains all the platform-specific socket handling used by the TCP and UDP classes\r
+\r
+// TODO - any conversion required to support IPv6\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "ip_sockets.hpp"\r
+#include "dprintf.hpp"\r
+#include <string.h>\r
+\r
+#ifdef MSWINDOWS\r
+// Windoze-specific includes\r
+#include <winsock2.h>\r
+#define ERRNO WSAGetLastError()\r
+#define HERRNO WSAGetLastError()\r
+#define IOCTL ioctlsocket\r
+#define CLOSE closesocket\r
+#define SHUT_RDWR SD_BOTH\r
+#define SOCKLEN_T int\r
+#define SEND_FLAGS 0\r
+#if _MSC_VER < 1600 // not defined before Visual Studio 10\r
+#define EINPROGRESS WSAEINPROGRESS\r
+#define EWOULDBLOCK WSAEWOULDBLOCK\r
+#define ECONNRESET WSAECONNRESET\r
+#endif\r
+#else\r
+// Generic Unix includes\r
+// fix for older versions of Darwin?\r
+#define _BSD_SOCKLEN_T_ int\r
+#include <sys/types.h>\r
+#include <sys/socket.h>\r
+#include <sys/ioctl.h>\r
+#include <sys/time.h>\r
+#include <netinet/in.h>\r
+#include <errno.h>\r
+#include <netdb.h>\r
+#include <unistd.h>\r
+#define INVALID_SOCKET -1\r
+#define ERRNO errno\r
+#define HERRNO h_errno\r
+#define SOCKET int\r
+#define SOCKET_ERROR -1\r
+#define IOCTL ::ioctl\r
+#define CLOSE ::close\r
+#define SOCKLEN_T socklen_t\r
+#define SEND_FLAGS MSG_NOSIGNAL\r
+#ifdef SOLARIS\r
+// Sun put some definitions in a different place\r
+#include <sys/filio.h>\r
+#endif\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Utilities\r
+\r
+  // get an operating-system error message given an error code\r
+  static std::string error_string(int error)\r
+  {\r
+    std::string result = "error " + dformat("%d",error);\r
+#ifdef MSWINDOWS\r
+    char* message = 0;\r
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                  0,\r
+                  error,\r
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // "User default language"\r
+                  (LPTSTR)&message,\r
+                  0,0);\r
+    if (message) \r
+    {\r
+      result = message;\r
+      LocalFree(message);\r
+    }\r
+    // the error message is for some perverse reason newline terminated - remove this\r
+    if (result[result.size()-1] == '\n')\r
+      result.erase(result.end()-1);\r
+    if (result[result.size()-1] == '\r')\r
+      result.erase(result.end()-1);\r
+#else\r
+    char* message = strerror(error);\r
+    if (message && message[0])\r
+      result = message;\r
+#endif\r
+    return result;\r
+  }\r
+\r
+  // convert address:port into a sockaddr\r
+  static void convert_address(unsigned long address, unsigned short port, sockaddr& sa)\r
+  {\r
+    sa.sa_family = AF_INET;\r
+    unsigned short network_port = htons(port);\r
+    memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));\r
+    unsigned long network_address = htonl(address);\r
+    memcpy(&sa.sa_data[2], &network_address, sizeof(network_address));\r
+  }\r
+\r
+//   // convert host:port into a sockaddr\r
+//   static void convert_host(hostent& host, unsigned short port, sockaddr& sa)\r
+//   {\r
+//     sa.sa_family = host.h_addrtype;\r
+//     unsigned short network_port = htons(port);\r
+//     memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));\r
+//     memcpy(&sa.sa_data[2], host.h_addr, host.h_length);\r
+//   }\r
+\r
+  // convert sockaddr to address:port\r
+  static void convert_sockaddr(const sockaddr& sa, unsigned long& address, unsigned short& port)\r
+  {\r
+    unsigned short network_port = 0;\r
+    memcpy(&network_port, &sa.sa_data[0], sizeof(network_port));\r
+    port = ntohs(network_port);\r
+    unsigned long network_address = 0;\r
+    memcpy(&network_address, &sa.sa_data[2], sizeof(network_address));\r
+    address = ntohl(network_address);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Initialisation\r
+  // Windows requires that Winsock is initialised before use and closed after\r
+  // These routines initialise once on first use and close on the destruction of the last object using it\r
+  // on non-windows platforms, I still increment/decrement the sockets count variable for diagnostic purposes\r
+\r
+  static int sockets_count = 0;\r
+\r
+  static int sockets_init(void)\r
+  {\r
+    int error = 0;\r
+    if (sockets_count++ == 0)\r
+    {\r
+#ifdef MSWINDOWS\r
+      WSAData winsock_info;\r
+      // request Winsock 2.0 or higher\r
+      error = WSAStartup(MAKEWORD(2,0),&winsock_info);\r
+#endif\r
+    }\r
+    return error;\r
+  }\r
+\r
+  static int sockets_close(void)\r
+  {\r
+    int error = 0;\r
+    if (--sockets_count == 0)\r
+    {\r
+#ifdef MSWINDOWS\r
+      if (WSACleanup() == SOCKET_ERROR)\r
+        error = ERRNO;\r
+#endif\r
+    }\r
+    return error;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Socket Implementation - common code to manipulate a TCP socket\r
+\r
+  class IP_socket_internals\r
+  {\r
+  private:\r
+    IP_socket_type m_type;\r
+    SOCKET m_socket;\r
+    unsigned long m_remote_address;\r
+    unsigned short m_remote_port;\r
+    mutable int m_error;\r
+    mutable std::string m_message;\r
+    unsigned m_count;\r
+\r
+    // disable copying of the internals\r
+    IP_socket_internals(const IP_socket_internals&);\r
+    IP_socket_internals& operator=(const IP_socket_internals&);\r
+\r
+  public:\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // PIMPL alias counting \r
+\r
+    void increment(void)\r
+      {\r
+        ++m_count;\r
+      }\r
+\r
+    bool decrement(void)\r
+      {\r
+        --m_count;\r
+        return m_count == 0;\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // constructors/destructors\r
+\r
+    // construct an invalid socket\r
+    IP_socket_internals(void) : m_type(undefined_socket_type), m_socket(INVALID_SOCKET), m_error(0), m_count(1)\r
+      {\r
+        set_error(sockets_init());\r
+      }\r
+\r
+    // close on destroy\r
+    ~IP_socket_internals(void)\r
+      {\r
+        close();\r
+        set_error(sockets_close());\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation, connection\r
+\r
+    bool initialised(void) const\r
+      {\r
+        return m_socket != INVALID_SOCKET;\r
+      }\r
+\r
+    // attach this object to a pre-opened socket\r
+    bool set(SOCKET socket, unsigned long remote_address, unsigned short remote_port)\r
+      {\r
+        if (initialised()) close();\r
+        clear_error();\r
+        m_socket = socket;\r
+        m_remote_address = remote_address;\r
+        m_remote_port = remote_port;\r
+        return true;\r
+      }\r
+\r
+    // create a raw socket attached to this object\r
+    bool initialise(IP_socket_type type)\r
+      {\r
+        if (initialised()) close();\r
+        clear_error();\r
+        if ((type != TCP) && (type != UDP))\r
+        {\r
+          set_error(-1, "Illegal socket type");\r
+          return false;\r
+        }\r
+        // create an anonymous socket\r
+        m_socket = ::socket(AF_INET, ((type == TCP) ? SOCK_STREAM : SOCK_DGRAM), 0);\r
+        if (m_socket == INVALID_SOCKET)\r
+        {\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        // record the type on success only\r
+        m_type = type;\r
+        // set the socket into non-blocking mode\r
+        unsigned long nonblocking = 1;\r
+        if (IOCTL(m_socket, FIONBIO, &nonblocking) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        return true;\r
+      }\r
+    \r
+    // function for performing IP lookup (i.e. gethostbyname)\r
+    // could be standalone but making it a member means that it can use the socket's error handler\r
+    // - remote_address: IP name or number\r
+    // - returns the IP address as a number - zero if there's an error\r
+    unsigned long ip_lookup(const std::string& remote_address)\r
+      {\r
+        unsigned long result = 0;\r
+        // Lookup the IP address to convert it into a host record\r
+        // this DOES lookup IP address names as well (not according to MS help !!)\r
+        // TODO - convert this to use ::getaddrinfo - ::gethostbyname is deprecated\r
+        hostent* host_info = ::gethostbyname(remote_address.c_str());\r
+        if (!host_info)\r
+        {\r
+          set_error(HERRNO);\r
+          return 0;\r
+        }\r
+        // extract the address from the host info\r
+        unsigned long network_address = 0;\r
+        memcpy(&network_address, host_info->h_addr, host_info->h_length);\r
+        result = ntohl(network_address);\r
+        return result;\r
+      }\r
+\r
+    // tests whether a socket is ready for communication\r
+    bool select(bool readable, bool writeable, unsigned wait)\r
+      {\r
+        if (!initialised()) return false;\r
+        // set up the readable set\r
+        fd_set readable_set;\r
+        fd_set* readable_set_ptr = 0;\r
+        if (readable)\r
+        {\r
+          FD_ZERO(&readable_set);\r
+          FD_SET(m_socket,&readable_set);\r
+          readable_set_ptr = &readable_set;\r
+        }\r
+        // set up the writeable set\r
+        fd_set writeable_set;\r
+        fd_set* writeable_set_ptr = 0;\r
+        if (writeable)\r
+        {\r
+          FD_ZERO(&writeable_set);\r
+          FD_SET(m_socket,&writeable_set);\r
+          writeable_set_ptr = &writeable_set;\r
+        }\r
+        // TODO - check the error set and lookup the error?\r
+        fd_set* error_set_ptr = 0;\r
+        // set up the timout value\r
+        // Note: a null pointer implements a blocking select\r
+        //       a pointer to a zero value implements a zero-wait poll\r
+        //       a pointer to a positive value implements a poll with a timeout\r
+        // I currently only implement polling with timeout which may be zero  - no blocking\r
+        timeval timeout;\r
+        timeval* timeout_ptr = 0;\r
+        timeout.tv_sec = wait/1000000;\r
+        timeout.tv_usec = wait%1000000;\r
+        timeout_ptr = &timeout;\r
+        // now test the socket\r
+        int select_result = ::select(m_socket+1, readable_set_ptr, writeable_set_ptr, error_set_ptr, timeout_ptr);\r
+        switch(select_result)\r
+        {\r
+        case SOCKET_ERROR:\r
+          // select failed with an error - trap the error\r
+          set_error(ERRNO);\r
+          return false;\r
+        case 0:\r
+          // timeout exceeded without a connection appearing\r
+          return false;\r
+        default:\r
+          // at least one connection is pending\r
+          // TODO - do we need to do the extra socket options checking on Posix?\r
+          // TODO - does this connect in any way to the error_set above?\r
+          return true;\r
+        }\r
+      }\r
+\r
+    // bind the socket to a port so that it can receive from specific address\r
+    bool bind(unsigned long remote_address, unsigned short local_port)\r
+      {\r
+        if (!initialised()) return false;\r
+        // name the socket and bind it to a port - this is a requirement for a server\r
+        sockaddr server;\r
+        convert_address(INADDR_ANY, local_port, server);\r
+        if (::bind(m_socket, &server, sizeof(server)) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        return true;\r
+      }\r
+    \r
+    // bind the socket to a port so that it can receive from any address\r
+    bool bind_any(unsigned short local_port)\r
+      {\r
+        return bind(INADDR_ANY, local_port);\r
+      }\r
+\r
+    // set this socket up to be a listening port\r
+    // must have been bound to a local port already\r
+    // - length of backlog queue to manage - may be zero\r
+    // - returns success status\r
+    bool listen(unsigned short queue)\r
+      {\r
+        if (!initialised()) return false;\r
+        // set the port to listen for incoming connections\r
+        if (::listen(m_socket, (int)queue) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        return true;\r
+      }\r
+\r
+    // test whether there's an incoming connection on the socket\r
+    // only applicable if it has been set up as a listening port\r
+    bool accept_ready(unsigned wait)\r
+      {\r
+        // the test for a connection being ready is the same as the test for whether the socket is readable\r
+        // see documentation for select\r
+        return select(true, false, wait);\r
+      }\r
+\r
+    // accept a connection on the socket\r
+    // only applicable if it has been set up as a listening port\r
+    // - returns socket filled in with the accepted connection's details - or with the error fields set\r
+    IP_socket accept(void)\r
+      {\r
+        if (!initialised()) return IP_socket();\r
+        IP_socket result;\r
+        // accept the connection, at the same time getting the address of the connecting client\r
+        sockaddr saddress;\r
+        SOCKLEN_T saddress_length = sizeof(saddress);\r
+        SOCKET socket = ::accept(m_socket, &saddress, &saddress_length);\r
+        if (socket == INVALID_SOCKET)\r
+        {\r
+          // only set the result socket with an error\r
+          result.m_impl->set_error(ERRNO);\r
+          return result;\r
+        }\r
+        // extract the contents of the address\r
+        unsigned long remote_address = 0;\r
+        unsigned short remote_port = 0;\r
+        convert_sockaddr(saddress, remote_address, remote_port);\r
+        result.m_impl->set(socket, remote_address, remote_port);\r
+        return result;\r
+      }\r
+\r
+    // client connect to a server\r
+    // - remote_address: IP number of remote address to connect to\r
+    // - remote_port: port to connect to\r
+    bool connect(unsigned long remote_address, unsigned short remote_port)\r
+      {\r
+        if (!initialised()) return false;\r
+        // fill in the connection data structure\r
+        sockaddr connect_data;\r
+        convert_address(remote_address, remote_port, connect_data);\r
+        // connect binds the socket to a local address\r
+        // if connectionless it simply sets the default remote address\r
+        // if connectioned it makes the connection\r
+        if (::connect(m_socket, &connect_data, sizeof(connect_data)) == SOCKET_ERROR)\r
+        {\r
+          // the socket is non-blocking, so connect will almost certainly fail with EINPROGRESS which is not an error\r
+          // only catch real errors\r
+          int error = ERRNO;\r
+          if (error != EINPROGRESS && error != EWOULDBLOCK)\r
+          {\r
+            set_error(error);\r
+            return false;\r
+          }\r
+        }\r
+        // extract the remote connection details for local storage\r
+        convert_sockaddr(connect_data, m_remote_address, m_remote_port);\r
+        return true;\r
+      }\r
+\r
+    // test whether a socket is connected and ready to communicate\r
+    bool connected(unsigned wait)\r
+      {\r
+        if (!initialised()) return false;\r
+        // Linux and Windows docs say test with select for whether socket is\r
+        // writable. However, a problem has been reported with Linux whereby\r
+        // the OS will report a socket as writable when it isn't\r
+        // first use the select method\r
+        if (!select(false, true, wait))\r
+          return false;\r
+#ifdef MSWINDOWS\r
+        // Windows needs no further processing - select method works\r
+        return true;\r
+#else\r
+        // Posix version needs further checking using the socket options\r
+        // DJDM: socket has returned EINPROGRESS on the first attempt at connection\r
+        // it has also returned that it can be written to\r
+        // we must now ask it if it has actually connected - using getsockopt\r
+        int error = 0;\r
+        socklen_t serror = sizeof(int);\r
+        if (::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &error, &serror)==0)\r
+          // handle the error value - one of them means that the socket has connected\r
+          if (!error || error == EISCONN)\r
+            return true;\r
+        return false;\r
+#endif\r
+      }\r
+\r
+    bool close(void)\r
+      {\r
+        bool result = true;\r
+        if (initialised())\r
+        {\r
+          if (shutdown(m_socket,SHUT_RDWR) == SOCKET_ERROR)\r
+          {\r
+            set_error(ERRNO);\r
+            result = false;\r
+          }\r
+          if (CLOSE(m_socket) == SOCKET_ERROR)\r
+          {\r
+            set_error(ERRNO);\r
+            result = false;\r
+          }\r
+        }\r
+        m_socket = INVALID_SOCKET;\r
+        m_remote_address = 0;\r
+        m_remote_port = 0;\r
+        return result;\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // sending/receiving\r
+\r
+    bool send_ready(unsigned wait)\r
+      {\r
+        // determines whether the socket is ready to send by testing whether it is writable\r
+        return select(false, true, wait);\r
+      }\r
+\r
+    bool send (std::string& data)\r
+      {\r
+        if (!initialised()) return false;\r
+        // send the data - this will never block but may not send all the data\r
+        int bytes = ::send(m_socket, data.c_str(), data.size(), SEND_FLAGS);\r
+        if (bytes == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent\r
+        data.erase(0,bytes);\r
+        return true;\r
+      }\r
+\r
+    bool send_packet(std::string& data, unsigned long address = 0, unsigned short port = 0)\r
+      {\r
+        if (!initialised()) return false;\r
+        // if no address specified, rely on the socket having been connected (can I test this?)\r
+        // so use the standard send, otherwise use the sendto function\r
+        int bytes = 0;\r
+        if (!address)\r
+        {\r
+          bytes = ::send(m_socket, data.c_str(), data.size(), SEND_FLAGS);\r
+        }\r
+        else\r
+        {\r
+          sockaddr saddress;\r
+          convert_address(address, port, saddress);\r
+          bytes = ::sendto(m_socket, data.c_str(), data.size(), SEND_FLAGS, &saddress, sizeof(saddress));\r
+        }\r
+        if (bytes == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent\r
+        data.erase(0,bytes);\r
+        return true;\r
+      }\r
+\r
+    bool receive_ready(unsigned wait)\r
+      {\r
+        // determines whether the socket is ready to receive by testing whether it is readable\r
+        return select(true, false, wait);\r
+      }\r
+\r
+    bool receive (std::string& data)\r
+      {\r
+        if (!initialised()) return false;\r
+        // determine how much data is available to read\r
+        unsigned long bytes = 0;\r
+        if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // get the data up to the amount claimed to be present - this is non-blocking\r
+        char* buffer = new char[bytes+1];\r
+        int read = ::recv(m_socket, buffer, bytes, 0);\r
+        if (read == SOCKET_ERROR)\r
+        {\r
+          delete[] buffer;\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        if (read == 0)\r
+        {\r
+          // TODO - check whether this is an appropriate conditon to close the socket\r
+          close();\r
+        }\r
+        else\r
+        {\r
+          // this is binary data so copy the bytes including nulls\r
+          data.append(buffer,read);\r
+        }\r
+        delete[] buffer;\r
+        return true;\r
+      }\r
+\r
+    bool receive_packet(std::string& data, unsigned long& address, unsigned short& port)\r
+      {\r
+        if (!initialised()) return false;\r
+        // determine how much data is available to read\r
+        unsigned long bytes = 0;\r
+        if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // get the data up to the amount claimed to be present - this is non-blocking\r
+        // also get the sender's details\r
+        char* buffer = new char[bytes+1];\r
+        sockaddr saddress;\r
+        SOCKLEN_T saddress_length = sizeof(saddress);\r
+        int read = ::recvfrom(m_socket, buffer, bytes, 0, &saddress, &saddress_length);\r
+        if (read == SOCKET_ERROR)\r
+        {\r
+          // UDP connection reset means that a previous sent failed to deliver cos the address was unknown\r
+          // this is NOT an error with the sending server socket, which IS still usable\r
+          int error = ERRNO;\r
+          if (error != ECONNRESET)\r
+          {\r
+            delete[] buffer;\r
+            set_error(error);\r
+            close();\r
+            return false;\r
+          }\r
+        }\r
+        // this is binary data so copy the bytes including nulls\r
+        data.append(buffer,read);\r
+        // also retrieve the sender's details\r
+        convert_sockaddr(saddress, address, port);\r
+        delete[] buffer;\r
+        return true;\r
+      }\r
+\r
+    bool receive_packet(std::string& data)\r
+      {\r
+        // call the above and then discard the address details\r
+        unsigned long address = 0;\r
+        unsigned short port = 0;\r
+        return receive_packet(data, address, port);\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // informational\r
+\r
+    IP_socket_type type(void) const\r
+      {\r
+        return m_type;\r
+      }\r
+\r
+    unsigned short local_port(void) const\r
+      {\r
+        if (!initialised()) return 0;\r
+        sockaddr saddress;\r
+        SOCKLEN_T saddress_length = sizeof(saddress);\r
+        if (::getsockname(m_socket, &saddress, &saddress_length) != 0)\r
+        {\r
+          set_error(ERRNO);\r
+          return 0;\r
+        }\r
+        unsigned long address = 0;\r
+        unsigned short port = 0;\r
+        convert_sockaddr(saddress, address, port);\r
+        return port;\r
+      }\r
+\r
+    unsigned long remote_address(void) const\r
+      {\r
+        return m_remote_address;\r
+      }\r
+\r
+    unsigned short remote_port(void) const\r
+      {\r
+        return m_remote_port;\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+\r
+    void set_error (int error, const char* message = 0) const\r
+      {\r
+        if (error != 0)\r
+        {\r
+          m_error = error;\r
+          if (message && (message[0] != 0))\r
+            m_message = message;\r
+          else\r
+            m_message = error_string(error);\r
+        }\r
+      }\r
+\r
+    int error(void) const\r
+      {\r
+        return m_error;\r
+      }\r
+\r
+    void clear_error (void) const\r
+      {\r
+        m_error = 0;\r
+        m_message.erase();\r
+      }\r
+\r
+    std::string message(void) const\r
+      {\r
+        return m_message;\r
+      }\r
+\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Socket - common code to manipulate a socket\r
+\r
+  // create an uninitialised socket\r
+  IP_socket::IP_socket(void) : m_impl(new IP_socket_internals)\r
+  {\r
+  }\r
+\r
+  // create an initialised socket\r
+  // - type: create either a TCP or UDP socket - if neither, creates an uninitialised socket\r
+  IP_socket::IP_socket(IP_socket_type type) : m_impl(new IP_socket_internals)\r
+  {\r
+    initialise(type);\r
+  }\r
+\r
+  // destroy the socket, closing it if open\r
+  IP_socket::~IP_socket(void)\r
+  {\r
+    if (m_impl->decrement())\r
+      delete m_impl;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // copying is implemented as aliasing\r
+\r
+  IP_socket::IP_socket(const IP_socket& right) : m_impl(0)\r
+  {\r
+    // make this an alias of right\r
+    m_impl = right.m_impl;\r
+    m_impl->increment();\r
+  }\r
+\r
+  IP_socket& IP_socket::operator=(const IP_socket& right)\r
+  {\r
+    // make self-copy safe\r
+    if (m_impl == right.m_impl) return *this;\r
+    // first dealias the existing implementation\r
+    if (m_impl->decrement())\r
+      delete m_impl;\r
+    // now make this an alias of right\r
+    m_impl = right.m_impl;\r
+    m_impl->increment();\r
+    return *this;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // initialisation, connection\r
+\r
+  // initialise the socket\r
+  // - type: create either a TCP or UDP socket\r
+  // - returns success status\r
+  bool IP_socket::initialise(IP_socket_type type)\r
+  {\r
+    return m_impl->initialise(type);\r
+  }\r
+\r
+  // test whether this is an initialised socket\r
+  // - returns whether this is initialised\r
+  bool IP_socket::initialised(void) const\r
+  {\r
+    return m_impl->initialised();\r
+  }\r
+\r
+  // close, i.e. disconnect the socket\r
+  // - returns a success flag\r
+  bool IP_socket::close(void)\r
+  {\r
+    return m_impl->close();\r
+  }\r
+\r
+  // function for performing IP lookup (i.e. gethostbyname)\r
+  // could be standalone but making it a member means that it can use the socket's error handler\r
+  // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+  // - returns the IP address as a long integer - zero if there's an error\r
+  unsigned long IP_socket::ip_lookup(const std::string& remote_address)\r
+  {\r
+    return m_impl->ip_lookup(remote_address);\r
+  }\r
+\r
+  // test whether a socket is ready to communicate\r
+  // - readable: test whether socket is ready to read\r
+  // - writeable: test whether a socket is ready to write\r
+  // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait\r
+  // returns false if not ready or error - use error() method to tell - true if ready\r
+  bool IP_socket::select(bool readable, bool writeable, unsigned timeout)\r
+  {\r
+    return m_impl->select(readable, writeable, timeout);\r
+  }\r
+\r
+  // bind the socket to a port so that it can receive from specific address - typically used by a client\r
+  // - remote_address: IP number of remote server to send/receive to/from\r
+  // - local_port: port on local machine to bind to the address\r
+  // - returns success flag\r
+  bool IP_socket::bind(unsigned long remote_address, unsigned short local_port)\r
+  {\r
+    return m_impl->bind(remote_address, local_port);\r
+  }\r
+\r
+  // bind the socket to a port so that it can receive from any address - typically used by a server\r
+  // - local_port: port on local machine to bind to the address\r
+  // - returns success flag\r
+  bool IP_socket::bind_any(unsigned short local_port)\r
+  {\r
+    return m_impl->bind_any(local_port);\r
+  }\r
+\r
+  // initialise a socket and set this socket up to be a listening port\r
+  // - queue: length of backlog queue to manage - may be zero\r
+  // - returns success status\r
+  bool IP_socket::listen(unsigned short queue)\r
+  {\r
+    return m_impl->listen(queue);\r
+  }\r
+\r
+  // test for a connection on the object's socket - only applicable if it has been set up as a listening port\r
+  // - returns true if a connection is ready to be accepted\r
+  bool IP_socket::accept_ready(unsigned timeout) const\r
+  {\r
+    return m_impl->accept_ready(timeout);\r
+  }\r
+\r
+  // accept a connection on the object's socket - only applicable if it has been set up as a listening port\r
+  // - returns the connection as a new socket\r
+  IP_socket IP_socket::accept(void)\r
+  {\r
+    return m_impl->accept();\r
+  }\r
+\r
+  // client connect to a server\r
+  // - address: IP number already lookup up with ip_lookup\r
+  // - port: port to connect to\r
+  // - returns a success flag\r
+  bool IP_socket::connect(unsigned long address, unsigned short port)\r
+  {\r
+    return m_impl->connect(address, port);\r
+  }\r
+\r
+  // test whether a socket is connected and ready to communicate, returns on successful connect or timeout\r
+  // - timeout: how long to wait in microseconds if not connected yet\r
+  // - returns success flag\r
+  bool IP_socket::connected(unsigned timeout)\r
+  {\r
+    return m_impl->connected(timeout);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // sending/receiving\r
+\r
+  // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+  // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+  // - returns status\r
+  bool IP_socket::send_ready(unsigned timeout)\r
+  {\r
+    return m_impl->send_ready(timeout);\r
+  }\r
+\r
+  // send data through the socket - if the data is long only part of it may\r
+  // be sent. The sent part is removed from the data, so the same string can\r
+  // be sent again and again until it is empty.\r
+  // - data: string containing data to be sent - any data successfully sent is removed\r
+  // - returns success flag\r
+  bool IP_socket::send (std::string& data)\r
+  {\r
+    return m_impl->send(data);\r
+  }\r
+\r
+  // send data through a connectionless (UDP) socket\r
+  // the data will be sent as a single packet\r
+  // - packet: string containing data to be sent - any data successfully sent is removed\r
+  // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote\r
+  // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote\r
+  // - returns success flag\r
+  bool IP_socket::send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port)\r
+  {\r
+    return m_impl->send_packet(packet, remote_address, remote_port);\r
+  }\r
+\r
+  // send data through a connectionless (UDP) socket\r
+  // the data will be sent as a single packet\r
+  // only works if the socket has been connected to remote\r
+  // - packet: string containing data to be sent - any data successfully sent is removed\r
+  // - returns success flag\r
+  bool IP_socket::send_packet(std::string& packet)\r
+  {\r
+    return m_impl->send_packet(packet);\r
+  }\r
+\r
+  // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+  // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+  // - returns status\r
+  bool IP_socket::receive_ready(unsigned timeout)\r
+  {\r
+    return m_impl->receive_ready(timeout);\r
+  }\r
+\r
+  // receive data through a connection-based (TCP) socket\r
+  // if the data is long only part of it may be received. The received data\r
+  // is appended to the string, building it up in stages, so the same string\r
+  // can be received again and again until all information has been\r
+  // received.\r
+  // - data: string receiving data from socket - any data successfully received is appended\r
+  // - returns success flag\r
+  bool IP_socket::receive (std::string& data)\r
+  {\r
+    return m_impl->receive(data);\r
+  }\r
+\r
+  // receive data through a connectionless (UDP) socket\r
+  // - packet: string receiving data from socket - any data successfully received is appended\r
+  // - remote_address: returns the address of the remote host received from\r
+  // - remote_port: returns the port of the remote host received from\r
+  // - returns success flag\r
+  bool IP_socket::receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)\r
+  {\r
+    return m_impl->receive_packet(packet, remote_address, remote_port);\r
+  }\r
+\r
+  // variant of above which does not give back the address and port of the sender\r
+  // receive data through a connectionless (UDP) socket\r
+  // - packet: string receiving data from socket - any data successfully received is appended\r
+  // - returns success flag\r
+  bool IP_socket::receive_packet(std::string& packet)\r
+  {\r
+    return m_impl->receive_packet(packet);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // informational\r
+\r
+  IP_socket_type IP_socket::type(void) const\r
+  {\r
+    return m_impl->type();\r
+  }\r
+\r
+  unsigned short IP_socket::local_port(void) const\r
+  {\r
+    return m_impl->local_port();\r
+  }\r
+\r
+  unsigned long IP_socket::remote_address(void) const\r
+  {\r
+    return m_impl->remote_address();\r
+  }\r
+\r
+  unsigned short IP_socket::remote_port(void) const\r
+  {\r
+    return m_impl->remote_port();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // error handling\r
+\r
+  void IP_socket::set_error (int error, const std::string& message) const\r
+  {\r
+    m_impl->set_error(error, message.c_str());\r
+  }\r
+\r
+  void IP_socket::clear_error (void) const\r
+  {\r
+    m_impl->clear_error();\r
+  }\r
+\r
+  int IP_socket::error(void) const\r
+  {\r
+    return m_impl->error();\r
+  }\r
+\r
+  std::string IP_socket::message(void) const\r
+  {\r
+    return m_impl->message();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
This page took 0.050202 seconds and 4 git commands to generate.