]> Dogcows Code - chaz/yoink/blobdiff - src/moof/socket.hh
the massive refactoring effort
[chaz/yoink] / src / moof / socket.hh
diff --git a/src/moof/socket.hh b/src/moof/socket.hh
new file mode 100644 (file)
index 0000000..f4d2c82
--- /dev/null
@@ -0,0 +1,1164 @@
+
+/*]  Copyright (c) 2009-2010, Charles McGarvey  [**************************
+**]  All rights reserved.
+*
+* vi:ts=4 sw=4 tw=75
+*
+* Distributable under the terms and conditions of the 2-clause BSD license;
+* see the file COPYING for a complete text of the license.
+*
+**************************************************************************/
+
+#ifndef _MOOF_SOCKET_HH_
+#define _MOOF_SOCKET_HH_
+
+/**
+ * \file socket.hh
+ * Network-related classes, including a reinterpreted sockets API.
+ */
+
+#include <algorithm>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#error No alternative to fcntl implemented yet.
+#endif
+
+#if defined(_WIN32)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <wspiapi.h>
+#define SHUT_RD   SD_RECEIVE
+#define SHUT_WR   SD_SEND
+#define SHUT_RDWR SD_BOTH
+#else
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#endif
+
+#include <moof/log.hh>
+#include <moof/packet.hh>
+#include <moof/thread.hh>
+
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
+#ifndef AI_V4MAPPED
+#define AI_V4MAPPED 0
+#endif
+
+
+namespace moof {
+
+
+/**
+ * The socket class represents a connection or between this node and a
+ * remote node.
+ */
+class socket
+{
+public:
+
+       /**
+        * A class to represent the address of a remote host, including the
+        * type of service and socket communication.
+        */
+       class address
+       {
+       public:
+
+               /**
+                * Construct an unspecified address.
+                */
+               address() :
+                       size_(0),
+                       type_(0)
+               {
+                       addr_.sa.sa_family = AF_UNSPEC;
+                       addr_.in.sin_port = 0;
+               }
+
+               /**
+                * 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.
+                */
+               address(const std::string& service,
+                               const std::string& name,
+                               int type = SOCK_STREAM,
+                               int family = AF_UNSPEC)
+               {
+                       init(service, name, type, family);
+               }
+
+               /**
+                * 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 address(const std::string& service,
+                                                int type = SOCK_STREAM,
+                                                int family = AF_UNSPEC)
+               {
+                       init(service, type, family);
+               }
+
+               /**
+                * Construct an address from the information in an addrinfo
+                * structure.
+                * \param addr The addrinfo structure.
+                */
+               address(const struct addrinfo* addr) :
+                       size_(addr->ai_addrlen),
+                       type_(addr->ai_socktype)
+               {
+                       memcpy(&addr_.sa, addr->ai_addr, addr->ai_addrlen);
+                       get_name_and_service(name_, service_);
+               }
+
+               /**
+                * 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.
+                */
+               address(const struct sockaddr* addr,
+                               size_t size,
+                               int type = SOCK_STREAM) :
+                       size_(size),
+                       type_(type)
+               {
+                       memcpy(&addr_.sa, addr, size);
+                       get_name_and_service(name_, service_);
+               }
+
+
+               /**
+                * Get an IPv4 broadcast address.
+                * \param service The service name or port number.
+                * \return The socket address.
+                */
+               static address broadcast(const std::string& service)
+               {
+                       std::istringstream stream(service);
+                       unsigned short port;
+                       stream >> port;
+
+                       struct sockaddr_in addr;
+                       addr.sin_family = AF_INET;
+                       addr.sin_port = htons(port);
+                       addr.sin_addr.s_addr = INADDR_BROADCAST;
+                       memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
+                       return address((struct sockaddr*)&addr, sizeof(addr),
+                                                  SOCK_DGRAM);
+               }
+
+
+               /**
+                * 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)
+               {
+                       const int flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED;
+                       struct addrinfo* addr = resolve(service.c_str(), name.c_str(),
+                                                                                       type, family,
+                                                                                       flags);
+                       if (addr)
+                       {
+                               size_ = addr->ai_addrlen;
+                               type_ = addr->ai_socktype;
+                               memcpy(&addr_.sa, addr->ai_addr, addr->ai_addrlen);
+
+                               service_ = service;
+                               name_ = name;
+
+                               freeaddrinfo(addr);
+                       }
+                       else
+                       {
+                               type_ = 0;
+                               size_ = 0;
+                               addr_.sa.sa_family = AF_UNSPEC;
+                               addr_.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)
+               {
+                       struct addrinfo* addr = resolve(service.c_str(), 0,
+                                                                                       type, family,
+                                                                                       AI_PASSIVE);
+                       if (addr)
+                       {
+                               size_ = addr->ai_addrlen;
+                               type_ = addr->ai_socktype;
+                               memcpy(&addr_.sa, addr->ai_addr, addr->ai_addrlen);
+
+                               service_ = service;
+                               get_name(name_);
+
+                               freeaddrinfo(addr);
+                       }
+                       else
+                       {
+                               type_ = 0;
+                               size_ = 0;
+                               addr_.sa.sa_family = AF_UNSPEC;
+                               addr_.in.sin_port = 0;
+                       }
+               }
+
+
+               /**
+                * 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 service_;
+               }
+
+               /**
+                * 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
+               {
+                       return name_;
+               }
+
+               /**
+                * Get the port number of the address service.
+                * \return Port number.
+                */
+               unsigned short port() const
+               {
+                       return ntohs(addr_.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 type_;
+               }
+
+               /**
+                * 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 addr_.sa.sa_family;
+               }
+
+
+               /**
+                * Get the sockaddr structure of the address.
+                * \return The sockaddr structure.
+                */
+               const struct sockaddr* sockaddr() const
+               {
+                       return size_ != 0 ? &addr_.sa : 0;
+               }
+
+               /**
+                * Get the size of the sockaddr structure of the address.
+                * \return The size of the sockaddr structure.
+                */
+               size_t size() const
+               {
+                       return size_;
+               }
+
+
+               /**
+                * 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<address>& resolved)
+               {
+                       struct addrinfo* list = resolve(service.c_str(), name.c_str(),
+                                                                                       type, family,
+                                                                                       AI_ADDRCONFIG | AI_V4MAPPED);
+                       int result = collect_addresses(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<address>& resolved)
+               {
+                       struct addrinfo* list = resolve(service.c_str(), 0,
+                                                                                       type, family,
+                                                                                       AI_PASSIVE);
+                       int result = collect_addresses(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 get_name(std::string& name, int flags = NI_NUMERICHOST)
+               {
+                       char node[256] = {'\0'};
+                       int result = getnameinfo(&addr_.sa, size_,
+                                                                        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 get_service(std::string& service, int flags)
+               {
+                       flags |= type_ == SOCK_DGRAM ? NI_DGRAM : 0;
+
+                       char serv[64] = {'\0'};
+                       int result = getnameinfo(&addr_.sa, size_,
+                                                                        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 get_name_and_service(std::string& name,
+                                                                 std::string& service,
+                                                                 int flags = NI_NUMERICHOST)
+               {
+                       flags |= type_ == SOCK_DGRAM ? NI_DGRAM : 0;
+
+                       char serv[64] = {'\0'};
+                       char node[256] = {'\0'};
+                       int result = getnameinfo(&addr_.sa, size_,
+                                                                        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);
+
+                       struct addrinfo hints;
+                       memset(&hints, 0, sizeof(hints));
+                       hints.ai_family = family;
+                       hints.ai_socktype = type;
+                       hints.ai_flags = flags;
+
+                       struct addrinfo* addr;
+                       int status = getaddrinfo(node, service, &hints, &addr);
+
+                       if (status == 0)
+                       {
+                               return addr;
+                       }
+                       else
+                       {
+                               log_warning(gai_strerror(status));
+                               return 0;
+                       }
+               }
+
+               static int collect_addresses(struct addrinfo* addresses,
+                                                                       std::vector<address>& resolved)
+               {
+                       if (addresses)
+                       {
+                               resolved.clear();
+
+                               for (struct addrinfo* addr = addresses;
+                                        addr != 0;
+                                        addr = addr->ai_next)
+                               {
+                                       resolved.push_back(address(addr));
+                               }
+
+                               return 0;
+                       }
+                       else return -1;
+               }
+
+
+               union
+               {
+                       struct sockaddr                 sa;
+                       struct sockaddr_in              in;
+                       struct sockaddr_storage storage;
+               }                       addr_;
+               size_t          size_;
+               int                     type_;
+
+               std::string     name_;
+               std::string service_;
+       };
+
+
+private:
+
+       struct impl
+       {
+               socket::address address;
+               int                             fd;
+               bool                    is_connected;
+
+               impl() :
+                       fd(-1),
+                       is_connected(false) {}
+
+               impl(const socket::address& address, int flags = 0) :
+                       address(address),
+                       fd(::socket(address.family(), address.type(), flags)),
+                       is_connected(false) {}
+       } impl_;
+
+
+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 address& address, int flags = 0) :
+               impl_(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) :
+               impl_(address(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) :
+               impl_(address(service, type, family), flags) {}
+
+
+       /**
+        * Deconstruct the socket, closing it.
+        */
+       ~socket()
+       {
+               close();
+       }
+
+
+       /**
+        * Get whether or not the socket is connected.
+        * \return True if the socket is connected, false otherwise.
+        */
+       bool is_connected() const
+       {
+               return impl_.is_connected;
+       }
+
+       /**
+        * Get the address associated with the socket.
+        */
+       const address& peer_address() const
+       {
+               return impl_.address;
+       }
+
+
+       /**
+        * Connect the socket to its peer.
+        * \return 0 on success, -1 on failure.
+        */
+       int connect()
+       {
+               int result = ::connect(impl_.fd,
+                                                          impl_.address.sockaddr(),
+                                                          impl_.address.size());
+               impl_.is_connected = 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(impl_.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(impl_.fd,
+                                         impl_.address.sockaddr(),
+                                         impl_.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(impl_.fd, backlog > 0 ? backlog : SOMAXCONN);
+       }
+
+       /**
+        * 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)
+       {
+               moof::socket temp = moof::socket(impl_.fd);
+               if (temp.impl_.fd != -1)
+               {
+                       socket = temp;
+                       return socket.impl_.fd;
+               }
+               return -1;
+       }
+
+
+       /**
+        * 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 defined(_WIN32)
+               return setsockopt(impl_.fd,
+                                                 level,
+                                                 option,
+                                                 reinterpret_cast<const char*>(&value),
+                                                 sizeof(value));
+#else
+               return setsockopt(impl_.fd, level, option, &value, sizeof(value));
+#endif
+       }
+
+       /**
+        * 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(impl_.fd, level, option,
+                                                 value.data(), value.length());
+       }
+
+       /**
+        * 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
+       {
+               int size = sizeof(value);
+               return getsockopt(impl_.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);
+
+#if defined(_WIN32)
+               int result = getsockopt(impl_.fd,
+                                                               level,
+                                                               option,
+                                                               reinterpret_cast<char*>(&str),
+                                                               &size);
+#else
+               int result = getsockopt(impl_.fd, level, option, &str, &size);
+#endif
+               value.assign(str, size);
+               return result;
+       }
+
+
+       /**
+        * Set the socket IO mode to either blocking or non-blocking.
+        * \param is_blocking True if the socket should block, false otherwise.
+        */
+       void is_blocking(bool is_blocking)
+       {
+#ifdef HAVE_FCNTL
+               int flags = fcntl(impl_.fd, F_GETFL);
+               flags = is_blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+               fcntl(impl_.fd, F_SETFL, flags);
+#elif defined(_WIN32)
+               u_long value = is_blocking;
+               ioctlsocket(impl_.fd, FIONBIO, &value);
+#endif
+       }
+
+       /**
+        * Get whether or not the socket is blocking or non-blocking.  If the
+        * IO mode can't be determined, this method will assume the socket is
+        * a blocking socket.
+        * \return True if the socket blocks, false otherwise.
+        */
+       bool is_blocking() const
+       {
+#ifdef HAVE_FCNTL
+               int flags = fcntl(impl_.fd, F_GETFL);
+               return !(flags & O_NONBLOCK);
+#endif
+               return true;
+       }
+
+
+       /**
+        * 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)
+       {
+#if defined(_WIN32)
+               return send(impl_.fd,
+                                       reinterpret_cast<const char *>(bytes), size,
+                                       flags);
+#else
+               return send(impl_.fd, bytes, size, flags);
+#endif
+       }
+
+       /**
+        * 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 address& address,
+                                 int flags = 0)
+       {
+#if defined(_WIN32)
+               return sendto(impl_.fd,
+                                         reinterpret_cast<const char*>(bytes), size,
+                                         flags,
+                                         address.sockaddr(), address.size());
+#else
+               return sendto(impl_.fd, bytes, size, flags,
+                                         address.sockaddr(), address.size());
+#endif
+       }
+
+       /**
+        * 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)
+       {
+               return write(packet.bytes(), packet.size(), flags);
+       }
+
+       /**
+        * 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 address& address,
+                                 int flags = 0)
+       {
+               return write(packet.bytes(), packet.size(), address, flags);
+       }
+
+
+       /**
+        * 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)
+       {
+#if defined(_WIN32)
+               ssize_t result = recv(impl_.fd,
+                                                         reinterpret_cast<char*>(bytes), size,
+                                                         flags);
+#else
+               ssize_t result = recv(impl_.fd, bytes, size, flags);
+#endif
+               if (result == 0) impl_.is_connected = false;
+               return result;
+       }
+
+       /**
+        * 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,
+                                socket::address& address,
+                                int flags = 0)
+       {
+               union
+               {
+                       struct sockaddr                 sa;
+                       struct sockaddr_storage storage;
+               } addr;
+               socklen_t length = sizeof(addr);
+
+#if defined(_WIN32)
+               ssize_t result = recvfrom(impl_.fd,
+                                                                 reinterpret_cast<char*>(bytes), size,
+                                                                 flags,
+                                                                 &addr.sa, &length);
+#else
+               ssize_t result = recvfrom(impl_.fd, bytes, size, flags,
+                                                                 &addr.sa, &length);
+#endif
+               if (result != -1)
+               {
+                       address = socket::address(&addr.sa, length,
+                                                                         impl_.address.type());
+               }
+               else if (result == 0)
+               {
+                       impl_.is_connected = 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 = moof::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, address& address, int flags = 0)
+       {
+               char buffer[65536];
+               ssize_t result = read(buffer, sizeof(buffer), address, flags);
+               if (result != -1) packet = moof::packet(buffer, result);
+               return result;
+       }
+
+
+       // 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) :
+               impl_(move.impl_)
+       {
+               move.impl_.fd = -1;
+               move.impl_.is_connected = false;
+       }
+
+       socket(impl move) :
+               impl_(move) {}
+
+       socket& operator=(socket& move)
+       {
+               close();
+               impl_ = move.impl_;
+               move.impl_.fd = -1;
+               move.impl_.is_connected = false;
+               return *this;
+       }
+
+       socket& operator=(impl move)
+       {
+               close();
+               impl_ = move;
+               return *this;
+       }
+
+       operator impl()
+       {
+               impl impl(impl_);
+               impl_.fd = -1;
+               impl_.is_connected = false;
+               return impl;
+       }
+
+
+private:
+
+       socket(int fd)
+       {
+               // for accepting a socket from fd
+               union
+               {
+                       struct sockaddr                 sa;
+                       struct sockaddr_storage storage;
+               } addr;
+               socklen_t length = sizeof(addr);
+
+               impl_.fd = ::accept(fd, &addr.sa, &length);
+               if (impl_.fd != -1)
+               {
+                       impl_.is_connected = true;
+                       impl_.address = address(&addr.sa, length);
+               }
+       }
+
+       void close()
+       {
+#if defined(_WIN32)
+               if (impl_.fd != -1) closesocket(impl_.fd);
+#else
+               if (impl_.fd != -1) ::close(impl_.fd);
+#endif
+       }
+};
+
+
+class socket_multiplexer
+{
+public:
+
+       typedef boost::function<int(socket_multiplexer&,
+                                                               packet&,
+                                                               const socket::address&)> function;
+
+       explicit socket_multiplexer(moof::socket sock) :
+               socket_(sock) {}
+
+
+       void socket(moof::socket sock)
+       {
+               mutex::scoped_lock lock(mutex_);
+               socket_ = sock;
+       }
+
+       moof::socket& socket()
+       {
+               return socket_;
+       }
+
+
+       std::vector<function>& protocols()
+       {
+               return protocols_;
+       }
+
+
+       void update(scalar t, scalar dt)
+       {
+               socket::address address;
+               packet                  packet;
+               ssize_t                 bytes = socket_.read(packet, address);
+
+               if (bytes > 0)
+               {
+                       std::vector<function>::iterator it;
+                       for (it = protocols_.begin(); it < protocols_.end(); ++it)
+                       {
+                               packet.revert();
+                               if ((*it)(*this, packet, address)) break;
+                       }
+               }
+       }
+
+
+       int background()
+       {
+               return 0;
+       }
+
+
+private:
+
+       moof::socket                    socket_;
+       std::vector<function>   protocols_;
+       mutex                                   mutex_;
+};
+
+
+/**
+ * An asynchronous task to resolve addresses.
+ */
+class resolver_task : public threaded_task
+{
+public:
+
+       /**
+        * 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.
+        */
+       resolver_task(const std::string& service,
+                                 const std::string& name,
+                                 int type = SOCK_STREAM,
+                                 int family = AF_UNSPEC) :
+               is_done_(false)
+       {
+               function_ = boost::bind(&resolver_task::resolve,
+                                                               this, service, name, type, family);
+       }
+
+
+       /**
+        * Get whether or not the task is done.
+        * \return True if the task has finished, false otherwise.
+        */
+       bool is_done() const
+       {
+               return is_done_;
+       }
+
+       /**
+        * Start the task.  This does nothing if the task was already run or is
+        * currently running.
+        */
+       void run()
+       {
+               if (!is_done() && !thread_.is_valid())
+               {
+                       thread_ = thread::detach(function_);
+               }
+       }
+
+
+       /**
+        * Get the addresses resolved.  This is filled and safe to access after
+        * the task finishes.
+        * \return List of addresses.
+        * \see is_done()
+        */
+       const std::vector<socket::address>& addresses() const
+       {
+               return address_list_;
+       }
+
+
+private:
+
+       int resolve(const std::string& service,
+                               const std::string& name,
+                               int type,
+                               int family)
+       {
+               int status = socket::address::resolve(service, name,
+                                                                                         type, family,
+                                                                                         address_list_);
+               is_done_ = true;
+               return status;
+       }
+
+
+       std::vector<socket::address>    address_list_;
+       bool                                            is_done_;
+       thread::function                        function_;
+};
+
+
+/**
+ * Insert a string representation of a socket address into a stream.
+ * \param stream The output stream.
+ * \param addr The socket address.
+ * \return The stream.
+ */
+std::ostream& operator << (std::ostream& stream, const socket::address& addr)
+{
+       stream << addr.name() << ":" << addr.service();
+       return stream;
+}
+
+/**
+ * Insert a string representation of a socket into a stream.
+ * \param stream The output stream.
+ * \param addr The socket.
+ * \return The stream.
+ */
+std::ostream& operator << (std::ostream& stream, const socket& sock)
+{
+       stream << sock.peer_address();
+       return stream;
+}
+
+
+} // namespace moof
+
+#endif // _MOOF_SOCKET_HH_
+
This page took 0.05086 seconds and 4 git commands to generate.