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