Mercurial > code
changeset 506:627284574e42
Sockets: massive update
- Separate Ip in Ipv4 and Ipv6,
- Socket have no state, condition, action anymore (use Condition instead),
- Get rid of Stream(Client|Connection|Server).
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 22 Feb 2016 16:09:51 +0100 |
parents | ebe16d7afbe6 |
children | b2b2442e3291 |
files | modules/sockets/CMakeLists.txt modules/sockets/sockets.cpp modules/sockets/sockets.h modules/sockets/test/main.cpp |
diffstat | 4 files changed, 569 insertions(+), 1649 deletions(-) [+] |
line wrap: on
line diff
--- a/modules/sockets/CMakeLists.txt Wed Feb 03 14:06:18 2016 +0100 +++ b/modules/sockets/CMakeLists.txt Mon Feb 22 16:09:51 2016 +0100 @@ -26,6 +26,7 @@ code_define_module( NAME sockets INCLUDES ${OPENSSL_INCLUDE_DIR} + FLAGS -DSOCKET_NO_SSL LIBRARIES ${LIBRARIES} ${OPENSSL_LIBRARIES}
--- a/modules/sockets/sockets.cpp Wed Feb 03 14:06:18 2016 +0100 +++ b/modules/sockets/sockets.cpp Mon Feb 22 16:09:51 2016 +0100 @@ -185,30 +185,30 @@ /* {{{ Error */ Error::Error(Code code, std::string function) - : m_code{code} - , m_function{std::move(function)} - , m_error{error()} + : m_code(code) + , m_function(std::move(function)) + , m_error(error()) { } Error::Error(Code code, std::string function, int n) - : m_code{code} - , m_function{std::move(function)} - , m_error{error(n)} + : m_code(code) + , m_function(std::move(function)) + , m_error(error(n)) { } Error::Error(Code code, std::string function, std::string error) - : m_code{code} - , m_function{std::move(function)} - , m_error{std::move(error)} + : m_code(code) + , m_function(std::move(function)) + , m_error(std::move(error)) { } /* }}} */ /* - * Predefine addressed to be used + * Predefined addressed to be used * ------------------------------------------------------------------ */ @@ -216,76 +216,84 @@ namespace address { -/* Default domain */ -int Ip::m_default{AF_INET}; +namespace { -Ip::Ip(Type domain) noexcept - : m_domain(static_cast<int>(domain)) +void resolv(sockaddr *destination, const std::string host, std::uint16_t port, int domain) { - assert(m_domain == AF_INET6 || m_domain == AF_INET); + assert(domain == AF_INET || domain == AF_INET6); + + addrinfo hints, *res; + + std::memset(&hints, 0, sizeof (addrinfo)); + hints.ai_family = domain; + + auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); + if (error != 0) + throw Error(Error::System, "getaddrinfo", gai_strerror(error)); + + if (domain == AF_INET6) + std::memcpy(destination, res->ai_addr, res->ai_addrlen); + else + std::memcpy(destination, res->ai_addr, res->ai_addrlen); + + freeaddrinfo(res); +} - if (m_domain == AF_INET6) { - std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); +} // !namespace + +/* {{{ Ipv4 */ + +Ipv4::Ipv4() noexcept +{ + std::memset(&m_sin, 0, sizeof (sockaddr_in)); +} + +Ipv4::Ipv4(const std::string &host, std::uint16_t port) + : Ipv4() +{ + if (host == "*") { + m_sin.sin_addr.s_addr = INADDR_ANY; + m_sin.sin_family = AF_INET; + m_sin.sin_port = htons(port); } else { - std::memset(&m_sin, 0, sizeof (sockaddr_in)); + resolv(reinterpret_cast<sockaddr *>(&m_sin), host, port, AF_INET); } } -Ip::Ip(const std::string &host, int port, Type domain) - : m_domain(static_cast<int>(domain)) +Ipv4::Ipv4(const sockaddr_storage *ss, socklen_t length) noexcept { - assert(m_domain == AF_INET6 || m_domain == AF_INET); + std::memcpy(&m_sin, ss, length); +} - if (host == "*") { - if (m_domain == AF_INET6) { - std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); +/* }}} */ - m_length = sizeof (sockaddr_in6); - m_sin6.sin6_addr = in6addr_any; - m_sin6.sin6_family = AF_INET6; - m_sin6.sin6_port = htons(port); - } else { - std::memset(&m_sin, 0, sizeof (sockaddr_in)); +/* {{{ Ipv6 */ - m_length = sizeof (sockaddr_in); - m_sin.sin_addr.s_addr = INADDR_ANY; - m_sin.sin_family = AF_INET; - m_sin.sin_port = htons(port); - } - } else { - addrinfo hints, *res; - - std::memset(&hints, 0, sizeof (addrinfo)); - hints.ai_family = domain; +Ipv6::Ipv6() noexcept +{ + std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); +} - auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); - if (error != 0) { - throw Error{Error::System, "getaddrinfo", gai_strerror(error)}; - } - - if (m_domain == AF_INET6) { - std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen); - } else { - std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen); - } - - m_length = res->ai_addrlen; - freeaddrinfo(res); +Ipv6::Ipv6(const std::string &host, std::uint16_t port) + : Ipv6() +{ + if (host == "*") { + m_sin6.sin6_addr = in6addr_any; + m_sin6.sin6_family = AF_INET6; + m_sin6.sin6_port = htons(port); + } else { + resolv(reinterpret_cast<sockaddr *>(&m_sin6), host, port, AF_INET6); } } -Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept - : m_length{length} - , m_domain{ss->ss_family} +Ipv6::Ipv6(const sockaddr_storage *ss, socklen_t length) noexcept { - assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET); + std::memcpy(&m_sin6, ss, length); +} - if (ss->ss_family == AF_INET6) { - std::memcpy(&m_sin6, ss, length); - } else if (ss->ss_family == AF_INET) { - std::memcpy(&m_sin, ss, length); - } -} +/* }}} */ + +/* {{{ Local */ #if !defined(_WIN32) @@ -295,12 +303,11 @@ } Local::Local(std::string path, bool rm) noexcept - : m_path{std::move(path)} + : m_path(std::move(path)) { /* Silently remove the file even if it fails */ - if (rm) { + if (rm) ::remove(m_path.c_str()); - } /* Copy the path */ std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path)); @@ -322,11 +329,19 @@ #endif // !_WIN32 +/* }}} */ + } // !address /* }}} */ /* + * Listener backends + * ------------------------------------------------------------------ + */ + +/* {{{ Backends */ +/* * Select * ------------------------------------------------------------------ */ @@ -506,26 +521,22 @@ { uint32_t events = 0; - if ((condition & Condition::Readable) == Condition::Readable) { + if ((condition & Condition::Readable) == Condition::Readable) events |= EPOLLIN; - } - if ((condition & Condition::Writable) == Condition::Writable) { + if ((condition & Condition::Writable) == Condition::Writable) events |= EPOLLOUT; - } return events; } Condition Epoll::toCondition(uint32_t events) const noexcept { - Condition condition{Condition::None}; + Condition condition = Condition::None; - if ((events & EPOLLIN) || (events & EPOLLHUP)) { + if ((events & EPOLLIN) || (events & EPOLLHUP)) condition |= Condition::Readable; - } - if (events & EPOLLOUT) { + if (events & EPOLLOUT) condition |= Condition::Writable; - } return condition; } @@ -539,22 +550,21 @@ ev.events = eflags; ev.data.fd = h; - if (epoll_ctl(m_handle, op, h, &ev) < 0) { - throw Error{Error::System, "epoll_ctl"}; - } + if (epoll_ctl(m_handle, op, h, &ev) < 0) + throw Error(Error::System, "epoll_ctl"); } Epoll::Epoll() - : m_handle{epoll_create1(0)} + : m_handle(epoll_create1(0)) { - if (m_handle < 0) { + if (m_handle < 0) throw Error{Error::System, "epoll_create"}; - } } Epoll::~Epoll() { - close(m_handle); + if (m_handle != -1) + close(m_handle); } /* @@ -595,16 +605,13 @@ int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms); std::vector<ListenerStatus> result; - if (ret == 0) { - throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG}; - } - if (ret < 0) { - throw Error{Error::System, "epoll_wait"}; - } + if (ret == 0) + throw Error(Error::Timeout, "epoll_wait", TIMEOUT_MSG); + if (ret < 0) + throw Error(Error::System, "epoll_wait"); - for (int i = 0; i < ret; ++i) { + for (int i = 0; i < ret; ++i) result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)}); - } return result; } @@ -625,14 +632,14 @@ Kqueue::Kqueue() : m_handle(kqueue()) { - if (m_handle < 0) { - throw Error{Error::System, "kqueue"}; - } + if (m_handle < 0) + throw Error(Error::System, "kqueue"); } Kqueue::~Kqueue() { - close(m_handle); + if (m_handle != -1) + close(m_handle); } void Kqueue::update(Handle h, int filter, int kflags) @@ -706,4 +713,6 @@ /* }}} */ +/* }}} */ + } // !net
--- a/modules/sockets/sockets.h Wed Feb 03 14:06:18 2016 +0100 +++ b/modules/sockets/sockets.h Mon Feb 22 16:09:51 2016 +0100 @@ -161,6 +161,7 @@ #include <cassert> #include <chrono> +#include <cstdint> #include <cstdlib> #include <cstring> #include <exception> @@ -449,7 +450,6 @@ Connecting, //!< The connection is in progress Connected, //!< Connection is complete Accepted, //!< Socket has been accepted (client) - Accepting, //!< The client acceptation is in progress Closed, //!< The socket has been closed Disconnected, //!< The connection was lost }; @@ -457,39 +457,6 @@ /* }}} */ /* - * Action enum - * ------------------------------------------------------------------ - * - * Defines the pending operation. - */ - -/* {{{ Action */ - -/** - * @enum Action - * @brief Define the current operation that must complete. - * - * Some operations like accept, connect, recv or send must sometimes do several round-trips to complete and the socket - * action is set with that enumeration. The user is responsible of calling accept, connect send or recv until the - * operation is complete. - * - * Note: the user must wait for the appropriate condition in Socket::condition to check if the required condition is - * met. - * - * It is important to complete the operation in the correct order because protocols like Tls may require to continue - * re-negociating when calling Socket::send or Socket::Recv. - */ -enum class Action { - None, //!< No action is required, socket is ready - Accept, //!< The socket is not yet accepted, caller must call accept() again - Connect, //!< The socket is not yet connected, caller must call connect() again - Receive, //!< The received operation has not succeeded yet, caller must call recv() or recvfrom() again - Send //!< The send operation has not succeded yet, caller must call send() or sendto() again -}; - -/* }}} */ - -/* * Condition enum * ------------------------------------------------------------------ * @@ -627,9 +594,6 @@ class Socket { private: Protocol m_proto; - State m_state{State::Closed}; - Action m_action{Action::None}; - Condition m_condition{Condition::None}; protected: /** @@ -649,8 +613,6 @@ * @param protocol the protocol * @param iface the implementation * @throw net::Error on errors - * @post state is set to Open - * @post handle is not set to Invalid */ Socket(int domain, int type, int protocol, Protocol iface = {}) : m_proto(std::move(iface)) @@ -660,14 +622,10 @@ #endif m_handle = ::socket(domain, type, protocol); - if (m_handle == Invalid) { - throw Error{Error::System, "socket"}; - } + if (m_handle == Invalid) + throw Error(Error::System, "socket"); m_proto.create(*this); - m_state = State::Open; - - assert(m_handle != Invalid); } /** @@ -680,7 +638,7 @@ * @throw net::Error on errors */ explicit inline Socket(Protocol protocol = {}, const Address &address = {}) - : Socket{address.domain(), protocol.type(), 0, std::move(protocol)} + : Socket(address.domain(), protocol.type(), 0, std::move(protocol)) { } @@ -690,23 +648,18 @@ * @param handle the native descriptor * @param state specify the socket state * @param protocol the type of socket implementation - * @post action is set to None - * @post condition is set to None */ explicit inline Socket(Handle handle, State state = State::Closed, Protocol protocol = {}) noexcept : m_proto(std::move(protocol)) - , m_state{state} - , m_handle{handle} + , m_handle(handle) { - assert(m_action == Action::None); - assert(m_condition == Condition::None); } /** * Create an invalid socket. Can be used when you cannot instanciate the socket immediately. */ explicit inline Socket(std::nullptr_t) noexcept - : m_handle{Invalid} + : m_handle(Invalid) { } @@ -722,16 +675,10 @@ */ inline Socket(Socket &&other) noexcept : m_proto(std::move(other.m_proto)) - , m_state{other.m_state} - , m_action{other.m_action} - , m_condition{other.m_condition} - , m_handle{other.m_handle} + , m_handle(other.m_handle) { /* Invalidate other */ other.m_handle = Invalid; - other.m_state = State::Closed; - other.m_action = Action::None; - other.m_condition = Condition::None; } /** @@ -764,83 +711,31 @@ } /** - * Get the current socket state. - * - * @return the state - */ - inline State state() const noexcept - { - return m_state; - } - - /** - * Change the current socket state. + * Tells if the socket is not invalid. * - * @param state the new state - * @warning only implementations should call this function + * @return true if not invalid */ - inline void setState(State state) noexcept - { - m_state = state; - } - - /** - * Get the pending operation. - * - * @return the action to complete before continuing - * @note usually only needed in non-blocking sockets - */ - inline Action action() const noexcept + inline bool isOpen() const noexcept { - return m_action; - } - - /** - * Change the pending operation. - * - * @param action the action - * @warning you should not call this function yourself - */ - inline void setAction(Action action) noexcept - { - m_action = action; - } - - /** - * Get the condition to wait for. - * - * @return the condition - */ - inline Condition condition() const noexcept - { - return m_condition; - } - - /** - * Change the condition required. - * - * @param condition the condition - * @warning you should not call this function yourself - */ - inline void setCondition(Condition condition) noexcept - { - m_condition = condition; + return m_handle != Invalid; } /** * Set an option for the socket. Wrapper of setsockopt(2). * + * @pre isOpen() * @param level the setting level * @param name the name * @param arg the value * @throw net::Error on errors */ template <typename Argument> - void set(int level, int name, const Argument &arg) + inline void set(int level, int name, const Argument &arg) { - if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) { - throw Error{Error::System, "set"}; - } + assert(m_handle != Invalid); + + if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) + throw Error(Error::System, "set"); } /** @@ -848,18 +743,22 @@ * * The object must have `set(Socket<Address, Protocol> &) const`. * + * @pre isOpen() * @param option the option * @throw net::Error on errors */ template <typename Option> inline void set(const Option &option) { + assert(m_handle != Invalid); + option.set(*this); } /** * Get an option for the socket. Wrapper of getsockopt(2). * + * @pre isOpen() * @param level the setting level * @param name the name * @throw net::Error on errors @@ -867,12 +766,13 @@ template <typename Argument> Argument get(int level, int name) { + assert(m_handle != Invalid); + Argument desired, result{}; socklen_t size = sizeof (result); - if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) { - throw Error{Error::System, "get"}; - } + if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) + throw Error(Error::System, "get"); std::memcpy(&result, &desired, size); @@ -885,13 +785,16 @@ * The object must have `T get(Socket<Address, Protocol> &) const`, T can be any type and it is the value * returned from this function. * + * @pre isOpen() * @return the same value as get() in the option * @throw net::Error on errors */ template <typename Option> inline auto get() -> decltype(std::declval<Option>().get(*this)) { - return Option{}.get(*this); + assert(m_handle != Invalid); + + return Option().get(*this); } /** @@ -908,374 +811,244 @@ /** * Bind using a native address. * + * @pre isOpen() * @param address the address * @param length the size - * @pre state must not be Bound * @throw net::Error on errors */ - void bind(const sockaddr *address, socklen_t length) + inline void bind(const sockaddr *address, socklen_t length) { - assert(m_state != State::Bound); - - if (::bind(m_handle, address, length) == Failure) { - throw Error{Error::System, "bind"}; - } - - m_state = State::Bound; + assert(m_handle != Invalid); + + if (::bind(m_handle, address, length) == Failure) + throw Error(Error::System, "bind"); } /** * Overload that takes an address. * + * @pre isOpen() * @param address the address * @throw net::Error on errors */ inline void bind(const Address &address) { - bind(address.address(), address.length()); + assert(m_handle != Invalid); + + if (::bind(m_handle, address.address(), address.length()) == Failure) + throw Error(Error::System, "bind"); + } + + /** + * Overload that creates an address on the fly. + * + * @pre isOpen() + * @param args the arguments to Address constructor + * @throw net::Error on errors + */ + template <typename... Args> + inline void bind(Args&&... args) + { + assert(m_handle != Invalid); + + Address address{std::forward<Args>(args)...}; + + if (::bind(m_handle, address.address(), address.length()) == Failure) + throw Error(Error::System, "bind"); } /** * Listen for pending connection. * + * @pre isOpen() * @param max the maximum number - * @pre state must be Bound * @throw net::Error on errors */ inline void listen(int max = 128) { - assert(m_state == State::Bound); - - if (::listen(this->m_handle, max) == Failure) { - throw Error{Error::System, "listen"}; - } + assert(m_handle != Invalid); + + if (::listen(this->m_handle, max) == Failure) + throw Error(Error::System, "listen"); } /** - * Connect to the address. * - * If connection cannot be established immediately, connect with no argument must be called again. See - * the underlying protocol for more information. - * - * @pre state must be State::Open + * @pre isOpen() * @param address the address * @param length the the address length + * @param condition the condition to wait (Optional) * @throw net::Error on errors - * @post state is set to State::Connecting or State::Connected - * @note For non-blocking sockets, see the underlying protocol function for more details */ - void connect(const sockaddr *address, socklen_t length) + inline void connect(const sockaddr *address, socklen_t length, Condition *condition = nullptr) { - assert(m_state == State::Open); - - m_action = Action::None; - m_condition = Condition::None; - - m_proto.connect(*this, address, length); - - assert((m_state == State::Connected && m_action == Action::None && m_condition == Condition::None) || - (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None)); + assert(m_handle != Invalid); + + m_proto.connect(*this, address, length, condition); + } /** * Overloaded function. * - * Effectively call connect(address.address(), address.length()); + * Effectively call connect(address.address(), address.length(), condition); * + * @pre isOpen() * @param address the address + * @param condition the condition to wait (Optional) */ - inline void connect(const Address &address) - { - connect(address.address(), address.length()); - } - - /** - * Continue the connection, only required with non-blocking sockets. - * - * @pre state must be State::Connecting - * @throw net::Error on errors - */ - void connect() + inline void connect(const Address &address, Condition *condition = nullptr) { - assert(m_state == State::Connecting); - - m_action = Action::None; - m_condition = Condition::None; - - m_proto.connect(*this); - - assert((m_state == State::Connected && m_action == Action::None && m_condition == Condition::None) || - (m_state == State::Connecting && m_action == Action::Connect && m_condition != Condition::None)); + assert(m_handle != Invalid); + + connect(address.address(), address.length(), condition); } - /** - * Accept a new client. If there are no pending connection, throws an error. - * - * If the client cannot be accepted immediately, the client is returned and accept with no arguments - * must be called on it. See the underlying protocol for more information. - * - * @pre state must be State::Bound - * @param info the address where to store client's information (optional) - * @return the new socket - * @throw Error on errors - * @post returned client's state is set to State::Accepting or State::Accepted - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - Socket<Address, Protocol> accept(Address *info) + inline void connect(Condition &condition) { - assert(m_state == State::Bound); - - m_action = Action::None; - m_condition = Condition::None; + m_proto.connect(*this, &condition); + } + + Socket<Address, Protocol> accept(Address *info, Condition *condition = nullptr) + { + assert(m_handle != Invalid); sockaddr_storage storage; socklen_t length = sizeof (storage); - Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length); - - if (info) { + Socket<Address, Protocol> sc = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length, condition); + + if (info) *info = Address{&storage, length}; - } - - /* Master do not change */ - assert(m_state == State::Bound); - assert(m_action == Action::None); - assert(m_condition == Condition::None); - - /* Client */ - assert( - (sc.state() == State::Accepting && sc.action() == Action::Accept && sc.condition() != Condition::None) || - (sc.state() == State::Accepted && sc.action() == Action::None && sc.condition() == Condition::None) - ); return sc; } - /** - * Continue the accept process on this client. This function must be called only when the socket is - * ready to be readable or writable! (see condition). - * - * @pre state must be State::Accepting - * @throw Error on errors - * @post if connection is complete, state is changed to State::Accepted, action and condition are unset - * @post if connection is still in progress, condition is set - */ - void accept() + inline void accept(Condition &condition) { - assert(m_state == State::Accepting); - - m_action = Action::None; - m_condition = Condition::None; - - m_proto.accept(*this); - - assert( - (m_state == State::Accepting && m_action == Action::Accept && m_condition != Condition::None) || - (m_state == State::Accepted && m_action == Action::None && m_condition == Condition::None) - ); + assert(m_handle != Invalid); + + m_proto.accept(*this, &condition); } /** * Get the local name. This is a wrapper of getsockname(). * + * @pre isOpen() * @return the address * @throw Error on failures * @pre state() must not be State::Closed */ Address address() const { - assert(m_state != State::Closed); + assert(m_handle != Invalid); sockaddr_storage ss; socklen_t length = sizeof (sockaddr_storage); - if (::getsockname(m_handle, (sockaddr *)&ss, &length) == Failure) { - throw Error{Error::System, "getsockname"}; - } + if (::getsockname(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure) + throw Error(Error::System, "getsockname"); return Address(&ss, length); } /** - * Receive some data. + * * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * - * If action is set to Action::None and result is set to 0, disconnection occured. - * + * @pre isOpen() * @param data the destination buffer * @param length the buffer length - * @pre action must not be Action::Send * @return the number of bytes received or 0 * @throw Error on error - * @note For non-blocking sockets, see the underlying protocol function for more details */ - unsigned recv(void *data, unsigned length) + inline unsigned recv(void *data, unsigned length, Condition *condition = nullptr) { - assert(m_action != Action::Send); - - m_action = Action::None; - m_condition = Condition::None; - - return m_proto.recv(*this, data, length); + assert(m_handle != Invalid); + + return m_proto.recv(*this, data, length, condition); } /** * Overloaded function. * + * @pre isOpen() * @param count the number of bytes to receive * @return the string * @throw Error on error */ - inline std::string recv(unsigned count) + std::string recv(unsigned count, Condition *condition = nullptr) { + assert(m_handle != Invalid); + std::string result; result.resize(count); - auto n = recv(const_cast<char *>(result.data()), count); + auto n = recv(const_cast<char *>(result.data()), count, condition); result.resize(n); return result; } /** - * Send some data. + * * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * + * @pre isOpen() * @param data the data buffer * @param length the buffer length * @return the number of bytes sent or 0 - * @pre action() must not be Flag::Receive - * @throw Error on error - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - unsigned send(const void *data, unsigned length) - { - assert(m_action != Action::Receive); - - m_action = Action::None; - m_condition = Condition::None; - - return m_proto.send(*this, data, length); - } - - /** - * Overloaded function. - * - * @param data the string to send - * @return the number of bytes sent * @throw Error on error */ - inline unsigned send(const std::string &data) + unsigned send(const void *data, unsigned length, Condition *condition = nullptr) { - return send(data.c_str(), data.size()); + assert(m_handle != Invalid); + + return m_proto.send(*this, data, length, condition); } - /** - * Send data to an end point. - * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * - * @param data the buffer - * @param length the buffer length - * @param address the client address - * @param addrlen the address length - * @return the number of bytes sent - * @throw net::Error on errors - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen) + inline unsigned send(const std::string &data, Condition *condition = nullptr) + { + return m_proto.send(*this, data.c_str(), data.size(), condition); + } + + inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition *condition = nullptr) { return m_proto.sendto(*this, data, length, address, addrlen); } - /** - * Overloaded function. - * - * @param data the buffer - * @param length the buffer length - * @param address the destination - * @return the number of bytes sent - * @throw net::Error on errors - */ - inline unsigned sendto(const void *data, unsigned length, const Address &address) + inline unsigned sendto(const void *data, unsigned length, const Address &address, Condition *condition = nullptr) { - return sendto(data, length, address.address(), address.length()); + return m_proto.sendto(data, length, address.address(), address.length(), condition); } - /** - * Overloaded function. - * - * @param data the data - * @param address the address - * @return the number of bytes sent - * @throw net:;Error on errors - */ - inline unsigned sendto(const std::string &data, const Address &address) + inline unsigned sendto(const std::string &data, const Address &address, Condition *condition = nullptr) { - return sendto(data.c_str(), data.length(), address); + return m_proto.sendto(*this, data.c_str(), data.length(), address.address(), address.length(), condition); } - /** - * Receive data from an end point. - * - * If the operation cannot be complete immediately, 0 is returned and user must call the function - * again when ready. See the underlying protocol for more information. - * - * @param data the destination buffer - * @param length the buffer length - * @param address the address destination - * @param addrlen the address length (in/out) - * @return the number of bytes received - * @throw net::Error on errors - * @note For non-blocking sockets, see the underlying protocol function for more details - */ - inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen) + inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition *condition = nullptr) { return m_proto.recvfrom(*this, data, length, address, addrlen); } - /** - * Overloaded function. - * - * @param data the destination buffer - * @param length the buffer length - * @param info the address destination - * @return the number of bytes received - * @throw net::Error on errors - */ - inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr) + inline unsigned recvfrom(void *data, unsigned length, Address *info = nullptr, Condition *condition = nullptr) { sockaddr_storage storage; socklen_t addrlen = sizeof (sockaddr_storage); - auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen); - - if (info && n != 0) { + auto n = m_proto.recvfrom(*this, data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen, condition); + + if (info && n != 0) *info = Address{&storage, addrlen}; - } return n; } - /** - * Overloaded function. - * - * @param count the maximum number of bytes to receive - * @param info the client information - * @return the string - * @throw net::Error on errors - */ - std::string recvfrom(unsigned count, Address *info = nullptr) + std::string recvfrom(unsigned count, Address *info = nullptr, Condition *condition = nullptr) { std::string result; result.resize(count); - auto n = recvfrom(const_cast<char *>(result.data()), count, info); + auto n = recvfrom(const_cast<char *>(result.data()), count, info, condition); result.resize(n); return result; @@ -1296,10 +1069,6 @@ #endif m_handle = Invalid; } - - m_state = State::Closed; - m_action = Action::None; - m_condition = Condition::None; } /** @@ -1320,15 +1089,9 @@ { m_handle = other.m_handle; m_proto = std::move(other.m_proto); - m_state = other.m_state; - m_action = other.m_action; - m_condition = other.m_condition; /* Invalidate other */ other.m_handle = Invalid; - other.m_state = State::Closed; - other.m_action = Action::None; - other.m_condition = Condition::None; return *this; } @@ -1439,11 +1202,14 @@ * @warning On Windows, it's not possible to check if the socket is blocking or not. */ class SockBlockMode { +private: + bool m_value; + public: - /** - * Set to false if you want non-blocking socket. - */ - bool value{false}; + inline SockBlockMode(bool value = true) noexcept + : m_value(value) + { + } /** * Set the option. @@ -1457,25 +1223,21 @@ #if defined(O_NONBLOCK) && !defined(_WIN32) int flags; - if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) { + if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) flags = 0; - } - - if (value) { + + if (m_value) flags &= ~(O_NONBLOCK); - } else { + else flags |= O_NONBLOCK; - } - - if (fcntl(sc.handle(), F_SETFL, flags) < 0) { - throw Error{Error::System, "fcntl"}; - } + + if (fcntl(sc.handle(), F_SETFL, flags) < 0) + throw Error(Error::System, "fcntl"); #else - unsigned long flags = (value) ? 0 : 1; - - if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) { - throw Error{Error::System, "fcntl"}; - } + unsigned long flags = (m_value) ? 0 : 1; + + if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) + throw Error(Error::System, "fcntl"); #endif } @@ -1491,13 +1253,12 @@ #if defined(O_NONBLOCK) && !defined(_WIN32) int flags = fcntl(sc.handle(), F_GETFL, 0); - if (flags < 0) { - throw Error{Error::System, "fcntl"}; - } + if (flags < 0) + throw Error(Error::System, "fcntl"); return !(flags & O_NONBLOCK); #else - throw Error{Error::Other, "get", "Windows API cannot let you get the blocking status of a socket"}; + throw Error(Error::Other, "get", std::strerror(ENOSYS)); #endif } }; @@ -1507,11 +1268,14 @@ * @brief Reuse address, must be used before calling Socket::bind */ class SockReuseAddress { +private: + bool m_value; + public: - /** - * Set to true if you want to set the SOL_SOCKET/SO_REUSEADDR option. - */ - bool value{true}; + inline SockReuseAddress(bool value = true) noexcept + : m_value(value) + { + } /** * Set the option. @@ -1522,7 +1286,7 @@ template <typename Address, typename Protocol> inline void set(Socket<Address, Protocol> &sc) const { - sc.set(SOL_SOCKET, SO_REUSEADDR, value ? 1 : 0); + sc.set(SOL_SOCKET, SO_REUSEADDR, m_value ? 1 : 0); } /** @@ -1612,16 +1376,21 @@ /* }}} */ +/* {{{ Options for TCP */ + /** * @class TcpNoDelay * @brief Set this option if you want to disable nagle's algorithm. */ class TcpNoDelay { +private: + bool m_value; + public: - /** - * Set to true to set TCP_NODELAY option. - */ - bool value{true}; + inline TcpNoDelay(bool value = true) noexcept + : m_value(value) + { + } /** * Set the option. @@ -1632,7 +1401,7 @@ template <typename Address, typename Protocol> inline void set(Socket<Address, Protocol> &sc) const { - sc.set(IPPROTO_TCP, TCP_NODELAY, value ? 1 : 0); + sc.set(IPPROTO_TCP, TCP_NODELAY, m_value ? 1 : 0); } /** @@ -1648,6 +1417,10 @@ } }; +/* }}} */ + +/* {{{ Options for IPv6 */ + /** * @class Ipv6Only * @brief Control IPPROTO_IPV6/IPV6_V6ONLY @@ -1656,11 +1429,14 @@ * false or true if portability is a concern. */ class Ipv6Only { +private: + bool m_value; + public: - /** - * Set this to use only IPv6. - */ - bool value{true}; + inline Ipv6Only(bool value = true) noexcept + : m_value(value) + { + } /** * Set the option. @@ -1671,7 +1447,7 @@ template <typename Address, typename Protocol> inline void set(Socket<Address, Protocol> &sc) const { - sc.set(IPPROTO_IPV6, IPV6_V6ONLY, value ? 1 : 0); + sc.set(IPPROTO_IPV6, IPV6_V6ONLY, m_value ? 1 : 0); } /** @@ -1687,6 +1463,8 @@ } }; +/* }}} */ + } // !option /* }}} */ @@ -1695,7 +1473,8 @@ * Predefined addressed to be used * ------------------------------------------------------------------ * - * - Ip, + * - Ipv4, + * - Ipv6, * - Local. */ @@ -1706,107 +1485,23 @@ */ namespace address { -/** - * @class Ip - * @brief Base class for IPv6 and IPv4, you can use it if you don't know in advance if you'll use IPv6 or IPv4. - */ -class Ip { +class Ipv4 { public: - /** - * @enum Type - * @brief Type of ip address. - */ - enum Type { - v4 = AF_INET, //!< AF_INET - v6 = AF_INET6 //!< AF_INET6 - }; - private: - /* - * Default domain when using default constructors. - * - * Note: AF_INET or AF_INET6, not - */ - static int m_default; - - union { - mutable sockaddr_in m_sin; - mutable sockaddr_in6 m_sin6; - }; - - socklen_t m_length{0}; - int m_domain{AF_INET}; + sockaddr_in m_sin; public: - /** - * Set the default domain to use when using default Ip constructor. By default, AF_INET is used. - * - * @pre domain must be Type::v4 or Type::v6 - */ - static inline void setDefault(Type domain) noexcept - { - assert(domain == Type::v4 || domain == Type::v6); - - m_default = static_cast<int>(domain); - } - - /** - * Construct using the default domain. - */ - inline Ip() noexcept - : Ip(static_cast<Type>(m_default)) - { - } - - /** - * Default initialize the Ip domain. - * - * @pre domain must be AF_INET or AF_INET6 only - * @param domain the domain (AF_INET or AF_INET6) - */ - Ip(Type domain) noexcept; - - /** - * Construct an address suitable for bind() or connect(). - * - * @pre domain must be Type::v4 or Type::v6 - * @param domain the domain (AF_INET or AF_INET6) - * @param host the host (* for any) - * @param port the port number - * @throw Error on errors - */ - Ip(const std::string &host, int port, Type domain = v4); - - /** - * Construct an address from a storage. - * - * @pre storage's domain must be AF_INET or AF_INET6 only - * @param ss the storage - * @param length the length - */ - Ip(const sockaddr_storage *ss, socklen_t length) noexcept; - - /** - * Get the domain (AF_INET or AF_INET6). - * - * @return the domain - */ + Ipv4() noexcept; + Ipv4(const std::string &host, std::uint16_t port); + Ipv4(const sockaddr_storage *ss, socklen_t length) noexcept; + inline int domain() const noexcept { - return m_domain; + return AF_INET; } - /** - * Return the underlying address, either sockaddr_in6 or sockaddr_in. - * - * @return the address - */ inline const sockaddr *address() const noexcept { - if (m_domain == AF_INET6) { - return reinterpret_cast<const sockaddr *>(&m_sin6); - } - return reinterpret_cast<const sockaddr *>(&m_sin); } @@ -1817,7 +1512,7 @@ */ inline socklen_t length() const noexcept { - return m_length; + return sizeof (sockaddr_in); } /** @@ -1825,12 +1520,8 @@ * * @return the port */ - inline int port() const noexcept + inline std::uint16_t port() const noexcept { - if (m_domain == AF_INET6) { - return ntohs(m_sin6.sin6_port); - } - return ntohs(m_sin.sin_port); } @@ -1846,15 +1537,66 @@ std::memset(result, 0, sizeof (result)); - if (m_domain == AF_INET6) { - if (!inet_ntop(AF_INET6, &m_sin6.sin6_addr, result, sizeof (result))) { - throw Error{Error::System, "inet_ntop"}; - } - } else { - if (!inet_ntop(AF_INET, &m_sin.sin_addr, result, sizeof (result))) { - throw Error{Error::System, "inet_ntop"}; - } - } + if (!inet_ntop(AF_INET, &m_sin.sin_addr, result, sizeof (result))) + throw Error(Error::System, "inet_ntop"); + + return result; + } +}; + +class Ipv6 { +private: + sockaddr_in6 m_sin6; + +public: + Ipv6() noexcept; + Ipv6(const std::string &host, std::uint16_t port); + Ipv6(const sockaddr_storage *ss, socklen_t length) noexcept; + + inline int domain() const noexcept + { + return AF_INET6; + } + + inline const sockaddr *address() const noexcept + { + return reinterpret_cast<const sockaddr *>(&m_sin6); + } + + /** + * Return the underlying address length. + * + * @return the length + */ + inline socklen_t length() const noexcept + { + return sizeof (sockaddr_in6); + } + + /** + * Get the port. + * + * @return the port + */ + inline std::uint16_t port() const noexcept + { + return ntohs(m_sin6.sin6_port); + } + + /** + * Get the IP address in textual form. + * + * @return the address + * @throw Error on errors + */ + std::string ip() const + { + char result[128]; + + std::memset(result, 0, sizeof (result)); + + if (!inet_ntop(AF_INET6, &m_sin6.sin6_addr, result, sizeof (result))) + throw Error(Error::System, "inet_ntop"); return result; } @@ -1985,31 +1727,12 @@ /* No-op */ } - /** - * Standard connect. - * - * If the socket is marked non-blocking and the connection cannot be established immediately, then the - * following is true: - * - * - state is set to State::Connecting, - * - action is set to Action::Connect, - * - condition is set to Condition::Writable. - * - * Then the user must wait until the socket is writable and call connect() with 0 arguments. - * - * If the socket is blocking, this function blocks until the connection is complete or an error occurs, in - * that case state is either set to State::Connected or State::Disconnected but action and condition are - * not set. - * - * @param sc the socket - * @param address the address - * @param length the length - * @throw net::Error on errors - * @note Wrapper of connect(2) - */ template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length) + void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length, Condition *condition) { + if (condition) + *condition = Condition::None; + if (::connect(sc.handle(), address, length) == Failure) { /* * Determine if the error comes from a non-blocking connect that cannot be @@ -2019,118 +1742,61 @@ int error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) { - sc.setState(State::Connecting); - sc.setAction(Action::Connect); - sc.setCondition(Condition::Writable); + if (condition) + *condition = Condition::Writable; } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "connect", error}; + throw Error(Error::System, "connect", error); } #else if (errno == EINPROGRESS) { - sc.setState(State::Connecting); - sc.setAction(Action::Connect); - sc.setCondition(Condition::Writable); + if (condition) + *condition = Condition::Writable; } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "connect"}; + throw Error(Error::System, "connect"); } #endif - } else { - sc.setState(State::Connected); } } - /** - * Continue the connection. This function must only be called when the socket is ready for writing, - * the user is responsible of waiting for that condition. - * - * This function check for SOL_SOCKET/SO_ERROR status. - * - * If the connection is complete, status is set to State::Connected, otherwise it is set to - * State::Disconnected. In both cases, action and condition are not set. - * - * @param sc the socket - * @throw net::Error on errors - */ template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc) + void connect(Socket<Address, Protocol> &sc, Condition *condition = nullptr) { + if (condition) + *condition = Condition::None; + int error = sc.template get<int>(SOL_SOCKET, SO_ERROR); - if (error != 0) { - sc.setState(State::Disconnected); - throw Error{Error::System, "connect", error}; - } - - sc.setState(State::Connected); + if (error != 0) + throw Error(Error::System, "connect", error); } - /** - * Accept a clear client. - * - * If the socket is marked non-blocking and there are no pending connection, this function throws an - * error. The user must wait that the socket is readable before calling this function. - * - * If the socket is blocking, this function blocks until a new client is connected or throws an error on - * errors. - * - * If the socket is correctly returned, its state is set to State::Accepted and its action and condition - * are not set. - * - * In any case, action and condition of this socket are not set. - * - * @param sc the socket - * @param address the address destination - * @param length the address length - * @return the socket - * @throw net::Error on errors - * @note Wrapper of accept(2) - */ template <typename Address, typename Protocol> - Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length) + Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length, Condition *condition = nullptr) { + if (condition) + *condition = Condition::None; + Handle handle = ::accept(sc.handle(), address, length); - if (handle == Invalid) { - throw Error{Error::System, "accept"}; - } - - return Socket<Address, Protocol>{handle, State::Accepted}; + if (handle == Invalid) + return Socket<Address, Protocol>(); + + return Socket<Address, Protocol>(handle); } - /** - * Continue accept. - * - * This function is just present for compatibility, it should never be called. - */ template <typename Address, typename Protocol> - inline void accept(Socket<Address, Protocol> &) const noexcept + inline void accept(Socket<Address, Protocol> &, Condition *condition) const noexcept { - /* no-op */ + if (condition) + *condition = Condition::None; } - /** - * Receive data. - * - * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to - * Condition::Readable. If 0 is returned and condition is not set, then the state is set to - * State::Disconnected. - * - * If the socket is blocking, this function blocks until some data is available or if an error occurs. - * - * In any case, action is never set. - * - * @param sc the socket - * @param data the destination - * @param length the destination length - * @return the number of bytes read - * @throw Error on errors - * @note Wrapper of recv(2) - */ template <typename Address> - unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length) + unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length, Condition *condition) { + if (condition) + *condition = Condition::None; + int nbread = ::recv(sc.handle(), (Arg)data, length, 0); if (nbread == Failure) { @@ -2139,46 +1805,33 @@ if (error == WSAEWOULDBLOCK) { nbread = 0; - sc.setCondition(Condition::Readable); + + if (condition) + condition = Condition::Readable; } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "recv", error}; + throw Error(Error::System, "recv", error); } #else if (errno == EAGAIN || errno == EWOULDBLOCK) { - sc.setCondition(Condition::Readable); + nbread = 0; + + if (condition) + *condition = Condition::Readable; } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "recv"}; + throw Error(Error::System, "recv"); } #endif - } else if (nbread == 0) { - sc.setState(State::Disconnected); } return static_cast<unsigned>(nbread); } - /** - * Send some data. - * - * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to - * Condition::Writable. - * - * If the socket is blocking, this function blocks until the data has been sent. - * - * On any other errors, this function throw net::Error. - * - * @param sc the socket - * @param data the buffer to send - * @param length the buffer length - * @return the number of bytes sent - * @throw net::Error on errors - * @note Wrapper of send(2) - */ template <typename Address> - unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length) + unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length, Condition *condition) { + if (condition) + *condition = Condition::None; + int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0); if (nbsent == Failure) { @@ -2187,18 +1840,20 @@ if (error == WSAEWOULDBLOCK) { nbsent = 0; - sc.setCondition(Condition::Writable); + + if (condition) + *condition = Condition::Writable; } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "send", error}; + throw Error(Error::System, "send", error); } #else if (errno == EAGAIN || errno == EWOULDBLOCK) { nbsent = 0; - sc.setCondition(Condition::Writable); + + if (condition) + *condition = Condition::Writable; } else { - sc.setState(State::Disconnected); - throw Error{Error::System, "send"}; + throw Error(Error::System, "send"); } #endif } @@ -2238,25 +1893,12 @@ /* No-op */ } - /** - * Receive data from an end point. - * - * If the socket is marked non-blocking and no data is available, 0 is returned and condition is set to - * Condition::Readable. - * - * If the socket is blocking, this functions blocks until some data is available or if an error occurs. - * - * @param sc the socket - * @param data the destination buffer - * @param length the buffer length - * @param address the address - * @param addrlen the initial address length - * @return the number of bytes received - * @throw Error on error - */ template <typename Address> - unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen) + unsigned recvfrom(Socket<Address, Udp> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition *condition) { + if (condition) + *condition = Condition::Readable; + int nbread; nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen); @@ -2267,16 +1909,20 @@ if (error == WSAEWOULDBLOCK) { nbread = 0; - sc.setCondition(Condition::Readable); + + if (condition) + *condition = Condition::Writable; } else { - throw Error{Error::System, "recvfrom"}; + throw Error(Error::System, "recvfrom"); } #else if (errno == EAGAIN || errno == EWOULDBLOCK) { nbread = 0; - sc.setCondition(Condition::Readable); + + if (condition) + *condition = Condition::Writable; } else { - throw Error{Error::System, "recvfrom"}; + throw Error(Error::System, "recvfrom"); } #endif } @@ -2284,25 +1930,12 @@ return static_cast<unsigned>(nbread); } - /** - * Send data to an end point. - * - * If the socket is marked non-blocking and the operation would block, then 0 is returned and condition is set to - * Condition::Writable. - * - * If the socket is blocking, this functions blocks until the data has been sent. - * - * @param sc the socket - * @param data the buffer - * @param length the buffer length - * @param address the client address - * @param addrlen the adderss length - * @return the number of bytes sent - * @throw Error on error - */ template <typename Address> - unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen) + unsigned sendto(Socket<Address, Udp> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition *condition) { + if (condition) + *condition = Condition::None; + int nbsent; nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen); @@ -2312,16 +1945,20 @@ if (error == WSAEWOULDBLOCK) { nbsent = 0; - sc.setCondition(Condition::Writable); + + if (condition) + *condition = Condition::Writable; } else { - throw Error{Error::System, "sendto", error}; + throw Error(Error::System, "sendto", error); } #else if (errno == EAGAIN || errno == EWOULDBLOCK) { nbsent = 0; - sc.setCondition(Condition::Writable); + + if (condition) + *condition = Condition::Writable; } else { - throw Error{Error::System, "sendto"}; + throw Error(Error::System, "sendto"); } #endif } @@ -2373,8 +2010,8 @@ * Construct with a context and ssl, for Tls::accept. */ Tls(Context context, Ssl ssl) - : m_context{std::move(context)} - , m_ssl{std::move(ssl)} + : m_context(std::move(context)) + , m_ssl(std::move(ssl)) { } @@ -2737,9 +2374,14 @@ using SocketTcp = Socket<Address, protocol::Tcp>; /** - * Helper to create TCP/IP sockets. + * Helper to create TCP/IPv4 sockets. */ -using SocketTcpIp = Socket<address::Ip, protocol::Tcp>; +using SocketTcpIp = Socket<address::Ipv4, protocol::Tcp>; + +/** + * Helper to create TCP/IPv6 sockets. + */ +using SocketTcpIpv6 = Socket<address::Ipv6, protocol::Tcp>; #if !defined(_WIN32) @@ -2757,9 +2399,14 @@ using SocketUdp = Socket<Address, protocol::Udp>; /** - * Helper to create UDP/IP sockets. + * Helper to create UDP/IPv4 sockets. */ -using SocketUdpIp = Socket<address::Ip, protocol::Udp>; +using SocketUdpIp = Socket<address::Ipv4, protocol::Udp>; + +/** + * Helper to create UDP/IPv6 sockets. + */ +using SocketUdpIpv6 = Socket<address::Ipv6, protocol::Udp>; #if !defined(SOCKET_NO_SSL) @@ -2779,13 +2426,13 @@ /* }}} */ /* - * Select wrapper + * Listener * ------------------------------------------------------------------ * * Wrapper for select(2) and other various implementations. */ -/* {{{ Listener */ +/* {{{ Listener stuff */ /** * @class ListenerStatus @@ -2806,6 +2453,10 @@ */ using ListenerTable = std::map<Handle, Condition>; +/* {{{ Backends */ + +/* {{{ Select */ + /** * @class Select * @brief Implements select(2) @@ -2838,6 +2489,10 @@ } }; +/* }}} */ + +/* {{{ Poll */ + #if defined(SOCKET_HAVE_POLL) /** @@ -2881,6 +2536,10 @@ #endif +/* }}} */ + +/* {{{ Epoll */ + #if defined(SOCKET_HAVE_EPOLL) /** @@ -2889,13 +2548,11 @@ */ class Epoll { private: - int m_handle; + int m_handle{-1}; std::vector<epoll_event> m_events; Epoll(const Epoll &) = delete; Epoll &operator=(const Epoll &) = delete; - Epoll(const Epoll &&) = delete; - Epoll &operator=(const Epoll &&) = delete; uint32_t toEpoll(Condition flags) const noexcept; Condition toCondition(uint32_t events) const noexcept; @@ -2907,6 +2564,20 @@ */ Epoll(); + inline Epoll(const Epoll &&other) noexcept + : m_handle(other.m_handle) + { + other.m_handle = -1; + } + + Epoll &operator=(const Epoll &&other) + { + m_handle = other.m_handle; + other.m_handle = -1; + + return *this; + } + /** * Close the epoll instance. */ @@ -2938,6 +2609,10 @@ #endif +/* }}} */ + +/* {{{ Kqueue */ + #if defined(SOCKET_HAVE_KQUEUE) /** @@ -2954,8 +2629,6 @@ Kqueue(const Kqueue &) = delete; Kqueue &operator=(const Kqueue &) = delete; - Kqueue(Kqueue &&) = delete; - Kqueue &operator=(Kqueue &&) = delete; void update(Handle sc, int filter, int kflags); @@ -2965,6 +2638,20 @@ */ Kqueue(); + inline Kqueue(Kqueue &&other) noexcept + : m_handle(other.m_handle) + { + other.m_handle = -1; + } + + inline Kqueue &operator=(Kqueue &&other) noexcept + { + m_handle = other.m_handle; + other.m_handle = -1; + + return *this; + } + /** * Destroy the kqueue instance. */ @@ -2996,6 +2683,12 @@ #endif +/* }}} */ + +/* }}} */ + +/* {{{ Listener */ + /** * @class Listener * @brief Synchronous multiplexing @@ -3158,13 +2851,11 @@ } else { /* Remove flag if already present */ if ((condition & Condition::Readable) == Condition::Readable && - (it->second & Condition::Readable) == Condition::Readable) { + (it->second & Condition::Readable) == Condition::Readable) condition &= ~(Condition::Readable); - } if ((condition & Condition::Writable) == Condition::Writable && - (it->second & Condition::Writable) == Condition::Writable) { + (it->second & Condition::Writable) == Condition::Writable) condition &= ~(Condition::Writable); - } /* Still need a call? */ if (condition != Condition::None) { @@ -3198,13 +2889,11 @@ * state. */ if ((condition & Condition::Readable) == Condition::Readable && - (it->second & Condition::Readable) != Condition::Readable) { + (it->second & Condition::Readable) != Condition::Readable) condition &= ~(Condition::Readable); - } if ((condition & Condition::Writable) == Condition::Writable && - (it->second & Condition::Writable) != Condition::Writable) { + (it->second & Condition::Writable) != Condition::Writable) condition &= ~(Condition::Writable); - } if (condition != Condition::None) { /* Determine if it's a complete removal */ @@ -3212,11 +2901,10 @@ m_backend.unset(m_table, sc, condition, removal); - if (removal) { + if (removal) m_table.erase(it); - } else { + else it->second &= ~(condition); - } } } @@ -3237,9 +2925,8 @@ */ inline void clear() { - while (!m_table.empty()) { + while (!m_table.empty()) remove(m_table.begin()->first); - } } /** @@ -3302,785 +2989,6 @@ /* }}} */ -/* - * Callback - * ------------------------------------------------------------------ - * - * Function owner with tests. - */ - -/* {{{ Callback */ - -/** - * @class Callback - * @brief Convenient signal owner that checks if the target is valid. - * - * This class also catch all errors thrown from signals to avoid interfering with our process. - */ -template <typename... Args> -class Callback : public std::function<void (Args...)> { -public: - /** - * Inherited constructors. - */ - using std::function<void (Args...)>::function; - - /** - * Execute the callback only if a target is set. - */ - void operator()(Args... args) const - { - if (*this) { - try { - std::function<void (Args...)>::operator()(args...); - } catch (...) { - } - } - } -}; - -/* }}} */ - -/* - * StreamConnection - * ------------------------------------------------------------------ - * - * Client connected on the server side. - */ - -/* {{{ StreamConnection */ - -/** - * @class StreamConnection - * @brief Connected client on the server side. - * - * This object is created from StreamServer when a new client is connected, it is the higher - * level object of sockets and completely asynchronous. - */ -template <typename Address, typename Protocol> -class StreamConnection { -public: - /** - * Called when the output has changed. - */ - using WriteHandler = Callback<>; - -private: - /* Signals */ - WriteHandler m_onWrite; - - /* Sockets and output buffer */ - Socket<Address, Protocol> m_socket; - std::string m_output; - -public: - /** - * Create the connection. - * - * @param s the socket - */ - StreamConnection(Socket<Address, Protocol> s) - : m_socket{std::move(s)} - { - m_socket.set(net::option::SockBlockMode{false}); - } - - /** - * Access the underlying socket. - * - * @return the socket - * @warning use with care - */ - inline Socket<Address, Protocol> &socket() noexcept - { - return m_socket; - } - - /** - * Access the current output. - * - * @return the output - */ - inline const std::string &output() const noexcept - { - return m_output; - } - - /** - * Overloaded function - * - * @return the output - * @warning use with care, avoid modifying the output if you don't know what you're doing - */ - inline std::string &output() noexcept - { - return m_output; - } - - /** - * Post some data to be sent asynchronously. - * - * @param str the data to append - */ - inline void send(std::string str) - { - m_output += str; - m_onWrite(); - } - - /** - * Kill the client. - */ - inline void close() - { - m_socket.close(); - } - - /** - * Set the write handler, the signal is emitted when the output has changed so that the StreamServer owner - * knows that there are some data to send. - * - * @param handler the handler - * @warning you usually never need to set this yourself - */ - inline void setWriteHandler(WriteHandler handler) - { - m_onWrite = std::move(handler); - } -}; - -/* }}} */ - -/* - * StreamServer - * ------------------------------------------------------------------ - * - * Convenient stream oriented server. - */ - -/* {{{ StreamServer */ - -/** - * @class StreamServer - * @brief Convenient stream server for TCP and TLS. - * - * This class does all the things for you as accepting new clients, listening for it and sending data. It works - * asynchronously without blocking to let you control your process workflow. - * - * This class is not thread safe and you must not call any of the functions from different threads. - */ -template <typename Address, typename Protocol> -class StreamServer { -public: - /** - * Handler when a new client is connected. - */ - using ConnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>; - - /** - * Handler when a client is disconnected. - */ - using DisconnectionHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &>; - - /** - * Handler when data has been received from a client. - */ - using ReadHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>; - - /** - * Handler when data has been correctly sent to a client. - */ - using WriteHandler = Callback<const std::shared_ptr<StreamConnection<Address, Protocol>> &, const std::string &>; - - /** - * Handler when an error occured. - */ - using ErrorHandler = Callback<const Error &>; - - /** - * Handler when there was a timeout. - */ - using TimeoutHandler = Callback<>; - -private: - using ClientMap = std::map<Handle, std::shared_ptr<StreamConnection<Address, Protocol>>>; - - /* Signals */ - ConnectionHandler m_onConnection; - DisconnectionHandler m_onDisconnection; - ReadHandler m_onRead; - WriteHandler m_onWrite; - ErrorHandler m_onError; - TimeoutHandler m_onTimeout; - - /* Sockets */ - Socket<Address, Protocol> m_master; - Listener<> m_listener; - ClientMap m_clients; - - /* - * Update flags depending on the required condition. - */ - void updateFlags(std::shared_ptr<StreamConnection<Address, Protocol>> &client) - { - assert(client->socket().action() != Action::None); - - m_listener.remove(client->socket().handle()); - m_listener.set(client->socket().handle(), client->socket().condition()); - } - - /* - * Continue accept process. - */ - template <typename AcceptCall> - void processAccept(std::shared_ptr<StreamConnection<Address, Protocol>> &client, const AcceptCall &acceptFunc) - { - try { - /* Do the accept */ - acceptFunc(); - - /* 1. First remove completely the client */ - m_listener.remove(client->socket().handle()); - - /* 2. If accept is not finished, wait for the appropriate condition */ - if (client->socket().state() == State::Accepted) { - /* 3. Client is accepted, notify the user */ - m_listener.set(client->socket().handle(), Condition::Readable); - m_onConnection(client); - } else { - /* Operation still in progress */ - updateFlags(client); - } - } catch (const Error &error) { - m_clients.erase(client->socket().handle()); - m_listener.remove(client->socket().handle()); - m_onError(error); - } - } - - /* - * Process initial accept of master socket, this is the initial accepting process. Except on errors, the - * socket is stored but the user will be notified only once the socket is completely accepted. - */ - void processInitialAccept() - { - // TODO: store address too. - std::shared_ptr<StreamConnection<Address, Protocol>> client = std::make_shared<StreamConnection<Address, Protocol>>(m_master.accept(nullptr)); - std::weak_ptr<StreamConnection<Address, Protocol>> ptr{client}; - - /* 1. Register output changed to update listener */ - client->setWriteHandler([this, ptr] () { - auto client = ptr.lock(); - - /* Do not update the listener immediately if an action is pending */ - if (client && client->socket().action() == Action::None && !client->output().empty()) { - m_listener.set(client->socket().handle(), Condition::Writable); - } - }); - - /* 2. Add the client */ - m_clients.insert(std::make_pair(client->socket().handle(), client)); - - /* - * 2. Do an initial check to set the listener flags, at this moment the socket may or not be - * completely accepted. - */ - processAccept(client, [&] () {}); - } - - /* - * Read or complete the read operation. - */ - void processRead(std::shared_ptr<StreamConnection<Address, Protocol>> &client) - { - /* - * Read because there is something to read or because the pending operation is - * read and must complete. - */ - auto buffer = client->socket().recv(512); - - /* - * Now the receive operation may be completed, in that case, two possibilities: - * - * 1. The action is set to None (completed) - * 2. The action is still not complete, update the flags - */ - if (client->socket().action() == Action::None) { - /* Empty mean normal disconnection */ - if (buffer.empty()) { - m_listener.remove(client->socket().handle()); - m_clients.erase(client->socket().handle()); - m_onDisconnection(client); - } else { - /* - * At this step, it is possible that we were completing a receive operation, in this - * case the write flag may be removed, add it if required. - */ - if (!client->output().empty()) { - m_listener.set(client->socket().handle(), Condition::Writable); - } - - m_onRead(client, buffer); - } - } else { - /* Operation in progress */ - updateFlags(client); - } - } - - /* - * Flush the output buffer. - */ - void processWrite(std::shared_ptr<StreamConnection<Address, Protocol>> &client) - { - auto &output = client->output(); - auto nsent = client->socket().send(output); - - if (client->socket().action() == Action::None) { - /* 1. Create a copy of content that has been sent */ - auto sent = output.substr(0, nsent); - - /* 2. Erase the content sent */ - output.erase(0, nsent); - - /* 3. Update listener */ - if (output.empty()) { - m_listener.unset(client->socket().handle(), Condition::Writable); - } - - /* 4. Notify user */ - m_onWrite(client, sent); - } else { - updateFlags(client); - } - } - - void processSync(std::shared_ptr<StreamConnection<Address, Protocol>> &client, Condition flags) - { - try { - auto action = client->socket().action(); - - if (action == Action::Receive || - (action == Action::None && (flags & Condition::Readable) == Condition::Readable)) { - processRead(client); - } else if ((flags & Condition::Writable) == Condition::Writable) { - processWrite(client); - } - } catch (const Error &error) { - m_onDisconnection(client); - m_listener.remove(client->socket().handle()); - m_clients.erase(client->socket().handle()); - } - } - -public: - /** - * Create a stream server with the specified address to bind. - * - * @param protocol the protocol (Tcp or Tls) - * @param address the address to bind - * @param max the max number to listen - * @throw Error on errors - */ - StreamServer(Protocol protocol, const Address &address, int max = 128) - : m_master{std::move(protocol), address} - { - // TODO: m_onError - m_master.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_master.bind(address); - m_master.listen(max); - m_listener.set(m_master.handle(), Condition::Readable); - } - - /** - * Set the connection handler, called when a new client is connected. - * - * @param handler the handler - */ - inline void setConnectionHandler(ConnectionHandler handler) - { - m_onConnection = std::move(handler); - } - - /** - * Set the disconnection handler, called when a client died. - * - * @param handler the handler - */ - inline void setDisconnectionHandler(DisconnectionHandler handler) - { - m_onDisconnection = std::move(handler); - } - - /** - * Set the receive handler, called when a client has sent something. - * - * @param handler the handler - */ - inline void setReadHandler(ReadHandler handler) - { - m_onRead = std::move(handler); - } - - /** - * Set the writing handler, called when some data has been sent to a client. - * - * @param handler the handler - */ - inline void setWriteHandler(WriteHandler handler) - { - m_onWrite = std::move(handler); - } - - /** - * Set the error handler, called when unrecoverable error has occured. - * - * @param handler the handler - */ - inline void setErrorHandler(ErrorHandler handler) - { - m_onError = std::move(handler); - } - - /** - * Set the timeout handler, called when the selection has timeout. - * - * @param handler the handler - */ - inline void setTimeoutHandler(TimeoutHandler handler) - { - m_onTimeout = std::move(handler); - } - - /** - * Poll for the next event. - * - * @param timeout the timeout (-1 for indefinitely) - * @throw Error on errors - */ - void poll(int timeout = -1) - { - try { - auto st = m_listener.wait(timeout); - - if (st.socket == m_master.handle()) { - /* New client */ - processInitialAccept(); - } else { - /* Recv / Send / Accept on a client */ - auto client = m_clients[st.socket]; - - if (client->socket().state() == State::Accepted) { - processSync(client, st.flags); - } else { - processAccept(client, [&] () { client->socket().accept(); }); - } - } - } catch (const Error &error) { - if (error.code() == Error::Timeout) { - m_onTimeout(); - } else { - m_onError(error); - } - } - } -}; - -/* }}} */ - -/* - * StreamClient - * ------------------------------------------------------------------ - */ - -/* {{{ StreamClient */ - -/** - * @class StreamClient - * @brief Client side connection to a server. - * - * This class is not thread safe and you must not call any of the functions from different threads. - */ -template <typename Address, typename Protocol> -class StreamClient { -public: - /** - * Handler when connection is complete. - */ - using ConnectionHandler = Callback<>; - - /** - * Handler when data has been received. - */ - using ReadHandler = Callback<const std::string &>; - - /** - * Handler when data has been sent correctly. - */ - using WriteHandler = Callback<const std::string &>; - - /** - * Handler when disconnected. - */ - using DisconnectionHandler = Callback<>; - - /** - * Handler on unrecoverable error. - */ - using ErrorHandler = Callback<const Error &>; - - /** - * Handler when timeout occured. - */ - using TimeoutHandler = Callback<>; - -private: - /* Signals */ - ConnectionHandler m_onConnection; - ReadHandler m_onRead; - WriteHandler m_onWrite; - DisconnectionHandler m_onDisconnection; - ErrorHandler m_onError; - TimeoutHandler m_onTimeout; - - /* Socket */ - Socket<Address, Protocol> m_socket; - Listener<> m_listener; - - /* Output buffer */ - std::string m_output; - - /* - * Update the flags after an uncompleted operation. This function must only be called when the operation - * has not complete (e.g. connect, recv, send). - */ - void updateFlags() - { - assert(m_socket.action() != Action::None); - - m_listener.remove(m_socket.handle()); - m_listener.set(m_socket.handle(), m_socket.condition()); - } - - /* - * This is the generic connect helper, it will be used to both initiate the connection or to continue the - * connection process if needed. - * - * Thus the template parameter is the appropriate function to call either, m_socket.connect(address) or - * m_socket.connect(). - * - * See poll() and connect() to understand. - */ - template <typename ConnectCall> - void processConnect(const ConnectCall &connectFunc) - { - /* Call m_socket.connect() or m_socket.connect(address) */ - connectFunc(); - - /* Remove entirely */ - m_listener.remove(m_socket.handle()); - - if (m_socket.state() == State::Connected) { - m_onConnection(); - m_listener.set(m_socket.handle(), Condition::Readable); - } else { - /* Connection still in progress */ - updateFlags(); - } - } - - /* - * Receive or complete the receive command, if the command is not complete, the listener is updated - * accordingly. - */ - void processRead() - { - auto received = m_socket.recv(512); - - if (m_socket.action() == Action::None) { - /* 0 means disconnection */ - if (received.empty()) { - m_onDisconnection(); - } else { - /* - * At this step, it is possible that we were completing a receive operation, in this - * case the write flag may be removed, add it if required. - */ - if (m_output.empty()) { - m_listener.unset(m_socket.handle(), Condition::Writable); - } - - m_onRead(received); - } - } else { - /* Receive operation in progress */ - updateFlags(); - } - } - - /* - * Send or complete the send command, if the command is not complete, the listener is updated - * accordingly. - */ - void processWrite() - { - auto nsent = m_socket.send(m_output); - - if (m_socket.action() == Action::None) { - /* 1. Make a copy of what has been sent */ - auto sent = m_output.substr(0, nsent); - - /* 2. Erase sent content */ - m_output.erase(0, nsent); - - /* 3. Update flags if needed */ - if (m_output.empty()) { - m_listener.unset(m_socket.handle(), Condition::Writable); - } - - /* 4. Notify user */ - m_onWrite(sent); - } else { - /* Send operation in progress */ - updateFlags(); - } - } - - /* - * Receive or send. - */ - void processSync(Condition condition) - { - if ((m_socket.action() == Action::Receive) || - (m_socket.action() == Action::None && (condition & Condition::Readable) == Condition::Readable)) { - processRead(); - } else { - processWrite(); - } - } - -public: - /** - * Create a client. The client is automatically marked as non-blocking. - * - * @param protocol the protocol (Tcp or Tls) - * @param address the optional address - * @throw net::Error on failures - */ - StreamClient(Protocol protocol = {}, const Address &address = {}) - : m_socket{std::move(protocol), address} - { - m_socket.set(net::option::SockBlockMode{false}); - m_listener.set(m_socket.handle(), Condition::Readable); - } - - /** - * Set the connection handler, called when the connection succeed. - * - * @param handler the handler - */ - inline void setConnectionHandler(ConnectionHandler handler) - { - m_onConnection = std::move(handler); - } - - /** - * Set the disconnection handler, called when the server closed the connection. - * - * @param handler the handler - */ - inline void setDisconnectionHandler(DisconnectionHandler handler) - { - m_onDisconnection = std::move(handler); - } - - /** - * Set the read handler, called when we received something. - * - * @param handler the handler - */ - inline void setReadHandler(ReadHandler handler) - { - m_onRead = std::move(handler); - } - - /** - * Set the write handler, called when we successfully sent data. - * - * @param handler the handler - */ - inline void setWriteHandler(WriteHandler handler) - { - m_onWrite = std::move(handler); - } - - /** - * Set the error handler, called when unexpected error occurs. - * - * @param handler the handler - */ - inline void setErrorHandler(ErrorHandler handler) - { - m_onError = std::move(handler); - } - - /** - * Connect to a server, this function may connect immediately or not in any case the connection handler - * will be called when the connection completed. - * - * @param address the address to connect to - */ - void connect(const Address &address) noexcept - { - assert(m_socket.state() == State::Open); - - processConnect([&] () { m_socket.connect(address); }); - } - - /** - * Asynchronously send data to the server. - * - * @param str the data to append - */ - void send(std::string str) - { - m_output += str; - - /* Don't update the listener if there is a pending operation */ - if (m_socket.state() == State::Connected && m_socket.action() == Action::None && !m_output.empty()) { - m_listener.set(m_socket.handle(), Condition::Writable); - } - } - - /** - * Wait for the next event. - * - * @param timeout the time to wait in milliseconds - * @throw Error on errors - */ - void poll(int timeout = -1) noexcept - { - try { - auto st = m_listener.wait(timeout); - - if (m_socket.state() != State::Connected) { - /* Continue the connection */ - processConnect([&] () { m_socket.connect(); }); - } else { - /* Read / Write */ - processSync(st.flags); - } - } catch (const Error &error) { - if (error.code() == Error::Timeout) { - m_onTimeout(); - } else { - m_listener.remove(m_socket.handle()); - m_onError(error); - } - } - } -}; - /* }}} */ } // !net
--- a/modules/sockets/test/main.cpp Wed Feb 03 14:06:18 2016 +0100 +++ b/modules/sockets/test/main.cpp Mon Feb 22 16:09:51 2016 +0100 @@ -43,10 +43,10 @@ SocketTcpIp s; try { - s.set(option::SockReuseAddress{true}); + s.set(option::SockReuseAddress(true)); ASSERT_TRUE(s.get<option::SockReuseAddress>()); - s.set(option::SockReuseAddress{false}); + s.set(option::SockReuseAddress(false)); ASSERT_FALSE(s.get<option::SockReuseAddress>()); } catch (const std::exception &ex) { FAIL() << ex.what(); @@ -58,10 +58,10 @@ SocketTcpIp s; try { - s.set(option::TcpNoDelay{true}); + s.set(option::TcpNoDelay(true)); ASSERT_TRUE(s.get<option::TcpNoDelay>()); - s.set(option::TcpNoDelay{false}); + s.set(option::TcpNoDelay(false)); ASSERT_FALSE(s.get<option::TcpNoDelay>()); } catch (const std::exception &ex) { FAIL() << ex.what(); @@ -70,13 +70,13 @@ TEST(Options, v6only) { - SocketTcpIp s{Tcp{}, Ip{Ip::v6}}; + SocketTcpIpv6 s; try { - s.set(option::Ipv6Only{true}); + s.set(option::Ipv6Only(true)); ASSERT_TRUE(s.get<option::Ipv6Only>()); - s.set(option::Ipv6Only{false}); + s.set(option::Ipv6Only(false)); ASSERT_FALSE(s.get<option::Ipv6Only>()); } catch (const std::exception &ex) { FAIL() << ex.what(); @@ -89,8 +89,8 @@ class TcpServerTest : public testing::Test { protected: - SocketTcp<Ip> m_server; - SocketTcp<Ip> m_client; + SocketTcp<Ipv4> m_server; + SocketTcp<Ipv4> m_client; std::thread m_tserver; std::thread m_tclient; @@ -98,7 +98,7 @@ public: TcpServerTest() { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.set(SockReuseAddress()); } ~TcpServerTest() @@ -113,7 +113,7 @@ TEST_F(TcpServerTest, connect) { m_tserver = std::thread([this] () { - m_server.bind(Ip{"*", 16000}); + m_server.bind(Ipv4("*", 16000)); m_server.listen(); m_server.accept(nullptr); m_server.close(); @@ -122,7 +122,7 @@ std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { - m_client.connect(Ip{"127.0.0.1", 16000}); + m_client.connect(Ipv4("127.0.0.1", 16000)); m_client.close(); }); } @@ -130,7 +130,7 @@ TEST_F(TcpServerTest, io) { m_tserver = std::thread([this] () { - m_server.bind(Ip{"*", 16000}); + m_server.bind(Ipv4("*", 16000)); m_server.listen(); auto client = m_server.accept(nullptr); @@ -144,7 +144,7 @@ std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { - m_client.connect(Ip{"127.0.0.1", 16000}); + m_client.connect(Ipv4("127.0.0.1", 16000)); m_client.send("hello world"); ASSERT_EQ("hello world", m_client.recv(512)); @@ -157,8 +157,8 @@ class UdpServerTest : public testing::Test { protected: - SocketUdp<Ip> m_server; - SocketUdp<Ip> m_client; + SocketUdp<Ipv4> m_server; + SocketUdp<Ipv4> m_client; std::thread m_tserver; std::thread m_tclient; @@ -166,7 +166,7 @@ public: UdpServerTest() { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.set(SockBlockMode()); } ~UdpServerTest() @@ -181,8 +181,8 @@ TEST_F(UdpServerTest, io) { m_tserver = std::thread([this] () { - Ip client; - Ip info{"*", 16000}; + Ipv4 client; + Ipv4 info("*", 16000); m_server.bind(info); auto msg = m_server.recvfrom(512, &client); @@ -196,7 +196,7 @@ std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { - Ip info{"127.0.0.1", 16000}; + Ipv4 info("127.0.0.1", 16000); m_client.sendto("hello world", info); @@ -253,7 +253,7 @@ TEST(ListenerSet, initialAdd) { Listener<TestBackendSet> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable); @@ -266,7 +266,7 @@ TEST(ListenerSet, readThenWrite) { Listener<TestBackendSet> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable); listener.set(s, Condition::Writable); @@ -280,7 +280,7 @@ TEST(ListenerSet, allOneShot) { Listener<TestBackendSet> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable | Condition::Writable); @@ -293,7 +293,7 @@ TEST(ListenerSet, readTwice) { Listener<TestBackendSet> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable); listener.set(s, Condition::Readable); @@ -307,7 +307,7 @@ TEST(ListenerSet, failure) { Listener<TestBackendSetFail> listener; - Handle s{0}; + Handle s = 0; try { listener.set(s, Condition::Readable); @@ -368,7 +368,7 @@ TEST(ListenerUnsetRemove, unset) { Listener<TestBackendUnset> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable); listener.unset(s, Condition::Readable); @@ -383,7 +383,7 @@ TEST(ListenerUnsetRemove, unsetOne) { Listener<TestBackendUnset> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable | Condition::Writable); listener.unset(s, Condition::Readable); @@ -398,7 +398,7 @@ TEST(ListenerUnsetRemove, unsetAll) { Listener<TestBackendUnset> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable | Condition::Writable); listener.unset(s, Condition::Readable); @@ -414,7 +414,7 @@ TEST(ListenerUnsetRemove, remove) { Listener<TestBackendUnset> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable | Condition::Writable); listener.remove(s); @@ -429,7 +429,7 @@ TEST(ListenerUnsetRemove, failure) { Listener<TestBackendUnsetFail> listener; - Handle s{0}; + Handle s = 0; listener.set(s, Condition::Readable | Condition::Writable); @@ -450,8 +450,8 @@ class ListenerTest : public testing::Test { protected: Listener<Select> m_listener; - SocketTcp<Ip> m_masterTcp; - SocketTcp<Ip> m_clientTcp; + SocketTcp<Ipv4> m_masterTcp; + SocketTcp<Ipv4> m_clientTcp; std::thread m_tserver; std::thread m_tclient; @@ -459,19 +459,17 @@ public: ListenerTest() { - m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_masterTcp.bind(Ip{"*", 16000}); + m_masterTcp.set(SockReuseAddress()); + m_masterTcp.bind(Ipv4("*", 16000)); m_masterTcp.listen(); } ~ListenerTest() { - if (m_tserver.joinable()) { + if (m_tserver.joinable()) m_tserver.join(); - } - if (m_tclient.joinable()) { + if (m_tclient.joinable()) m_tclient.join(); - } } }; @@ -491,7 +489,7 @@ std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { - m_clientTcp.connect(Ip{"127.0.0.1", 16000}); + m_clientTcp.connect(Ipv4("127.0.0.1", 16000)); }); } @@ -513,7 +511,7 @@ std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { - m_clientTcp.connect(Ip{"127.0.0.1", 16000}); + m_clientTcp.connect(Ipv4("127.0.0.1", 16000)); m_clientTcp.send("hello"); }); } @@ -524,8 +522,8 @@ class NonBlockingConnectTest : public testing::Test { protected: - SocketTcp<Ip> m_server; - SocketTcp<Ip> m_client; + SocketTcp<Ipv4> m_server; + SocketTcp<Ipv4> m_client; std::thread m_tserver; std::thread m_tclient; @@ -533,7 +531,7 @@ public: NonBlockingConnectTest() { - m_client.set(SockBlockMode{false}); + m_client.set(SockBlockMode(false)); } ~NonBlockingConnectTest() @@ -551,8 +549,8 @@ class TcpAcceptTest : public testing::Test { protected: - SocketTcp<Ip> m_server; - SocketTcp<Ip> m_client; + SocketTcp<Ipv4> m_server; + SocketTcp<Ipv4> m_client; std::thread m_tserver; std::thread m_tclient; @@ -560,8 +558,8 @@ public: TcpAcceptTest() { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Ip{"*", 16000}); + m_server.set(SockReuseAddress()); + m_server.bind(Ipv4("*", 16000)); m_server.listen(); } @@ -580,8 +578,8 @@ class TcpRecvTest : public testing::Test { protected: - SocketTcp<Ip> m_server; - SocketTcp<Ip> m_client; + SocketTcp<Ipv4> m_server; + SocketTcp<Ipv4> m_client; std::thread m_tserver; std::thread m_tclient; @@ -589,8 +587,8 @@ public: TcpRecvTest() { - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Ip{"*", 16000}); + m_server.set(SockReuseAddress()); + m_server.bind(Ipv4("*", 16000)); m_server.listen(); } @@ -614,7 +612,7 @@ std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { - m_client.connect(Ip{"127.0.0.1", 16000}); + m_client.connect(Ipv4("127.0.0.1", 16000)); m_client.send("hello"); m_client.close(); }); @@ -624,10 +622,12 @@ * Socket SSL * -------------------------------------------------------- */ +#if !defined(SOCKET_NO_SSL) + class TlsRecvTest : public testing::Test { protected: - SocketTls<Ip> m_server{nullptr}; - SocketTls<Ip> m_client; + SocketTls<Ipv4> m_server{nullptr}; + SocketTls<Ipv4> m_client; std::thread m_tserver; std::thread m_tclient; @@ -641,9 +641,9 @@ protocol.setPrivateKey("sockets/test.key"); protocol.setVerify(false); - m_server = SocketTls<Ip>{std::move(protocol)}; - m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_server.bind(Ip{"*", 16000}); + m_server = SocketTls<Ipv4>(std::move(protocol)); + m_server.set(SockReuseAddress()); + m_server.bind(Ipv4("*", 16000)); m_server.listen(); } @@ -672,7 +672,7 @@ m_tclient = std::thread([this] () { try { - m_client.connect(Ip{"127.0.0.1", 16000}); + m_client.connect(Ipv4("127.0.0.1", 16000)); m_client.send("hello"); } catch (const net::Error &ex) { FAIL() << ex.function() << ": " << ex.what(); @@ -680,6 +680,8 @@ }); } +#endif + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);