]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Socket.hh
tcp socket disconnecting by remote
[chaz/yoink] / src / Moof / Socket.hh
index 2411cc2e90a0c4c28c16ebf5bbe103fa255cf049..17e841cca87ba4170b82637fe0dd53aecb349581 100644 (file)
@@ -9,11 +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>
@@ -70,16 +76,16 @@ public:
         * 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 host The numeric IP address of the host.
+        * \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& host,
+                                 const std::string& name,
                                  int type = SOCK_STREAM,
                                  int family = AF_UNSPEC)
        {
-               init(service, host, type, family);
+               init(service, name, type, family);
        }
 
        /**
@@ -105,7 +111,7 @@ public:
                mType(addr->ai_socktype)
        {
                memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
-               getServiceAndHostName(mService, mHost);
+               getNameAndService(mName, mService);
        }
 
        /**
@@ -121,7 +127,7 @@ public:
                mType(type)
        {
                memcpy(&mAddr.sa, addr, size);
-               getServiceAndHostName(mService, mHost);
+               getNameAndService(mName, mService);
        }
 
 
@@ -149,16 +155,16 @@ public:
         * 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 host The numeric IP address of the host.
+        * \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& host,
+                         const std::string& name,
                          int type = SOCK_STREAM,
                          int family = AF_UNSPEC)
        {
-               struct addrinfo* addr = resolve(service.c_str(), host.c_str(),
+               struct addrinfo* addr = resolve(service.c_str(), name.c_str(),
                                                                                type, family,
                                                                                AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED);
                if (addr)
@@ -168,7 +174,7 @@ public:
                        memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
 
                        mService = service;
-                       mHost = host;
+                       mName = name;
 
                        freeaddrinfo(addr);
                }
@@ -202,7 +208,7 @@ public:
                        memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen);
 
                        mService = service;
-                       getHost(mHost);
+                       getName(mName);
 
                        freeaddrinfo(addr);
                }
@@ -231,9 +237,9 @@ public:
         * the address, or a resolved numeric host if none was used.
         * \return The host.
         */
-       const std::string& host() const
+       const std::string& name() const
        {
-               return mHost;
+               return mName;
        }
 
        /**
@@ -289,19 +295,19 @@ public:
         * method may take some time to return.  Use the ResolveTask class to
         * resolve addresses asynchronously.
         * \param service The service name or port number.
-        * \param host The name of the local or remote host.
+        * \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& host,
+                                          const std::string& name,
                                           int type,
                                           int family,
                                           std::vector<SocketAddress>& resolved)
        {
-               struct addrinfo* list = resolve(service.c_str(), host.c_str(),
+               struct addrinfo* list = resolve(service.c_str(), name.c_str(),
                                                                                type, family,
                                                                                AI_ADDRCONFIG | AI_V4MAPPED);
                int result = collectAddresses(list, resolved);
@@ -332,6 +338,68 @@ public:
        }
 
 
+       /**
+        * 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,
@@ -383,43 +451,6 @@ private:
        }
 
 
-       void getService(std::string& service)
-       {
-               char value[64] = {'\0'};
-               int result = getnameinfo(&mAddr.sa, mSize,
-                                                                0, 0,
-                                                                value, sizeof(value),
-                                                                (mType == SOCK_DGRAM) ? NI_DGRAM : 0);
-               if (result == 0) service.assign(value);
-       }
-
-       void getHost(std::string& host)
-       {
-               char value[256] = {'\0'};
-               int result = getnameinfo(&mAddr.sa, mSize,
-                                                                value, sizeof(value),
-                                                                0, 0,
-                                                                NI_NUMERICHOST);
-               if (result == 0) host.assign(value);
-       }
-
-       void getServiceAndHostName(std::string& service, std::string& host)
-       {
-               char serv[64] = {'\0'};
-               char node[256] = {'\0'};
-               int result = getnameinfo(&mAddr.sa, mSize,
-                                                                node, sizeof(node),
-                                                                serv, sizeof(serv),
-                                                                NI_NUMERICHOST |
-                                                                ((mType == SOCK_DGRAM) ? NI_DGRAM : 0));
-               if (result == 0)
-               {
-                       service.assign(serv);
-                       host.assign(node);
-               }
-       }
-
-
        union
        {
                sockaddr                        sa;
@@ -429,7 +460,7 @@ private:
        size_t          mSize;
        int                     mType;
 
-       std::string     mHost;
+       std::string     mName;
        std::string mService;
 };
 
@@ -476,14 +507,16 @@ public:
         * 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 host The numeric IP address of the host.
+        * \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) :
+       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) {}
 
        /**
@@ -494,8 +527,10 @@ public:
         * \param family The family; can be AF_INET or AF_INET6.
         * \param flags The socket options.
         */
-       Socket(const std::string& service, int type = SOCK_STREAM,
-                       int family = AF_UNSPEC, int flags = 0) :
+       Socket(const std::string& service,
+                  int type = SOCK_STREAM,
+                  int family = AF_UNSPEC,
+                  int flags = 0) :
                mImpl(SocketAddress(service, type, family), flags) {}
 
 
@@ -595,34 +630,25 @@ public:
         * 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.
         */
-       int set(int option, int value = 0)
+       template <class T>
+       int set(int option, const T& value, int level = SOL_SOCKET)
        {
-               if (option == SO_NONBLOCK)
-               {
-#ifdef HAVE_FCNTL
-                       int flags = fcntl(mImpl.fd, F_GETFL);
-                       return fcntl(mImpl.fd,
-                                                F_SETFL,
-                                                flags | (value ? O_NONBLOCK : 0));
-#else
-                       return ioctl(mImpl.fd, FIONBIO, value);
-#endif
-               }
-               return setsockopt(mImpl.fd, SOL_SOCKET, option,
-                                                 &value, sizeof(value));
+               return setsockopt(mImpl.fd, level, option, &value, sizeof(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 set(int option, const std::string& value, int level = SOL_SOCKET)
        {
-               return setsockopt(mImpl.fd, SOL_SOCKET, option,
+               return setsockopt(mImpl.fd, level, option,
                                                  value.data(), value.length());
        }
 
@@ -630,40 +656,58 @@ public:
         * 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.
         */
-       int get(int option, int& value)
+       template <class T>
+       int get(int option, T& value, int level = SOL_SOCKET) const
        {
-               if (option == SO_NONBLOCK)
-               {
-#ifdef HAVE_FCNTL
-                       int flags = fcntl(mImpl.fd, F_GETFL);
-                       return flags & O_NONBLOCK;
-#else
-                       return ioctl(mImpl.fd, FIONBIO, &value);
-#endif
-               }
-               socklen_t optlen = sizeof(value);
-               return getsockopt(mImpl.fd, SOL_SOCKET, option, &value, &optlen);
+               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 get(int option, std::string& value, int level = SOL_SOCKET) const
        {
-               char str[256] = {'\0'};
-               socklen_t optlen = sizeof(str);
-               int result = getsockopt(mImpl.fd, SOL_SOCKET, option,
-                                                               &str, &optlen);
-               value = str;
+               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(mImpl.fd, F_GETFL);
+               fcntl(mImpl.fd, F_SETFL, flags | (value ? O_NONBLOCK : 0));
+#else
+               ioctl(mImpl.fd, FIONBIO, value);
+#endif
+       }
+
+       bool isBlocking() const
+       {
+#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.
@@ -685,11 +729,13 @@ public:
         * \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)
+       ssize_t write(const void* bytes,
+                                 size_t size,
+                                 const SocketAddress& address,
+                                 int flags = 0)
        {
                return sendto(mImpl.fd, bytes, size, flags,
-                       address.address(), address.size());
+                                         address.address(), address.size());
        }
 
        /**
@@ -711,8 +757,9 @@ public:
         * \param flags The send options.
         * \return The number of bytes written.
         */
-       ssize_t write(const Packet& packet, const SocketAddress& address,
-                       int flags = 0)
+       ssize_t write(const Packet& packet,
+                                 const SocketAddress& address,
+                                 int flags = 0)
        {
                return write(packet.bytes(), packet.size(), address, flags);
        }
@@ -727,7 +774,9 @@ public:
         */
        ssize_t read(void* bytes, size_t size, int flags = 0)
        {
-               return recv(mImpl.fd, bytes, size, flags);
+               ssize_t result = recv(mImpl.fd, bytes, size, flags);
+               if (result == 0) mImpl.isConnected = false;
+               return result;
        }
 
        /**
@@ -739,8 +788,10 @@ public:
         * \param flags The recv options.
         * \return The number of bytes read.
         */
-       ssize_t read(void* bytes, size_t size, SocketAddress& address,
-                       int flags = 0)
+       ssize_t read(void* bytes,
+                                size_t size,
+                                SocketAddress& address,
+                                int flags = 0)
        {
                union
                {
@@ -755,6 +806,10 @@ public:
                {
                        address = SocketAddress(&addr.sa, length, mImpl.address.type());
                }
+               else if (result == 0)
+               {
+                       mImpl.isConnected = false;
+               }
                return result;
        }
 
@@ -858,6 +913,68 @@ private:
 };
 
 
+class SocketMultiplexer
+{
+public:
+
+       typedef boost::function<int(SocketMultiplexer&,
+                                                               Packet&,
+                                                               const SocketAddress&)> Function;
+
+       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;
+       }
+
+
+       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.
  */
@@ -868,18 +985,18 @@ public:
        /**
         * Construct a resolver task from a service and hostname.
         * \param service Server name or port number.
-        * \param host The hostname or numeric address.
+        * \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& host,
+                                const std::string& name,
                                 int type = SOCK_STREAM,
                                 int family = AF_UNSPEC) :
                mIsDone(false)
        {
                mFunction = boost::bind(&ResolverTask::resolve,
-                                                               this, service, host, type, family);
+                                                               this, service, name, type, family);
        }
 
 
@@ -920,11 +1037,11 @@ public:
 private:
 
        int resolve(const std::string& service,
-                               const std::string& host,
+                               const std::string& name,
                                int type,
                                int family)
        {
-               int status = SocketAddress::resolve(service, host,
+               int status = SocketAddress::resolve(service, name,
                                                                                        type, family,
                                                                                        mAddressList);
                mIsDone = true;
@@ -938,6 +1055,19 @@ private:
 };
 
 
+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.037728 seconds and 4 git commands to generate.