]> Dogcows Code - chaz/yoink/blobdiff - src/stlplus/portability/ip_sockets.cpp
testing new non-autotools build system
[chaz/yoink] / src / stlplus / portability / ip_sockets.cpp
diff --git a/src/stlplus/portability/ip_sockets.cpp b/src/stlplus/portability/ip_sockets.cpp
new file mode 100644 (file)
index 0000000..6dc1fad
--- /dev/null
@@ -0,0 +1,961 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author:    Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+//            (c) Andy Rushton           2004-2009\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 EINPROGRESS WSAEINPROGRESS\r
+#define EWOULDBLOCK WSAEWOULDBLOCK\r
+#define ECONNRESET WSAECONNRESET\r
+#define SOCKLEN_T int\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
+#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(), 0);\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(), 0);\r
+        }\r
+        else\r
+        {\r
+          sockaddr saddress;\r
+          convert_address(address, port, saddress);\r
+          bytes = ::sendto(m_socket, data.c_str(), data.size(), 0, &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.036251 seconds and 4 git commands to generate.