From 3fd6f206f0e64122b7cb289907a1a780b6ed2dc9 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Fri, 21 May 2010 00:10:25 -0600 Subject: [PATCH 1/1] exception-aware packets; other misc socket changes --- link.sh | 7 +- src/Moof/Packet.cc | 82 ++++++++++-- src/Moof/Packet.hh | 112 ++++++++++++++-- src/Moof/Service.cc | 58 +++++++- src/Moof/Service.hh | 44 +++++-- src/Moof/Socket.hh | 315 ++++++++++++++++++++++++++++++-------------- 6 files changed, 477 insertions(+), 141 deletions(-) diff --git a/link.sh b/link.sh index 7db3de0..d3a739c 100755 --- a/link.sh +++ b/link.sh @@ -6,9 +6,10 @@ # # You shouldn't call this directly; instead, use the configure script's # --enable-link-sh option and run make normally. This isn't enabled by -# default because there is the potential for runtime linking problems. If -# you have a newer version of GCC, you should prefer the --as-needed linker -# flag over this method, though they both should accomplish the same thing. +# default because there is the potential for runtime linking problems on +# some platforms. If you have a newer version of GCC, you should prefer +# the --as-needed linker flag over this method, though they both should +# accomplish the same thing. # # This script was adapted from some public domain code written by Bram # Moolenaar for Vim. The only input needed is the link command in the diff --git a/src/Moof/Packet.cc b/src/Moof/Packet.cc index 842d6c1..bb8fcc7 100644 --- a/src/Moof/Packet.cc +++ b/src/Moof/Packet.cc @@ -100,6 +100,7 @@ Packet::Packet(size_t size) : mSize(size), mR(0), mW(0), + mOriginalW(0), mBoolR(0), mBoolW(0), mBoolNumR(0), @@ -110,12 +111,14 @@ Packet::Packet(const char* data, size_t size) : mSize(size), mR(0), mW(size), + mOriginalW(size), mBoolR(0), mBoolW(0), mBoolNumR(0), mBoolNumW(0) { if (mBuffer) memcpy(mBuffer, data, size); + else throw std::length_error("out of memory"); } @@ -124,12 +127,14 @@ Packet::Packet(const Packet& copy) : mSize(copy.mSize), mR(copy.mR), mW(copy.mW), + mOriginalW(copy.mOriginalW), mBoolR(copy.mBoolR), mBoolW(copy.mBoolW), mBoolNumR(copy.mBoolNumR), mBoolNumW(copy.mBoolNumW) { if (mBuffer) memcpy(mBuffer, copy.mBuffer, mSize); + else throw std::length_error("out of memory"); } Packet& Packet::operator=(const Packet& copy) @@ -140,11 +145,13 @@ Packet& Packet::operator=(const Packet& copy) mSize = copy.mSize; mR = copy.mR; mW = copy.mW; + mOriginalW = copy.mOriginalW; mBoolR = copy.mBoolR; mBoolW = copy.mBoolW; mBoolNumR = copy.mBoolNumR; mBoolNumW = copy.mBoolNumW; if (mBuffer) memcpy(mBuffer, copy.mBuffer, mSize); + else throw std::length_error("out of memory"); return *this; } @@ -163,7 +170,7 @@ Packet& Packet::operator<<(bool value) mBoolW = mW; unsigned char byte = 0; - if (write(&byte, 1) == 0) return *this; + if (write(&byte, 1) == 0) throw std::length_error("out of memory"); } if (value) mBuffer[mBoolW] |= (1 << bit); @@ -196,28 +203,40 @@ Packet& Packet::operator<<(int64_t value) Packet& Packet::operator<<(uint8_t value) { - write(&value, sizeof(value)); + if (write(&value, sizeof(value)) != sizeof(value)) + { + throw std::length_error("out of memory"); + } return *this; } Packet& Packet::operator<<(uint16_t value) { value = htons(value); - write(&value, sizeof(value)); + if (write(&value, sizeof(value)) != sizeof(value)) + { + throw std::length_error("out of memory"); + } return *this; } Packet& Packet::operator<<(uint32_t value) { value = htonl(value); - write(&value, sizeof(value)); + if (write(&value, sizeof(value)) != sizeof(value)) + { + throw std::length_error("out of memory"); + } return *this; } Packet& Packet::operator<<(uint64_t value) { value = htonll(value); - write(&value, sizeof(value)); + if (write(&value, sizeof(value)) != sizeof(value)) + { + throw std::length_error("out of memory"); + } return *this; } @@ -226,7 +245,10 @@ Packet& Packet::operator<<(float value) // XXX: assumes the ieee-754 uint32_t* integer = reinterpret_cast(&value); *integer = htonl(*integer); - write(integer, sizeof(value)); + if (write(integer, sizeof(value)) != sizeof(value)) + { + throw std::length_error("out of memory"); + } return *this; } @@ -235,7 +257,10 @@ Packet& Packet::operator<<(double value) // XXX: assumes the ieee-754 uint64_t* integer = reinterpret_cast(&value); *integer = htonll(*integer); - write(integer, sizeof(value)); + if (write(integer, sizeof(value)) != sizeof(value)) + { + throw std::length_error("out of memory"); + } return *this; } @@ -269,7 +294,7 @@ Packet& Packet::operator>>(bool& value) mBoolR = mR; unsigned char byte = 0; - if (read(&byte, 1) == 0) return *this; + if (read(&byte, 1) == 0) throw std::out_of_range("end of packet"); } value = 1 & (mBuffer[mBoolR] >> bit); @@ -300,27 +325,39 @@ Packet& Packet::operator>>(int64_t& value) Packet& Packet::operator>>(uint8_t& value) { - read(&value, sizeof(value)); + if (read(&value, sizeof(value)) != sizeof(value)) + { + throw std::out_of_range("end of packet"); + } return *this; } Packet& Packet::operator>>(uint16_t& value) { - read(&value, sizeof(value)); + if (read(&value, sizeof(value)) != sizeof(value)) + { + throw std::out_of_range("end of packet"); + } value = ntohs(value); return *this; } Packet& Packet::operator>>(uint32_t& value) { - read(&value, sizeof(value)); + if (read(&value, sizeof(value)) != sizeof(value)) + { + throw std::out_of_range("end of packet"); + } value = ntohl(value); return *this; } Packet& Packet::operator>>(uint64_t& value) { - read(&value, sizeof(value)); + if (read(&value, sizeof(value)) != sizeof(value)) + { + throw std::out_of_range("end of packet"); + } value = ntohll(value); return *this; } @@ -329,7 +366,10 @@ Packet& Packet::operator>>(float& value) { // XXX: assumes the ieee-754 uint32_t* integer = reinterpret_cast(&value); - read(integer, sizeof(value)); + if (read(integer, sizeof(value)) != sizeof(value)) + { + throw std::out_of_range("end of packet"); + } *integer = htonl(*integer); return *this; } @@ -338,7 +378,10 @@ Packet& Packet::operator>>(double& value) { // XXX: assumes the ieee-754 uint64_t* integer = reinterpret_cast(&value); - read(integer, sizeof(value)); + if (read(integer, sizeof(value)) != sizeof(value)) + { + throw std::out_of_range("end of packet"); + } *integer = htonll(*integer); return *this; } @@ -363,5 +406,16 @@ void Packet::clear() } +void Packet::reset() +{ + mR = 0; + mW = mOriginalW; + mBoolR = 0; + mBoolW = 0; + mBoolNumR = 0; + mBoolNumW = 0; +} + + } // namespace Mf diff --git a/src/Moof/Packet.hh b/src/Moof/Packet.hh index 1a5e42f..8bcefff 100644 --- a/src/Moof/Packet.hh +++ b/src/Moof/Packet.hh @@ -9,10 +9,16 @@ * **************************************************************************/ +/** + * \file Packet.hh + * Classes for building and interpreting datagram packets. + */ + #ifndef _MOOF_PACKET_HH_ #define _MOOF_PACKET_HH_ #include +#include #include #include @@ -25,18 +31,43 @@ namespace Mf { +/** + * Represents a packet of serialized variables ready for transfer over the + * network. This method is most suitable for representing datagram + * packets, but it may also be useful for seralizing data for persistent + * state storage. The semantics are similar to that of a FIFO queue or + * stream where packets are written and read by inserting and extracting + * variables to and from the packet, although the actual order of the + * variables in the buffer may be different. At any time, a pointer to a + * buffer and the size of the buffer can be retrieved. This class also + * handles endian differences by serializing variables in network byte + * order (big endian). + */ class Packet { public: + /** + * Construct a packet with an initial capacity. + * \param capacity Initial capacity of the packet. + */ Packet(size_t size = PAGE_SIZE); - Packet(const char* data, size_t size); - - Packet(const Packet& copy); - Packet& operator=(const Packet& copy); - ~Packet(); + /** + * Construct a packet with some bytes from a buffer. The bytes will be + * copied into the packet, so you don't need to keep the original + * buffer. + * \param data The bytes. + * \param size The number of bytes. + */ + Packet(const char* data, size_t size); + /** + * Insert a variable into the packet, serializing it. This usually + * increases the size of the packet by the size of the data type. + * \param value The value to insert. + * \return This. + */ Packet& operator<<(bool value); Packet& operator<<(int8_t value); Packet& operator<<(int16_t value); @@ -49,8 +80,21 @@ public: Packet& operator<<(float value); Packet& operator<<(double value); + /** + * Write some bytes to the packet. + * \param bytes The bytes. + * \param size The number of bytes. + * return The number of bytes actually written. + */ size_t write(const void* bytes, size_t size); + + /** + * Extract a variable from the packet. This usually decreases the size + * of the packet by the size of the data type. + * \param value Reference to the variable to extract. + * \return This. + */ Packet& operator>>(bool& value); Packet& operator>>(int8_t& value); Packet& operator>>(int16_t& value); @@ -63,23 +107,57 @@ public: Packet& operator>>(float& value); Packet& operator>>(double& value); + /** + * Read some bytes from the packet. + * \param bytes The buffer to hold the bytes read. + * \param size The size of the read buffer. + * \return The number of bytes actually read. + */ size_t read(void* bytes, size_t size); + /** + * Clear the contents of the packet, setting the size of the packet to + * zero. + */ void clear(); + /** + * Reset the read/write markers to their initial positions, putting the + * packet in the state it was at right after construction. + */ + void reset(); + + + /** + * Get a pointer to an internal structure holding the serialized bytes + * of the packet. + * return The pointer. + */ const char* bytes() const { return mBuffer + mR; } + /** + * Get the size of the buffer holding the serialized bytes of the + * packet. + * \return The number of bytes. + */ size_t size() const { return mW - mR; } + // The rest of this stuff is just to implement correct copy semantics. + + Packet(const Packet& copy); + Packet& operator=(const Packet& copy); + ~Packet(); + + private: char* mBuffer; @@ -87,6 +165,7 @@ private: size_t mR; size_t mW; + size_t mOriginalW; size_t mBoolR; size_t mBoolW; @@ -99,15 +178,22 @@ inline Packet& operator<<(Packet& packet, const char* value) { uint16_t length = strlen(value); packet << length; - packet.write(value, length); + if (packet.write(value, length) != length) + { + throw std::length_error("out of memory"); + } return packet; } template inline Packet& operator<<(Packet& packet, const std::basic_string& value) { - packet << (uint16_t)value.length(); - packet.write(value.data(), value.length() * sizeof(T)); + packet << static_cast(value.length()); + size_t numBytes = value.length() * sizeof(T); + if (packet.write(value.data(), numBytes) != numBytes) + { + throw std::length_error("out of memory"); + } return packet; } @@ -118,8 +204,12 @@ inline Packet& operator>>(Packet& packet, std::basic_string& value) packet >> length; T str[length]; - size_t charsRead = packet.read(str, length * sizeof(T)); - value.assign(str, charsRead); + size_t numBytes = length * sizeof(T); + if (packet.read(str, numBytes) != numBytes) + { + throw std::out_of_range("end of packet"); + } + value.assign(str, length); return packet; } @@ -127,7 +217,7 @@ inline Packet& operator>>(Packet& packet, std::basic_string& value) template inline Packet& operator<<(Packet& packet, const std::vector& value) { - packet << (uint16_t)value.size(); + packet << static_cast(value.size()); typename std::vector::const_iterator it; for (it = value.begin(); it != value.end(); ++it) { diff --git a/src/Moof/Service.cc b/src/Moof/Service.cc index a5fae0f..400006b 100644 --- a/src/Moof/Service.cc +++ b/src/Moof/Service.cc @@ -10,16 +10,70 @@ **************************************************************************/ #include "Service.hh" +#include "Socket.hh" + + +#define SOLICIT 0x1234ABCD +#define RESPONSE 0xABCD1234 namespace Mf { -ServiceFinder::ServiceFinder(const std::string& service, int type) +int Service::handlePacket(SocketMultiplexer& sock, + Packet& packet, + const SocketAddress& address) { + uint32_t magic = 0; + + try + { + packet >> magic; + } + catch (...) + { + return -1; + } + + if (magic == SOLICIT) + { + Packet out; + out << RESPONSE << mAddress.service() << mText; + sock.socket().write(out); + return 0; + } + return -1; } -void ServiceFinder::update(Scalar t, Scalar dt) +int ServiceFinder::handlePacket(SocketMultiplexer& sock, + Packet& packet, + const SocketAddress& address) +{ + try + { + uint32_t magic; + packet >> magic; + if (magic == RESPONSE) + { + std::string service; + std::string text; + + packet >> service >> text; + if (service == mService) + { + mServices.push_back(Service(address, text)); + return 0; + } + } + } + catch (...) + { + } + + return -1; +} + +ServiceFinder::ServiceFinder(const std::string& service, int type) { } diff --git a/src/Moof/Service.hh b/src/Moof/Service.hh index 7c5e6b7..77cee18 100644 --- a/src/Moof/Service.hh +++ b/src/Moof/Service.hh @@ -9,6 +9,11 @@ * **************************************************************************/ +/** + * \file Service.hh + * Classes for publishing and finding network services. + */ + #ifndef _MOOF_SERVICE_HH_ #define _MOOF_SERVICE_HH_ @@ -36,29 +41,39 @@ public: Service(const SocketAddress& address, const std::string& text); + /** + * Publish the service on the local network. + */ + void publish(); + + void stop(); + + /** * Get the host address. * \return The address. */ - const SocketAddress& address() const; + const SocketAddress& address() const + { + return mAddress; + } /** * Get the service information. * \return The service information as a string. */ - const std::string& text() const; - - - /** - * Publish the service on the network. - */ - void publish(); - - void update(Scalar t, Scalar dt); + const std::string& text() const + { + return mText; + } private: + int handlePacket(SocketMultiplexer& sock, + Packet& packet, + const SocketAddress& address); + SocketAddress mAddress; std::string mText; }; @@ -70,8 +85,6 @@ public: ServiceFinder(const std::string& service, int type = SOCK_STREAM); - void update(Scalar t, Scalar dt); - const std::vector& services() const { @@ -81,7 +94,12 @@ public: private: - std::vector mServices; + int handlePacket(SocketMultiplexer& sock, + Packet& packet, + const SocketAddress& address); + + std::string mService; + std::vector mServices; }; diff --git a/src/Moof/Socket.hh b/src/Moof/Socket.hh index 2411cc2..da8132a 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(mService, mName); } /** @@ -121,7 +127,7 @@ public: mType(type) { memcpy(&mAddr.sa, addr, size); - getServiceAndHostName(mService, mHost); + getNameAndService(mService, mName); } @@ -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. @@ -858,6 +902,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 +974,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 +1026,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 +1044,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_ -- 2.45.2