X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fmoof%2Fsocket.hh;fp=src%2Fmoof%2Fsocket.hh;h=f4d2c82609e433d95cc8cc6ce2cf6429b291e2d7;hb=831f04d4bc19a390415ac0bbac4331c7a65509bc;hp=0000000000000000000000000000000000000000;hpb=299af4f2047e767e5d79501c26444473bda64c64;p=chaz%2Fyoink diff --git a/src/moof/socket.hh b/src/moof/socket.hh new file mode 100644 index 0000000..f4d2c82 --- /dev/null +++ b/src/moof/socket.hh @@ -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 +#include +#include +#include +#include +#include + +#if HAVE_FCNTL_H +#include +#else +#error No alternative to fcntl implemented yet. +#endif + +#if defined(_WIN32) +#include +#include +#include +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#else +#include +#include +#include +#include +#endif + +#include +#include +#include + + +#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
& 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
& 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
& 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 + int set(int option, const T& value, int level = SOL_SOCKET) + { +#if defined(_WIN32) + return setsockopt(impl_.fd, + level, + option, + reinterpret_cast(&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 + 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(&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(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(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(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(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 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& 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::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 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& 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 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_ +