X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FSocket.hh;h=17e841cca87ba4170b82637fe0dd53aecb349581;hp=2411cc2e90a0c4c28c16ebf5bbe103fa255cf049;hb=2f239b9ba2a556a5ca810cfffc60552a56a4fe86;hpb=2a97ae825077e5b75aee35b1c1ada221a4ae7b26 diff --git a/src/Moof/Socket.hh b/src/Moof/Socket.hh index 2411cc2..17e841c 100644 --- a/src/Moof/Socket.hh +++ b/src/Moof/Socket.hh @@ -9,11 +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 @@ -70,16 +76,16 @@ public: * Construct an address with a specified host. The address can be used * to connect to a host. * \param service The service name or port number. - * \param host The numeric IP address of the host. + * \param name The numeric IP address of the host. * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM. * \param family The family; can be AF_INET or AF_INET6. */ SocketAddress(const std::string& service, - const std::string& host, + const std::string& name, int type = SOCK_STREAM, int family = AF_UNSPEC) { - init(service, host, type, family); + init(service, name, type, family); } /** @@ -105,7 +111,7 @@ public: mType(addr->ai_socktype) { memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); - getServiceAndHostName(mService, mHost); + getNameAndService(mName, mService); } /** @@ -121,7 +127,7 @@ public: mType(type) { memcpy(&mAddr.sa, addr, size); - getServiceAndHostName(mService, mHost); + getNameAndService(mName, mService); } @@ -149,16 +155,16 @@ public: * Initialize the address with a specified host. The address can be * used to connect to a host. * \param service The service name or port number. - * \param host The numeric IP address of the host. + * \param name The numeric IP address of the host. * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM. * \param family The family; can be AF_INET or AF_INET6. */ void init(const std::string& service, - const std::string& host, + const std::string& name, int type = SOCK_STREAM, int family = AF_UNSPEC) { - struct addrinfo* addr = resolve(service.c_str(), host.c_str(), + struct addrinfo* addr = resolve(service.c_str(), name.c_str(), type, family, AI_ADDRCONFIG | AI_NUMERICHOST | AI_V4MAPPED); if (addr) @@ -168,7 +174,7 @@ public: memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); mService = service; - mHost = host; + mName = name; freeaddrinfo(addr); } @@ -202,7 +208,7 @@ public: memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); mService = service; - getHost(mHost); + getName(mName); freeaddrinfo(addr); } @@ -231,9 +237,9 @@ public: * the address, or a resolved numeric host if none was used. * \return The host. */ - const std::string& host() const + const std::string& name() const { - return mHost; + return mName; } /** @@ -289,19 +295,19 @@ public: * method may take some time to return. Use the ResolveTask class to * resolve addresses asynchronously. * \param service The service name or port number. - * \param host The name of the local or remote host. + * \param name The name of the local or remote host. * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM. * \param family The family; can be AF_INET or AF_INET6. * \param resolved The list to be filled with addresses. * \return 0 on success, -1 on error. */ static int resolve(const std::string& service, - const std::string& host, + const std::string& name, int type, int family, std::vector& resolved) { - struct addrinfo* list = resolve(service.c_str(), host.c_str(), + struct addrinfo* list = resolve(service.c_str(), name.c_str(), type, family, AI_ADDRCONFIG | AI_V4MAPPED); int result = collectAddresses(list, resolved); @@ -332,6 +338,68 @@ public: } + /** + * Resolve the hostname of the address. The default behavior is to + * avoid a reverse lookup by giving the numeric address. You can + * change that behavior with the getnameinfo flags. + * \param name The place to store the hostname or IP address. + * \param flags The getnameinfo flags. + */ + void getName(std::string& name, int flags = NI_NUMERICHOST) + { + char node[256] = {'\0'}; + int result = getnameinfo(&mAddr.sa, mSize, + node, sizeof(node), + 0, 0, + flags); + if (result == 0) name.assign(node); + } + + /** + * Resolve the service name of the address. + * \param service The place to store the service name or port number. + * \param flags The getnameinfo flags. + */ + void getService(std::string& service, int flags) + { + flags |= mType == SOCK_DGRAM ? NI_DGRAM : 0; + + char serv[64] = {'\0'}; + int result = getnameinfo(&mAddr.sa, mSize, + 0, 0, + serv, sizeof(serv), + flags); + if (result == 0) service.assign(serv); + } + + /** + * Resolve the service and hostname of the address. The default + * behavior is to avoid a reverse lookup by giving the numeric address. + * You can change that behavior with the getnameinfo flags. + * \param name The place to store the hostname or IP address. + * \param service The place to store the service name or port number. + * \param flags The getnameinfo flags. + */ + void getNameAndService(std::string& name, + std::string& service, + int flags = NI_NUMERICHOST) + { + flags |= mType == SOCK_DGRAM ? NI_DGRAM : 0; + + char serv[64] = {'\0'}; + char node[256] = {'\0'}; + int result = getnameinfo(&mAddr.sa, mSize, + node, sizeof(node), + serv, sizeof(serv), + flags); + if (result == 0) + { + service.assign(serv); + name.assign(node); + } + } + + private: static struct addrinfo* resolve(const char* service, @@ -383,43 +451,6 @@ private: } - void getService(std::string& service) - { - char value[64] = {'\0'}; - int result = getnameinfo(&mAddr.sa, mSize, - 0, 0, - value, sizeof(value), - (mType == SOCK_DGRAM) ? NI_DGRAM : 0); - if (result == 0) service.assign(value); - } - - void getHost(std::string& host) - { - char value[256] = {'\0'}; - int result = getnameinfo(&mAddr.sa, mSize, - value, sizeof(value), - 0, 0, - NI_NUMERICHOST); - if (result == 0) host.assign(value); - } - - void getServiceAndHostName(std::string& service, std::string& host) - { - char serv[64] = {'\0'}; - char node[256] = {'\0'}; - int result = getnameinfo(&mAddr.sa, mSize, - node, sizeof(node), - serv, sizeof(serv), - NI_NUMERICHOST | - ((mType == SOCK_DGRAM) ? NI_DGRAM : 0)); - if (result == 0) - { - service.assign(serv); - host.assign(node); - } - } - - union { sockaddr sa; @@ -429,7 +460,7 @@ private: size_t mSize; int mType; - std::string mHost; + std::string mName; std::string mService; }; @@ -476,14 +507,16 @@ public: * Construct a socket with a specified host. The socket can be used to * connect to a host. * \param service The service name or port number. - * \param host The numeric IP address of the host. + * \param name The numeric IP address of the host. * \param type The type of socket; either SOCK_STREAM or SOCK_DGRAM. * \param family The family; can be AF_INET or AF_INET6. * \param flags The socket options. */ - Socket(const std::string& service, const std::string& name, - int type = SOCK_STREAM, int family = AF_UNSPEC, - int flags = 0) : + Socket(const std::string& service, + const std::string& name, + int type = SOCK_STREAM, + int family = AF_UNSPEC, + int flags = 0) : mImpl(SocketAddress(service, name, type, family), flags) {} /** @@ -494,8 +527,10 @@ public: * \param family The family; can be AF_INET or AF_INET6. * \param flags The socket options. */ - Socket(const std::string& service, int type = SOCK_STREAM, - int family = AF_UNSPEC, int flags = 0) : + Socket(const std::string& service, + int type = SOCK_STREAM, + int family = AF_UNSPEC, + int flags = 0) : mImpl(SocketAddress(service, type, family), flags) {} @@ -595,34 +630,25 @@ public: * Set an integer socket option. * \param option The option to set. * \param value The new value. + * \param level The layer to handle the option. * \return 0 on success, -1 on failure. */ - int set(int option, int value = 0) + template + int set(int option, const T& value, int level = SOL_SOCKET) { - if (option == SO_NONBLOCK) - { -#ifdef HAVE_FCNTL - int flags = fcntl(mImpl.fd, F_GETFL); - return fcntl(mImpl.fd, - F_SETFL, - flags | (value ? O_NONBLOCK : 0)); -#else - return ioctl(mImpl.fd, FIONBIO, value); -#endif - } - return setsockopt(mImpl.fd, SOL_SOCKET, option, - &value, sizeof(value)); + return setsockopt(mImpl.fd, level, option, &value, sizeof(value)); } /** * Set a string socket option. * \param option The option to set. * \param value The new value. + * \param level The layer to handle the option. * \return 0 on success, -1 on failure. */ - int set(int option, const std::string& value) + int set(int option, const std::string& value, int level = SOL_SOCKET) { - return setsockopt(mImpl.fd, SOL_SOCKET, option, + return setsockopt(mImpl.fd, level, option, value.data(), value.length()); } @@ -630,40 +656,58 @@ public: * Get an integer socket option. * \param option The option to set. * \param value The new value. + * \param level The layer to handle the option. * \return 0 on success, -1 on failure. */ - int get(int option, int& value) + template + int get(int option, T& value, int level = SOL_SOCKET) const { - if (option == SO_NONBLOCK) - { -#ifdef HAVE_FCNTL - int flags = fcntl(mImpl.fd, F_GETFL); - return flags & O_NONBLOCK; -#else - return ioctl(mImpl.fd, FIONBIO, &value); -#endif - } - socklen_t optlen = sizeof(value); - return getsockopt(mImpl.fd, SOL_SOCKET, option, &value, &optlen); + int size = sizeof(value); + return getsockopt(mImpl.fd, level, option, &value, &size); } /** * Get a string socket option. * \param option The option to set. * \param value The new value. + * \param level The layer to handle the option. * \return 0 on success, -1 on failure. */ - int get(int option, std::string& value) + int get(int option, std::string& value, int level = SOL_SOCKET) const { - char str[256] = {'\0'}; - socklen_t optlen = sizeof(str); - int result = getsockopt(mImpl.fd, SOL_SOCKET, option, - &str, &optlen); - value = str; + char str[256] = {'\0'}; + socklen_t size = sizeof(str); + + int result = getsockopt(mImpl.fd, level, option, &str, &size); + value.assign(str, size); return result; } + void setBlocking(bool isBlocking) + { + int value = isBlocking; +#ifdef HAVE_FCNTL + int flags = fcntl(mImpl.fd, F_GETFL); + fcntl(mImpl.fd, F_SETFL, flags | (value ? O_NONBLOCK : 0)); +#else + ioctl(mImpl.fd, FIONBIO, value); +#endif + } + + bool isBlocking() const + { +#ifdef HAVE_FCNTL + int flags = fcntl(mImpl.fd, F_GETFL); + return flags & O_NONBLOCK; +#else + int value; + ioctl(mImpl.fd, FIONBIO, &value); + return value; +#endif + } + + /** * Write some bytes to the socket. Use this for connected sockets. * \param bytes The bytes. @@ -685,11 +729,13 @@ public: * \param flags The send options. * \return The number of bytes written. */ - ssize_t write(const void* bytes, size_t size, - const SocketAddress& address, int flags = 0) + ssize_t write(const void* bytes, + size_t size, + const SocketAddress& address, + int flags = 0) { return sendto(mImpl.fd, bytes, size, flags, - address.address(), address.size()); + address.address(), address.size()); } /** @@ -711,8 +757,9 @@ public: * \param flags The send options. * \return The number of bytes written. */ - ssize_t write(const Packet& packet, const SocketAddress& address, - int flags = 0) + ssize_t write(const Packet& packet, + const SocketAddress& address, + int flags = 0) { return write(packet.bytes(), packet.size(), address, flags); } @@ -727,7 +774,9 @@ public: */ ssize_t read(void* bytes, size_t size, int flags = 0) { - return recv(mImpl.fd, bytes, size, flags); + ssize_t result = recv(mImpl.fd, bytes, size, flags); + if (result == 0) mImpl.isConnected = false; + return result; } /** @@ -739,8 +788,10 @@ public: * \param flags The recv options. * \return The number of bytes read. */ - ssize_t read(void* bytes, size_t size, SocketAddress& address, - int flags = 0) + ssize_t read(void* bytes, + size_t size, + SocketAddress& address, + int flags = 0) { union { @@ -755,6 +806,10 @@ public: { address = SocketAddress(&addr.sa, length, mImpl.address.type()); } + else if (result == 0) + { + mImpl.isConnected = false; + } return result; } @@ -858,6 +913,68 @@ private: }; +class SocketMultiplexer +{ +public: + + typedef boost::function 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; + } + + +private: + + Socket mSocket; + std::vector mProtocols; + Mutex mMutex; +}; + + /** * An asynchronous task to resolve addresses. */ @@ -868,18 +985,18 @@ public: /** * Construct a resolver task from a service and hostname. * \param service Server name or port number. - * \param host The hostname or numeric address. + * \param name The hostname or numeric address. * \param type The type of communication. * \param family The requested protocol family. */ ResolverTask(const std::string& service, - const std::string& host, + const std::string& name, int type = SOCK_STREAM, int family = AF_UNSPEC) : mIsDone(false) { mFunction = boost::bind(&ResolverTask::resolve, - this, service, host, type, family); + this, service, name, type, family); } @@ -920,11 +1037,11 @@ public: private: int resolve(const std::string& service, - const std::string& host, + const std::string& name, int type, int family) { - int status = SocketAddress::resolve(service, host, + int status = SocketAddress::resolve(service, name, type, family, mAddressList); mIsDone = true; @@ -938,6 +1055,19 @@ private: }; +std::ostream& operator<<(std::ostream& stream, const SocketAddress& addr) +{ + stream << addr.name() << ":" << addr.service(); + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const Socket& sock) +{ + stream << sock.address(); + return stream; +} + + } // namespace Mf #endif // _MOOF_SOCKET_HH_