X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FSocket.hh;h=dd43274c5cba006eccdab8dd80a6f2a56820bc0b;hp=948f9423785ea944087f29e580b5ef5b4f6f4391;hb=c78934a448d0126709fccec3d5a636b3baa87da4;hpb=264bdbb09bc86797f1f80d151ac408cb780b9355 diff --git a/src/Moof/Socket.hh b/src/Moof/Socket.hh index 948f942..dd43274 100644 --- a/src/Moof/Socket.hh +++ b/src/Moof/Socket.hh @@ -47,10 +47,17 @@ 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) @@ -59,36 +66,70 @@ public: 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 host 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, + int type = SOCK_STREAM, + int family = AF_UNSPEC) { - init(service, name, type, family); + init(service, host, 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. + */ SocketAddress(const std::string& service, - int type = SOCK_STREAM, int family = AF_UNSPEC) + 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(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); - mName = name; - mSize = addr->ai_addrlen; + getServiceAndHostName(mService, mHost); } - 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(&mAddr.sa, addr, size); - mSize = size; - setNameFromAddress(); + getServiceAndHostName(mService, mHost); } + /** + * 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); @@ -104,35 +145,35 @@ public: } - 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) + /** + * 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 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, + int type = SOCK_STREAM, + int family = AF_UNSPEC) + { + struct addrinfo* addr = resolve(service.c_str(), host.c_str(), + type, family, + AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED); + if (addr) { + mSize = addr->ai_addrlen; mType = addr->ai_socktype; memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); - mSize = addr->ai_addrlen; - if (name != "") mName = name; - else setNameFromAddress(); + mService = service; + mHost = host; freeaddrinfo(addr); } else { - Mf::logWarning(gai_strerror(status)); mType = 0; mSize = 0; mAddr.sa.sa_family = AF_UNSPEC; @@ -140,99 +181,242 @@ public: } } + /** + * 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; + getHost(mHost); + + 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& host() const { - mName = name; + return mHost; } + /** + * Get the port number of the address service. + * \return Port number. + */ unsigned short port() const { 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 mAddr.sa.sa_family; } + /** + * Get the sockaddr structure of the address. + * \return The sockaddr structure. + */ const struct sockaddr* address() const { 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& 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 host 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, + int type, + int family, + std::vector& resolved) + { + struct addrinfo* list = resolve(service.c_str(), host.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& resolved) + { + struct addrinfo* list = resolve(service.c_str(), 0, + type, family, + AI_PASSIVE); + int result = collectAddresses(list, resolved); + freeaddrinfo(list); + return result; + } + + +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 -1; + return 0; } + } + + static int collectAddresses(struct addrinfo* addresses, + std::vector& resolved) + { + if (addresses) + { + resolved.clear(); + + for (struct addrinfo* addr = addresses; + addr != 0; + addr = addr->ai_next) + { + resolved.push_back(SocketAddress(addr)); + } - return 0; + return 0; + } + else return -1; } -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 setNameFromAddress() + void getHost(std::string& host) { -#if defined(_WIN32) - // inet_ntop was introduced in Vista - mName = inet_ntoa(mAddr.in.sin_addr); -#else - char name[INET6_ADDRSTRLEN] = {'\0'}; - inet_ntop(mAddr.sa.sa_family, &mAddr.sa, name, sizeof(name)); - mName = name; -#endif + 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); + } } @@ -243,224 +427,320 @@ private: sockaddr_storage storage; } mAddr; size_t mSize; - std::string mName; int mType; + + std::string mHost; + std::string mService; }; +/** + * The socket class represents a connection or between this node and a + * remote node. + */ class Socket { struct Impl { + SocketAddress address; int fd; bool isConnected; - SocketAddress address; - }; + Impl() : + fd(-1), + isConnected(false) {} -public: + Impl(const SocketAddress& address, int flags = 0) : + address(address), + fd(::socket(address.family(), address.type(), flags)), + isConnected(false) {} + } mImpl; - Socket() : - mFd(-1), - mIsConnected(false) {} - Socket(const SocketAddress& address) : - mFd(-1), - mIsConnected(false), - mAddress(address) {} +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 host 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) : - mFd(-1), - mIsConnected(false), - mAddress(SocketAddress(service, name, type, family)) {} - - Socket(const std::string& service, - int type = SOCK_STREAM, int family = AF_UNSPEC) : - mFd(-1), - mIsConnected(false), - mAddress(SocketAddress(service, type, family)) {} - - - Socket(Socket& move) : - mFd(move.mFd), - mIsConnected(move.mIsConnected), - mAddress(move.mAddress) - { - move.mFd = -1; - move.mIsConnected = false; - } - - Socket(Impl move) : - mFd(move.fd), - mIsConnected(move.isConnected), - mAddress(move.address) {} - - Socket& operator=(Socket& move) - { -#if defined(_WIN32) - if (mFd != -1) closesocket(mFd); -#else - if (mFd != -1) close(mFd); -#endif - mFd = move.mFd; - mIsConnected = move.mIsConnected; - mAddress = move.mAddress; - move.mFd = -1; - move.mIsConnected = false; - return *this; - } - - Socket& operator=(Impl move) - { -#if defined(_WIN32) - if (mFd != -1) closesocket(mFd); -#else - if (mFd != -1) close(mFd); -#endif - mFd = move.fd; - mIsConnected = move.isConnected; - mAddress = move.address; - return *this; - } - - operator Impl() - { - Impl impl; - impl.fd = mFd; - impl.isConnected = mIsConnected; - impl.address = mAddress; - mFd = -1; - mIsConnected = false; - return impl; - } - - + 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. + */ + 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 mIsConnected; + 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() { - if (mFd == -1) mFd = socket(mAddress.family(), mAddress.type(), 0); - int result = ::connect(mFd, mAddress.address(), mAddress.size()); - mIsConnected = result != -1; + 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() { - if (mFd == -1) mFd = socket(mAddress.family(), mAddress.type(), 0); - 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); } + /** + * 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) { - Socket temp = Socket(mFd); - if (temp.mFd != -1) + Socket temp = Socket(mImpl.fd); + if (temp.mImpl.fd != -1) { socket = temp; - return socket.mFd; + return socket.mImpl.fd; } return -1; } + /** + * Set an integer socket option. + * \param option The option to set. + * \param value The new value. + * \return 0 on success, -1 on failure. + */ int set(int option, int value = 0) { if (option == SO_NONBLOCK) { #ifdef HAVE_FCNTL - int flags = fcntl(mFd, F_GETFL); - return fcntl(mFd, F_SETFL, (value ? O_NONBLOCK : 0) | flags); + int flags = fcntl(mImpl.fd, F_GETFL); + return fcntl(mImpl.fd, + F_SETFL, + flags | (value ? O_NONBLOCK : 0)); #else - return ioctl(mFd, FIONBIO, value); + return ioctl(mImpl.fd, FIONBIO, value); #endif } - return setsockopt(mFd, SOL_SOCKET, option, &value, sizeof(value)); + return setsockopt(mImpl.fd, SOL_SOCKET, option, + &value, sizeof(value)); } + /** + * Set a string socket option. + * \param option The option to set. + * \param value The new value. + * \return 0 on success, -1 on failure. + */ int set(int option, const std::string& value) { - return setsockopt(mFd, SOL_SOCKET, option, - value.data(), value.length()); + return setsockopt(mImpl.fd, SOL_SOCKET, option, + value.data(), value.length()); } + /** + * Get an integer socket option. + * \param option The option to set. + * \param value The new value. + * \return 0 on success, -1 on failure. + */ int get(int option, int& value) { if (option == SO_NONBLOCK) { #ifdef HAVE_FCNTL - int flags = fcntl(mFd, F_GETFL); + int flags = fcntl(mImpl.fd, F_GETFL); return flags & O_NONBLOCK; #else - return ioctl(mFd, FIONBIO, &value); + return ioctl(mImpl.fd, FIONBIO, &value); #endif } socklen_t optlen = sizeof(value); - return getsockopt(mFd, SOL_SOCKET, option, &value, &optlen); + return getsockopt(mImpl.fd, SOL_SOCKET, option, &value, &optlen); } + /** + * Get a string socket option. + * \param option The option to set. + * \param value The new value. + * \return 0 on success, -1 on failure. + */ int get(int option, std::string& value) { - char str[64] = {'\0'}; + char str[256] = {'\0'}; socklen_t optlen = sizeof(str); - int result = getsockopt(mFd, SOL_SOCKET, option, &str, &optlen); + int result = getsockopt(mImpl.fd, SOL_SOCKET, option, + &str, &optlen); value = str; return result; } - ssize_t write(const void* bytes, size_t size) + /** + * 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(mFd, bytes, size, 0); + return send(mImpl.fd, bytes, size, flags); } + + /** + * 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) + const SocketAddress& address, int flags = 0) { - return sendto(mFd, bytes, size, 0, + return sendto(mImpl.fd, bytes, size, flags, address.address(), address.size()); } - ssize_t write(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) { - return write(packet.bytes(), packet.size()); + return write(packet.bytes(), packet.size(), flags); } - ssize_t write(const Packet& packet, const SocketAddress& address) + /** + * 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) { - return write(packet.bytes(), packet.size(), address); + return write(packet.bytes(), packet.size(), address, flags); } - ssize_t read(void* bytes, size_t 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) { - return recv(mFd, bytes, size, 0); + return recv(mImpl.fd, bytes, size, flags); } - ssize_t read(void* bytes, size_t size, 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 { @@ -469,31 +749,84 @@ public: } addr; socklen_t length = sizeof(addr); - ssize_t result = recvfrom(mFd, bytes, size, 0, &addr.sa, &length); + ssize_t result = recvfrom(mImpl.fd, bytes, size, flags, + &addr.sa, &length); if (result != -1) { - address = SocketAddress(&addr.sa, length, mAddress.type()); + address = SocketAddress(&addr.sa, length, mImpl.address.type()); } return result; } - ssize_t read(Packet& packet) + /** + * 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)); + ssize_t result = read(buffer, sizeof(buffer), flags); if (result != -1) packet = Packet(buffer, result); return result; } - ssize_t read(Packet& packet, SocketAddress& address) + /** + * 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); + ssize_t result = read(buffer, sizeof(buffer), address, flags); if (result != -1) packet = 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) : + 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; + } + + private: Socket(int fd) @@ -506,45 +839,78 @@ private: } addr; socklen_t length = sizeof(addr); - mFd = ::accept(fd, &addr.sa, &length); - if (mFd != -1) + mImpl.fd = ::accept(fd, &addr.sa, &length); + if (mImpl.fd != -1) { - mIsConnected = true; - mAddress = SocketAddress(&addr.sa, length); + mImpl.isConnected = true; + mImpl.address = SocketAddress(&addr.sa, length); } } - - int mFd; - bool mIsConnected; - SocketAddress mAddress; + void close() + { +#if defined(_WIN32) + if (mImpl.fd != -1) closesocket(mImpl.fd); +#else + if (mImpl.fd != -1) ::close(mImpl.fd); +#endif + } }; +/** + * 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 host 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, + int type = SOCK_STREAM, + int family = AF_UNSPEC) : mIsDone(false) { mFunction = boost::bind(&ResolverTask::resolve, - this, service, name, type, family); + this, service, host, 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& addresses() const { return mAddressList; @@ -553,11 +919,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& host, + int type, + int family) { - int status = SocketAddress::resolve(service, name, - type, family, mAddressList); + int status = SocketAddress::resolve(service, host, + type, family, + mAddressList); mIsDone = true; return status; } @@ -565,7 +934,7 @@ private: std::vector mAddressList; bool mIsDone; - Function mFunction; + Thread::Function mFunction; };