]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Socket.hh
more explicit constructors
[chaz/yoink] / src / Moof / Socket.hh
index a0f2a7cae65bf2da03ee8b57af64f6b754e06c19..cc47f32ee41cf46f5c8bd38a8e9363fbc4e1171c 100644 (file)
@@ -9,10 +9,17 @@
 *
 **************************************************************************/
 
+/**
+ * \file Network.hh
+ * Network-related classes, including a reinterpreted sockets API.
+ */
+
 #ifndef _MOOF_SOCKET_HH_
 #define _MOOF_SOCKET_HH_
 
+#include <algorithm>
 #include <cstring>
+#include <iostream>
 #include <sstream>
 #include <string>
 #include <vector>
 namespace Mf {
 
 
+/**
+ * A class to represent the address of a remote host, including the type of
+ * service and socket communication.
+ */
 class SocketAddress
 {
 public:
 
+       /**
+        * Construct an unspecified address.
+        */
        SocketAddress() :
                mSize(0),
                mType(0)
        {
-               mAddress.sa.sa_family = AF_UNSPEC;
-               mAddress.v4.sin_port = 0;
+               mAddr.sa.sa_family = AF_UNSPEC;
+               mAddr.in.sin_port = 0;
        }
 
-       SocketAddress(const std::string& service, const std::string& name,
-                       int type = SOCK_STREAM, int family = AF_UNSPEC)
+       /**
+        * Construct an address with a specified host.  The address can be used
+        * to connect to a host.
+        * \param service The service name or port number.
+        * \param name The numeric IP address of the host.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        */
+       SocketAddress(const std::string& service,
+                                 const std::string& name,
+                                 int type = SOCK_STREAM,
+                                 int family = AF_UNSPEC)
        {
                init(service, name, type, family);
        }
 
-       SocketAddress(const std::string& service,
-                       int type = SOCK_STREAM, int family = AF_UNSPEC)
+       /**
+        * Construct an address without a specified host.  The address can be
+        * used to accept on a local port.
+        * \param service The service name or port number.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        */
+       explicit SocketAddress(const std::string& service,
+                                                  int type = SOCK_STREAM,
+                                                  int family = AF_UNSPEC)
        {
                init(service, type, family);
        }
 
-       SocketAddress(const struct addrinfo* addr, const std::string& name)
+       /**
+        * Construct an address from the information in an addrinfo structure.
+        * \param addr The addrinfo structure.
+        */
+       SocketAddress(const struct addrinfo* addr) :
+               mSize(addr->ai_addrlen),
+               mType(addr->ai_socktype)
        {
-               mType = addr->ai_socktype;
-               memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen);
-               mName = name;
-               mSize = addr->ai_addrlen;
+               memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
+               getNameAndService(mName, mService);
        }
 
-       SocketAddress(const struct sockaddr* addr, size_t size,
-                       int type = SOCK_STREAM)
+       /**
+        * Construct an address from a sockaddr structure.
+        * \param addr The sockaddr structure.
+        * \param size The size of the sockaddr structure.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        */
+       SocketAddress(const struct sockaddr* addr,
+                                 size_t size,
+                                 int type = SOCK_STREAM) :
+               mSize(size),
+               mType(type)
        {
-               mType = type;
-               memcpy(&mAddress.sa, addr, size);
-               mSize = size;
-               setNameFromAddress();
+               memcpy(&mAddr.sa, addr, size);
+               getNameAndService(mName, mService);
        }
 
 
+       /**
+        * Get an IPv4 broadcast address.
+        * \param service The service name or port number.
+        * \return The socket address.
+        */
        static SocketAddress broadcast(const std::string& service)
        {
                std::istringstream stream(service);
@@ -103,267 +151,647 @@ public:
        }
 
 
-       void init(const std::string& service, const std::string& name = "",
-                       int type = SOCK_STREAM, int family = AF_UNSPEC)
+       /**
+        * Initialize the address with a specified host.  The address can be
+        * used to connect to a host.
+        * \param service The service name or port number.
+        * \param name The numeric IP address of the host.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        */
+       void init(const std::string& service,
+                         const std::string& name,
+                         int type = SOCK_STREAM,
+                         int family = AF_UNSPEC)
        {
-               ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM);
-               ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
-
-               struct addrinfo hints;
-               memset(&hints, 0, sizeof(hints));
-               hints.ai_family = family;
-               hints.ai_socktype = type;
-               hints.ai_flags = AI_PASSIVE;
-
-               struct addrinfo* addr;
-               int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0,
-                               service.c_str(), &hints, &addr);
-               if (status == 0)
+               struct addrinfo* addr = resolve(service.c_str(), name.c_str(),
+                                                                               type, family,
+                                                                               AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED);
+               if (addr)
                {
-                       mType = addr->ai_socktype;
-                       memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen);
                        mSize = addr->ai_addrlen;
+                       mType = addr->ai_socktype;
+                       memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
 
-                       if (name != "") mName = name;
-                       else            setNameFromAddress();
+                       mService = service;
+                       mName = name;
 
                        freeaddrinfo(addr);
                }
                else
                {
-                       Mf::logWarning(gai_strerror(status));
                        mType = 0;
                        mSize = 0;
-                       mAddress.sa.sa_family = AF_UNSPEC;
-                       mAddress.v4.sin_port = 0;
+                       mAddr.sa.sa_family = AF_UNSPEC;
+                       mAddr.in.sin_port = 0;
                }
        }
 
+       /**
+        * Initialize the address without a specified host.  The address can be
+        * used to accept on a local port.
+        * \param service The service name or port number.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        */
        void init(const std::string& service,
-                       int type = SOCK_STREAM, int family = AF_UNSPEC)
+                         int type = SOCK_STREAM,
+                         int family = AF_UNSPEC)
        {
-               init(service, "", type, family);
+               struct addrinfo* addr = resolve(service.c_str(), 0,
+                                                                               type, family,
+                                                                               AI_PASSIVE);
+               if (addr)
+               {
+                       mSize = addr->ai_addrlen;
+                       mType = addr->ai_socktype;
+                       memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
+
+                       mService = service;
+                       getName(mName);
+
+                       freeaddrinfo(addr);
+               }
+               else
+               {
+                       mType = 0;
+                       mSize = 0;
+                       mAddr.sa.sa_family = AF_UNSPEC;
+                       mAddr.in.sin_port = 0;
+               }
        }
 
 
-       const std::string& name() const
+       /**
+        * Get the name of the service.  This could also be a port number if
+        * there is no service name associated with the number.
+        * \return The service.
+        */
+       const std::string& service() const
        {
-               return mName;
+               return mService;
        }
 
-       void setName(const std::string& name)
+       /**
+        * Get the name of the host.  This may be the host used to construct
+        * the address, or a resolved numeric host if none was used.
+        * \return The host.
+        */
+       const std::string& name() const
        {
-               mName = name;
+               return mName;
        }
 
+       /**
+        * Get the port number of the address service.
+        * \return Port number.
+        */
        unsigned short port() const
        {
-               return ntohs(mAddress.v4.sin_port);
+               return ntohs(mAddr.in.sin_port);
        }
 
+       /**
+        * Get the type of socket associated with the service of this address.
+        * \return Socket type; either SOCK_STREAM or SOCK_DGRAM.
+        */
        int type() const
        {
                return mType;
        }
 
+       /**
+        * Get the family of the protocol associated with the address.
+        * \return Protocol family; either AF_INET, AF_INET6, or AF_UNSPEC.
+        */
        int family() const
        {
-               return mAddress.sa.sa_family;
+               return mAddr.sa.sa_family;
        }
 
 
+       /**
+        * Get the sockaddr structure of the address.
+        * \return The sockaddr structure.
+        */
        const struct sockaddr* address() const
        {
-               return mSize != 0 ? &mAddress.sa : 0;
+               return mSize != 0 ? &mAddr.sa : 0;
        }
 
+       /**
+        * Get the size of the sockaddr structure of the address.
+        * \return The size of the sockaddr structure.
+        */
        size_t size() const
        {
                return mSize;
        }
 
 
-       static int resolve(const std::string& service, const std::string& name,
-                       int type, int family, std::vector<SocketAddress>& resolved)
+       /**
+        * Get a list of addresses resolved to by the given search criteria.
+        * This can be used to perform lookups for name resolution, so this
+        * method may take some time to return.  Use the ResolveTask class to
+        * resolve addresses asynchronously.
+        * \param service The service name or port number.
+        * \param name The name of the local or remote host.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        * \param resolved The list to be filled with addresses.
+        * \return 0 on success, -1 on error.
+        */
+       static int resolve(const std::string& service,
+                                          const std::string& name,
+                                          int type,
+                                          int family,
+                                          std::vector<SocketAddress>& resolved)
+       {
+               struct addrinfo* list = resolve(service.c_str(), name.c_str(),
+                                                                               type, family,
+                                                                               AI_ADDRCONFIG | AI_V4MAPPED);
+               int result = collectAddresses(list, resolved);
+               freeaddrinfo(list);
+               return result;
+       }
+
+       /**
+        * Get a list of addresses resolved to by the given search criteria.
+        * The addresses will be suitable for accepting on a local port.
+        * \param service The service name or port number.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        * \param resolved The list to be filled with addresses.
+        * \return 0 on success, -1 on error.
+        */
+       static int resolve(const std::string& service,
+                                          int type,
+                                          int family,
+                                          std::vector<SocketAddress>& resolved)
+       {
+               struct addrinfo* list = resolve(service.c_str(), 0,
+                                                                               type, family,
+                                                                               AI_PASSIVE);
+               int result = collectAddresses(list, resolved);
+               freeaddrinfo(list);
+               return result;
+       }
+
+
+       /**
+        * Resolve the hostname of the address.  The default behavior is to
+        * avoid a reverse lookup by giving the numeric address.  You can
+        * change that behavior with the getnameinfo flags.
+        * \param name The place to store the hostname or IP address.
+        * \param flags The getnameinfo flags.
+        */
+       void getName(std::string& name, int flags = NI_NUMERICHOST)
+       {
+               char node[256] = {'\0'};
+               int result = getnameinfo(&mAddr.sa, mSize,
+                                                                node, sizeof(node),
+                                                                0, 0,
+                                                                flags);
+               if (result == 0) name.assign(node);
+       }
+
+       /**
+        * Resolve the service name of the address.
+        * \param service The place to store the service name or port number.
+        * \param flags The getnameinfo flags.
+        */
+       void getService(std::string& service, int flags)
+       {
+               flags |= mType == SOCK_DGRAM ? NI_DGRAM : 0;
+
+               char serv[64] = {'\0'};
+               int result = getnameinfo(&mAddr.sa, mSize,
+                                                                0, 0,
+                                                                serv, sizeof(serv),
+                                                                flags);
+               if (result == 0) service.assign(serv);
+       }
+
+       /**
+        * Resolve the service and hostname of the address.  The default
+        * behavior is to avoid a reverse lookup by giving the numeric address.
+        * You can change that behavior with the getnameinfo flags.
+        * \param name The place to store the hostname or IP address.
+        * \param service The place to store the service name or port number.
+        * \param flags The getnameinfo flags.
+        */
+       void getNameAndService(std::string& name,
+                                                  std::string& service,
+                                                  int flags = NI_NUMERICHOST)
+       {
+               flags |= mType == SOCK_DGRAM ? NI_DGRAM : 0;
+
+               char serv[64] = {'\0'};
+               char node[256] = {'\0'};
+               int result = getnameinfo(&mAddr.sa, mSize,
+                                                                node, sizeof(node),
+                                                                serv, sizeof(serv),
+                                                                flags);
+               if (result == 0)
+               {
+                       service.assign(serv);
+                       name.assign(node);
+               }
+       }
+
+
+private:
+
+       static struct addrinfo* resolve(const char* service,
+                                                                       const char* node,
+                                                                       int type,
+                                                                       int family,
+                                                                       int flags)
        {
                ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM);
                ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
 
-               resolved.clear();
-
                struct addrinfo hints;
                memset(&hints, 0, sizeof(hints));
                hints.ai_family = family;
                hints.ai_socktype = type;
-               hints.ai_flags = AI_PASSIVE;
+               hints.ai_flags = flags;
+
+               struct addrinfo* addr;
+               int status = getaddrinfo(node, service, &hints, &addr);
 
-               struct addrinfo* list;
-               int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0,
-                               service.length() > 0 ? service.c_str() : 0, &hints, &list);
                if (status == 0)
                {
-                       for (struct addrinfo* addr = list;
-                                       addr != 0; addr = addr->ai_next)
-                       {
-                               resolved.push_back(SocketAddress(addr, name));
-                       }
-
-                       freeaddrinfo(list);
+                       return addr;
                }
                else
                {
                        Mf::logWarning(gai_strerror(status));
+                       return 0;
                }
-
-               return 0;
        }
 
+       static int collectAddresses(struct addrinfo* addresses,
+                                                               std::vector<SocketAddress>& resolved)
+       {
+               if (addresses)
+               {
+                       resolved.clear();
 
-private:
+                       for (struct addrinfo* addr = addresses;
+                                addr != 0;
+                                addr = addr->ai_next)
+                       {
+                               resolved.push_back(SocketAddress(addr));
+                       }
 
-       void setNameFromAddress()
-       {
-#if defined(_WIN32)
-               // inet_ntop was introduced in Vista
-               mName = inet_ntoa(mAddress.v4.sin_addr);
-#else
-               char name[INET6_ADDRSTRLEN] = {'\0'};
-               inet_ntop(mAddress.sa.sa_family, &mAddress.sa, name, sizeof(name));
-               mName = name;
-#endif
+                       return 0;
+               }
+               else return -1;
        }
 
 
        union
        {
                sockaddr                        sa;
-               sockaddr_in                     v4;
-               sockaddr_in6            v6;
+               sockaddr_in                     in;
                sockaddr_storage        storage;
-       }                       mAddress;
+       }                       mAddr;
        size_t          mSize;
-       std::string     mName;
        int                     mType;
+
+       std::string     mName;
+       std::string mService;
 };
 
 
+/**
+ * The socket class represents a connection or between this node and a
+ * remote node.
+ */
 class Socket
 {
-public:
-
-       Socket(const SocketAddress& address) :
-               mFd(-1),
-               mAddress(address)
+       struct Impl
        {
-               mFd = socket(address.family(), address.type(), 0);
-       }
+               SocketAddress   address;
+               int                             fd;
+               bool                    isConnected;
+
+               Impl() :
+                       fd(-1),
+                       isConnected(false) {}
+
+               Impl(const SocketAddress& address, int flags = 0) :
+                       address(address),
+                       fd(::socket(address.family(), address.type(), flags)),
+                       isConnected(false) {}
+       } mImpl;
 
+
+public:
+
+       /**
+        * Construct a socket with no associated peer.
+        */
+       Socket() {}
+
+       /**
+        * Construct a socket with an address.
+        * \param address The address.
+        * \param flags The socket options.
+        */
+       Socket(const SocketAddress& address, int flags = 0) :
+               mImpl(address, flags) {}
+
+       /**
+        * Construct a socket with a specified host.  The socket can be used to
+        * connect to a host.
+        * \param service The service name or port number.
+        * \param name The numeric IP address of the host.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        * \param flags The socket options.
+        */
+       Socket(const std::string& service,
+                  const std::string& name,
+                  int type = SOCK_STREAM,
+                  int family = AF_UNSPEC,
+                  int flags = 0) :
+               mImpl(SocketAddress(service, name, type, family), flags) {}
+
+       /**
+        * Construct a socket without a specified host.  The socket can be used
+        * to accept sockets on a local port.
+        * \param service The service name or port number.
+        * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM.
+        * \param family The family; can be AF_INET or AF_INET6.
+        * \param flags The socket options.
+        */
+       explicit Socket(const std::string& service,
+                                       int type = SOCK_STREAM,
+                                       int family = AF_UNSPEC,
+                                       int flags = 0) :
+               mImpl(SocketAddress(service, type, family), flags) {}
+
+
+       /**
+        * Deconstruct the socket, closing it.
+        */
        ~Socket()
        {
-#if defined(_WIN32)
-               if (mFd != -1) closesocket(mFd);
-#else
-               if (mFd != -1) close(mFd);
-#endif
+               close();
        }
 
 
+       /**
+        * Get whether or not the socket is connected.
+        * \return True if the socket is connected, false otherwise.
+        */
        bool isConnected() const
        {
-               return mFd != -1;
+               return mImpl.isConnected;
        }
 
+       /**
+        * Get the address associated with the socket.
+        */
        const SocketAddress& address() const
        {
-               return mAddress;
+               return mImpl.address;
        }
 
 
+       /**
+        * Connect the socket to its peer.
+        * \return 0 on success, -1 on failure.
+        */
        int connect()
        {
-               return ::connect(mFd, mAddress.address(), mAddress.size());
+               int result = ::connect(mImpl.fd,
+                                                          mImpl.address.address(),
+                                                          mImpl.address.size());
+               mImpl.isConnected = result != -1;
+               return result;
+       }
+
+       /**
+        * Disconnect a connected socket from its peer.
+        * \param flags Specify the socket directions to close.
+        * \return 0 on success, -1 on failure.
+        */
+       int disconnect(int flags = SHUT_RDWR)
+       {
+               return shutdown(mImpl.fd, flags);
        }
 
+
+       /**
+        * Bind the socket to interface and port number specified in the
+        * address.
+        * \return 0 on success, -1 on failure.
+        */
        int bind()
        {
-               return ::bind(mFd, mAddress.address(), mAddress.size());
+               return ::bind(mImpl.fd,
+                                         mImpl.address.address(),
+                                         mImpl.address.size());
        }
 
+       /**
+        * Listen on the socket for incoming connections.  This is only useful
+        * for sockets of type SOCK_STREAM.
+        * \param backlog The number of unaccepted connections to queue.
+        * \return 0 on success, -1 on failure.
+        */
        int listen(int backlog = SOMAXCONN)
        {
-               return ::listen(mFd, backlog > 0 ? backlog : SOMAXCONN);
+               return ::listen(mImpl.fd, backlog > 0 ? backlog : SOMAXCONN);
        }
 
-       Socket accept()
+       /**
+        * Accept a new connection on the socket.  This is only useful for
+        * sockets of type SOCK_STREAM.
+        * \param socket Set to the new socket on return.
+        * \return 0 on success, -1 on failure.
+        */
+       int accept(Socket& socket)
        {
-               return Socket(mFd);
+               Socket temp = Socket(mImpl.fd);
+               if (temp.mImpl.fd != -1)
+               {
+                       socket = temp;
+                       return socket.mImpl.fd;
+               }
+               return -1;
        }
 
 
-       int set(int option, int value = 0)
+       /**
+        * Set an integer socket option.
+        * \param option The option to set.
+        * \param value The new value.
+        * \param level The layer to handle the option.
+        * \return 0 on success, -1 on failure.
+        */
+       template <class T>
+       int set(int option, const T& value, int level = SOL_SOCKET)
        {
-               if (option == SO_NONBLOCK)
-               {
-#ifdef HAVE_FCNTL
-                       int flags = fcntl(mFd, F_GETFL);
-                       return fcntl(mFd, F_SETFL, (value ? O_NONBLOCK : 0) | flags);
-#else
-                       return ioctl(mFd, FIONBIO, value);
-#endif
-               }
-               return setsockopt(mFd, SOL_SOCKET, option, &value, sizeof(value));
+               return setsockopt(mImpl.fd, level, option, &value, sizeof(value));
        }
 
-       int set(int option, const std::string& value)
+       /**
+        * Set a string socket option.
+        * \param option The option to set.
+        * \param value The new value.
+        * \param level The layer to handle the option.
+        * \return 0 on success, -1 on failure.
+        */
+       int set(int option, const std::string& value, int level = SOL_SOCKET)
        {
-               return setsockopt(mFd, SOL_SOCKET, option,
-                               value.c_str(), value.length());
+               return setsockopt(mImpl.fd, level, option,
+                                                 value.data(), value.length());
        }
 
-       int get(int option, int& value)
+       /**
+        * Get an integer socket option.
+        * \param option The option to set.
+        * \param value The new value.
+        * \param level The layer to handle the option.
+        * \return 0 on success, -1 on failure.
+        */
+       template <class T>
+       int get(int option, T& value, int level = SOL_SOCKET) const
        {
-               if (option == SO_NONBLOCK)
-               {
+               int size = sizeof(value);
+               return getsockopt(mImpl.fd, level, option, &value, &size);
+       }
+
+       /**
+        * Get a string socket option.
+        * \param option The option to set.
+        * \param value The new value.
+        * \param level The layer to handle the option.
+        * \return 0 on success, -1 on failure.
+        */
+       int get(int option, std::string& value, int level = SOL_SOCKET) const
+       {
+               char            str[256] = {'\0'};
+               socklen_t       size = sizeof(str);
+
+               int result = getsockopt(mImpl.fd, level, option, &str, &size);
+               value.assign(str, size);
+               return result;
+       }
+
+
+       void setBlocking(bool isBlocking)
+       {
+               int value = isBlocking;
 #ifdef HAVE_FCNTL
-                       int flags = fcntl(mFd, F_GETFL);
-                       return flags & O_NONBLOCK;
+               int flags = fcntl(mImpl.fd, F_GETFL);
+               fcntl(mImpl.fd, F_SETFL, flags | (value ? O_NONBLOCK : 0));
 #else
-                       return ioctl(mFd, FIONBIO, &value);
+               ioctl(mImpl.fd, FIONBIO, value);
 #endif
-               }
-               socklen_t optlen = sizeof(value);
-               return getsockopt(mFd, SOL_SOCKET, option, &value, &optlen);
        }
 
-       int get(int option, std::string& value)
+       bool isBlocking() const
        {
-               char str[64] = {'\0'};
-               socklen_t optlen = sizeof(str);
-               int result = getsockopt(mFd, SOL_SOCKET, option, &str, &optlen);
-               value = str;
-               return result;
+#ifdef HAVE_FCNTL
+               int flags = fcntl(mImpl.fd, F_GETFL);
+               return flags & O_NONBLOCK;
+#else
+               int value;
+               ioctl(mImpl.fd, FIONBIO, &value);
+               return value;
+#endif
+       }
+
+
+       /**
+        * Write some bytes to the socket.  Use this for connected sockets.
+        * \param bytes The bytes.
+        * \param size The number of bytes.
+        * \param flags The send options.
+        * \return The number of bytes written.
+        */
+       ssize_t write(const void* bytes, size_t size, int flags = 0)
+       {
+               return send(mImpl.fd, bytes, size, flags);
        }
 
-       void write(const Packet& packet)
+       /**
+        * Write some bytes to the socket using the given address.  Use this
+        * for unconnected sockets.
+        * \param bytes The bytes.
+        * \param size The number of bytes.
+        * \param address The address to send to.
+        * \param flags The send options.
+        * \return The number of bytes written.
+        */
+       ssize_t write(const void* bytes,
+                                 size_t size,
+                                 const SocketAddress& address,
+                                 int flags = 0)
        {
-               write(mAddress, packet);
+               return sendto(mImpl.fd, bytes, size, flags,
+                                         address.address(), address.size());
        }
 
-       void write(const SocketAddress& address, const Packet& packet)
+       /**
+        * Write a packet to the socket.  Use this for connected sockets.
+        * \param packet The packet.
+        * \param flags The send options.
+        * \return The number of bytes written.
+        */
+       ssize_t write(const Packet& packet, int flags = 0)
        {
-               sendto(mFd, packet.bytes(), packet.size(), 0,
-                               address.address(), address.size());
+               return write(packet.bytes(), packet.size(), flags);
        }
 
-       Packet read()
+       /**
+        * Write a packet to the socket using the given address.  Use this for
+        * unconnected sockets.
+        * \param packet The packet.
+        * \param address The address to send to.
+        * \param flags The send options.
+        * \return The number of bytes written.
+        */
+       ssize_t write(const Packet& packet,
+                                 const SocketAddress& address,
+                                 int flags = 0)
        {
-               char buffer[1024];
-               int size = recv(mFd, buffer, sizeof(buffer), 0);
+               return write(packet.bytes(), packet.size(), address, flags);
+       }
+
 
-               return Packet(buffer, size);
+       /**
+        * Read some bytes from the socket.  Use this for connected sockets.
+        * \param bytes The buffer to store the bytes.
+        * \param size The size of the buffer.
+        * \param flags The recv options.
+        * \return The number of bytes read.
+        */
+       ssize_t read(void* bytes, size_t size, int flags = 0)
+       {
+               ssize_t result = recv(mImpl.fd, bytes, size, flags);
+               if (result == 0) mImpl.isConnected = false;
+               return result;
        }
 
-       Packet read(SocketAddress& address)
+       /**
+        * Read some bytes from the socket using the given address.  Use this
+        * for unconnected sockets.
+        * \param bytes The buffer to store the bytes.
+        * \param size The size of the buffer.
+        * \param address The address to read from.
+        * \param flags The recv options.
+        * \return The number of bytes read.
+        */
+       ssize_t read(void* bytes,
+                                size_t size,
+                                SocketAddress& address,
+                                int flags = 0)
        {
                union
                {
@@ -372,12 +800,85 @@ public:
                } addr;
                socklen_t length = sizeof(addr);
 
-               char buffer[1024];
-               int size = recvfrom(mFd, buffer, sizeof(buffer), 0,
+               ssize_t result = recvfrom(mImpl.fd, bytes, size, flags,
                                &addr.sa, &length);
+               if (result != -1)
+               {
+                       address = SocketAddress(&addr.sa, length, mImpl.address.type());
+               }
+               else if (result == 0)
+               {
+                       mImpl.isConnected = false;
+               }
+               return result;
+       }
+
+       /**
+        * Read a packet from the socket.  Use this for connected sockets.
+        * \param packet Set to the packet read on return.
+        * \param flags The recv options.
+        * \return The number of bytes read.
+        */
+       ssize_t read(Packet& packet, int flags = 0)
+       {
+               char buffer[65536];
+               ssize_t result = read(buffer, sizeof(buffer), flags);
+               if (result != -1) packet = Packet(buffer, result);
+               return result;
+       }
+
+       /**
+        * Read a packet from the socket using the given address.  Use this for
+        * unconnected sockets.
+        * \param packet Set to the packet read on return.
+        * \param address The address to read from.
+        * \param flags The recv options.
+        * \return The number of bytes read.
+        */
+       ssize_t read(Packet& packet, SocketAddress& address, int flags = 0)
+       {
+               char buffer[65536];
+               ssize_t result = read(buffer, sizeof(buffer), address, flags);
+               if (result != -1) packet = Packet(buffer, result);
+               return result;
+       }
+
 
-               address = SocketAddress(&addr.sa, length, mAddress.type());
-               return Packet(buffer, size);
+       // The rest of this junk is used to implement the "move" semantics
+       // correctly, since it makes no sense for socket objects to be copied.
+
+       Socket(Socket& move) :
+               mImpl(move.mImpl)
+       {
+               move.mImpl.fd = -1;
+               move.mImpl.isConnected = false;
+       }
+
+       Socket(Impl move) :
+               mImpl(move) {}
+
+       Socket& operator=(Socket& move)
+       {
+               close();
+               mImpl = move.mImpl;
+               move.mImpl.fd = -1;
+               move.mImpl.isConnected = false;
+               return *this;
+       }
+
+       Socket& operator=(Impl move)
+       {
+               close();
+               mImpl = move;
+               return *this;
+       }
+
+       operator Impl()
+       {
+               Impl impl(mImpl);
+               mImpl.fd = -1;
+               mImpl.isConnected = false;
+               return impl;
        }
 
 
@@ -393,40 +894,140 @@ private:
                } addr;
                socklen_t length = sizeof(addr);
 
-               mFd = ::accept(fd, &addr.sa, &length);
-               mAddress = SocketAddress(&addr.sa, length);
+               mImpl.fd = ::accept(fd, &addr.sa, &length);
+               if (mImpl.fd != -1)
+               {
+                       mImpl.isConnected = true;
+                       mImpl.address = SocketAddress(&addr.sa, length);
+               }
        }
 
+       void close()
+       {
+#if defined(_WIN32)
+               if (mImpl.fd != -1) closesocket(mImpl.fd);
+#else
+               if (mImpl.fd != -1) ::close(mImpl.fd);
+#endif
+       }
+};
+
+
+class SocketMultiplexer
+{
+public:
+
+       typedef boost::function<int(SocketMultiplexer&,
+                                                               Packet&,
+                                                               const SocketAddress&)> Function;
+
+       explicit SocketMultiplexer(Socket sock) :
+               mSocket(sock) {}
+
+
+       void setSocket(Socket sock)
+       {
+               Mutex::ScopedLock lock(mMutex);
+               mSocket = sock;
+       }
+
+       Socket& socket()
+       {
+               return mSocket;
+       }
+
+
+       std::vector<Function>& protocols()
+       {
+               return mProtocols;
+       }
 
-       int                             mFd;
-       SocketAddress   mAddress;
+
+       void update(Scalar t, Scalar dt)
+       {
+               SocketAddress   address;
+               Packet                  packet;
+               ssize_t                 bytes = mSocket.read(packet, address);
+
+               if (bytes > 0)
+               {
+                       std::vector<Function>::iterator it;
+                       for (it = mProtocols.begin(); it < mProtocols.end(); ++it)
+                       {
+                               packet.reset();
+                               if ((*it)(*this, packet, address)) break;
+                       }
+               }
+       }
+
+
+       int background()
+       {
+               return 0;
+       }
+
+
+private:
+
+       Socket                                  mSocket;
+       std::vector<Function>   mProtocols;
+       Mutex                                   mMutex;
 };
 
 
+/**
+ * An asynchronous task to resolve addresses.
+ */
 class ResolverTask : public ThreadedTask
 {
 public:
 
-       ResolverTask(const std::string& service, const std::string& name,
-                       int type = SOCK_STREAM, int family = AF_UNSPEC) :
+       /**
+        * Construct a resolver task from a service and hostname.
+        * \param service Server name or port number.
+        * \param name The hostname or numeric address.
+        * \param type The type of communication.
+        * \param family The requested protocol family.
+        */
+       ResolverTask(const std::string& service,
+                                const std::string& name,
+                                int type = SOCK_STREAM,
+                                int family = AF_UNSPEC) :
                mIsDone(false)
        {
                mFunction = boost::bind(&ResolverTask::resolve,
-                               this, service, name, type, family);
+                                                               this, service, name, type, family);
        }
 
 
+       /**
+        * Get whether or not the task is done.
+        * \return True if the task has finished, false otherwise.
+        */
        bool isDone() const
        {
                return mIsDone;
        }
 
+       /**
+        * Start the task.  This does nothing if the task was already run or is
+        * currently running.
+        */
        void run()
        {
-               if (!mThread) mThread = Mf::detachFunction(mFunction);
+               if (!isDone() && !mThread.isValid())
+               {
+                       mThread = Thread::detach(mFunction);
+               }
        }
 
 
+       /**
+        * Get the addresses resolved.  This is filled and safe to access after
+        * the task finishes.
+        * \return List of addresses.
+        * \see isDone()
+        */
        const std::vector<SocketAddress>& addresses() const
        {
                return mAddressList;
@@ -435,11 +1036,14 @@ public:
 
 private:
 
-       int resolve(const std::string& service, const std::string& name,
-                       int type, int family)
+       int resolve(const std::string& service,
+                               const std::string& name,
+                               int type,
+                               int family)
        {
                int status = SocketAddress::resolve(service, name,
-                               type, family, mAddressList);
+                                                                                       type, family,
+                                                                                       mAddressList);
                mIsDone = true;
                return status;
        }
@@ -447,10 +1051,23 @@ private:
 
        std::vector<SocketAddress>      mAddressList;
        bool                                            mIsDone;
-       Function                                        mFunction;
+       Thread::Function                        mFunction;
 };
 
 
+std::ostream& operator<<(std::ostream& stream, const SocketAddress& addr)
+{
+       stream << addr.name() << ":" << addr.service();
+       return stream;
+}
+
+std::ostream& operator<<(std::ostream& stream, const Socket& sock)
+{
+       stream << sock.address();
+       return stream;
+}
+
+
 } // namespace Mf
 
 #endif // _MOOF_SOCKET_HH_
This page took 0.045546 seconds and 4 git commands to generate.