X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FSocket.hh;h=60fd8e054531aa080f7cc1740d6075664f3ffb18;hp=2411cc2e90a0c4c28c16ebf5bbe103fa255cf049;hb=8c6a82feb045ac859b344cd7665a36ac00f4949e;hpb=2a97ae825077e5b75aee35b1c1ada221a4ae7b26 diff --git a/src/Moof/Socket.hh b/src/Moof/Socket.hh index 2411cc2..60fd8e0 100644 --- a/src/Moof/Socket.hh +++ b/src/Moof/Socket.hh @@ -9,29 +9,39 @@ * **************************************************************************/ +/** + * \file Socket.hh + * Network-related classes, including a reinterpreted sockets API. + */ + #ifndef _MOOF_SOCKET_HH_ #define _MOOF_SOCKET_HH_ #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 -#if HAVE_FCNTL_H -#include -#else -#include -#endif #endif #include @@ -39,8 +49,12 @@ #include -#ifndef SO_NONBLOCK -#define SO_NONBLOCK 1024 +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + +#ifndef AI_V4MAPPED +#define AI_V4MAPPED 0 #endif @@ -70,16 +84,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); } /** @@ -89,9 +103,9 @@ public: * \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) + explicit SocketAddress(const std::string& service, + int type = SOCK_STREAM, + int family = AF_UNSPEC) { init(service, type, family); } @@ -105,7 +119,7 @@ public: mType(addr->ai_socktype) { memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); - getServiceAndHostName(mService, mHost); + getNameAndService(mName, mService); } /** @@ -121,7 +135,7 @@ public: mType(type) { memcpy(&mAddr.sa, addr, size); - getServiceAndHostName(mService, mHost); + getNameAndService(mName, mService); } @@ -149,16 +163,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 +182,7 @@ public: memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); mService = service; - mHost = host; + mName = name; freeaddrinfo(addr); } @@ -202,7 +216,7 @@ public: memcpy(&mAddr.sa, addr->ai_addr, addr->ai_addrlen); mService = service; - getHost(mHost); + getName(mName); freeaddrinfo(addr); } @@ -231,9 +245,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 +303,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 +346,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 +459,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 +468,7 @@ private: size_t mSize; int mType; - std::string mHost; + std::string mName; std::string mService; }; @@ -476,14 +515,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 +535,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) : + explicit Socket(const std::string& service, + int type = SOCK_STREAM, + int family = AF_UNSPEC, + int flags = 0) : mImpl(SocketAddress(service, type, family), flags) {} @@ -595,34 +638,33 @@ 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)); +#if defined(_WIN32) + return setsockopt(mImpl.fd, + level, + option, + reinterpret_cast(&value), + sizeof(value)); #else - return ioctl(mImpl.fd, FIONBIO, value); + return setsockopt(mImpl.fd, level, option, &value, sizeof(value)); #endif - } - 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. + * \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 +672,74 @@ 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); + +#if defined(_WIN32) + int result = getsockopt(mImpl.fd, + level, + option, + reinterpret_cast(&str), + &size); +#else + int result = getsockopt(mImpl.fd, level, option, &str, &size); +#endif + value.assign(str, size); return result; } + /** + * Set the socket IO mode to either blocking or non-blocking. + * \param isBlocking True if the socket blocks, false otherwise. + */ + void setBlocking(bool isBlocking) + { +#ifdef HAVE_FCNTL + int flags = fcntl(mImpl.fd, F_GETFL); + flags = isBlocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + fcntl(mImpl.fd, F_SETFL, flags); +#elif defined(_WIN32) + u_long value = isBlocking; + ioctlsocket(mImpl.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 isBlocking() const + { +#ifdef HAVE_FCNTL + int flags = fcntl(mImpl.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. @@ -673,7 +749,13 @@ public: */ ssize_t write(const void* bytes, size_t size, int flags = 0) { +#if defined(_WIN32) + return send(mImpl.fd, + reinterpret_cast(bytes), size, + flags); +#else return send(mImpl.fd, bytes, size, flags); +#endif } /** @@ -685,11 +767,20 @@ 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) { +#if defined(_WIN32) + return sendto(mImpl.fd, + reinterpret_cast(bytes), size, + flags, + address.address(), address.size()); +#else return sendto(mImpl.fd, bytes, size, flags, - address.address(), address.size()); + address.address(), address.size()); +#endif } /** @@ -711,8 +802,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 +819,15 @@ public: */ ssize_t read(void* bytes, size_t size, int flags = 0) { - return recv(mImpl.fd, bytes, size, flags); +#if defined(_WIN32) + ssize_t result = recv(mImpl.fd, + reinterpret_cast(bytes), size, + flags); +#else + ssize_t result = recv(mImpl.fd, bytes, size, flags); +#endif + if (result == 0) mImpl.isConnected = false; + return result; } /** @@ -739,8 +839,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 { @@ -749,12 +851,23 @@ public: } addr; socklen_t length = sizeof(addr); +#if defined(_WIN32) + ssize_t result = recvfrom(mImpl.fd, + reinterpret_cast(bytes), size, + flags, + &addr.sa, &length); +#else ssize_t result = recvfrom(mImpl.fd, bytes, size, flags, - &addr.sa, &length); + &addr.sa, &length); +#endif if (result != -1) { address = SocketAddress(&addr.sa, length, mImpl.address.type()); } + else if (result == 0) + { + mImpl.isConnected = false; + } return result; } @@ -858,6 +971,68 @@ private: }; +class SocketMultiplexer +{ +public: + + typedef boost::function Function; + + explicit 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 +1043,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 +1095,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 +1113,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_