# HG changeset patch # User David Demelier # Date 1446758920 -3600 # Node ID 5a9671dabb155482194860b61426004852da3e5d # Parent f8594d3394abc7cec4e8d1c52eed02fe8ffd1d07 Socket: - Fix epoll which wrongly reset flags, - Fix wrongly use of SOCKET_DEFAULT_BACKEND, - Fix error in GCC using constexpr, switch to inline, - Add more documentation about non-blocking sockets, - Remove Socket::setBlockMode, - Start updating all doxygen comments, - Put back UDP. diff -r f8594d3394ab -r 5a9671dabb15 C++/examples/Socket/blocking-accept.cpp --- a/C++/examples/Socket/blocking-accept.cpp Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/examples/Socket/blocking-accept.cpp Thu Nov 05 22:28:40 2015 +0100 @@ -22,20 +22,21 @@ int main() { #if defined(WITH_SSL) - net::SocketTls master; - net::SocketTls client{net::Invalid}; + net::SocketTls master; + net::SocketTls client{net::Invalid}; #else - net::SocketTcp master; - net::SocketTcp client{net::Invalid}; + net::SocketTcp master; + net::SocketTcp client{net::Invalid}; #endif net::Listener<> listener; try { - master.bind(net::Ipv4{"*", WITH_PORT}); + master.set(net::option::SockReuseAddress{true}); + master.bind(net::address::Ip{"*", WITH_PORT}); master.listen(); - listener.set(master.handle(), net::FlagRead); + listener.set(master.handle(), net::Condition::Readable); listener.wait(std::chrono::seconds(WITH_TIMEOUT)); client = master.accept(nullptr); diff -r f8594d3394ab -r 5a9671dabb15 C++/examples/Socket/blocking-connect.cpp --- a/C++/examples/Socket/blocking-connect.cpp Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/examples/Socket/blocking-connect.cpp Thu Nov 05 22:28:40 2015 +0100 @@ -28,14 +28,14 @@ int main() { #if defined(WITH_SSL) - net::SocketTls socket; + net::SocketTls socket; #else - net::SocketTcp socket; + net::SocketTcp socket; #endif try { std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl; - socket.connect(net::Ipv4{WITH_HOST, WITH_PORT}); + socket.connect(net::address::Ip{WITH_HOST, WITH_PORT}); } catch (const net::Error &error) { std::cerr << "error: " << error.what() << std::endl; std::exit(1); diff -r f8594d3394ab -r 5a9671dabb15 C++/examples/Socket/non-blocking-accept.cpp --- a/C++/examples/Socket/non-blocking-accept.cpp Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/examples/Socket/non-blocking-accept.cpp Thu Nov 05 22:28:40 2015 +0100 @@ -23,11 +23,11 @@ int main() { #if defined(WITH_SSL) - net::SocketTls master; - net::SocketTls client{net::Invalid}; + net::SocketTls master; + net::SocketTls client{net::Invalid}; #else - net::SocketTcp master; - net::SocketTcp client{net::Invalid}; + net::SocketTcp master; + net::SocketTcp client{net::Invalid}; #endif net::Listener<> listener; @@ -35,11 +35,10 @@ // 1. Create the master socket for listening. try { - master.bind(net::Ipv4{"*", WITH_PORT}); + master.set(net::option::SockReuseAddress{true}); + master.set(net::option::SockBlockMode{false}); + master.bind(net::address::Ip{"*", WITH_PORT}); master.listen(); - - // Usually never needed, but for the example put everything as non-blocking. - master.setBlockMode(false); } catch (const net::Error &error) { std::cerr << "error: " << error.what() << std::endl; std::exit(1); @@ -48,22 +47,16 @@ while (client.state() != net::State::Accepted && timer.elapsed() < (WITH_TIMEOUT * 1000)) { try { if (client.state() == net::State::Closed) { - // 1. Wait for a pre-accept process. - listener.set(master.handle(), net::FlagRead); + // 2. Wait for a pre-accept process. + listener.set(master.handle(), net::Condition::Readable); listener.wait(std::chrono::seconds(WITH_TIMEOUT)); client = master.accept(nullptr); - client.setBlockMode(false); + client.set(net::option::SockBlockMode{false}); listener.remove(master.handle()); } else { - // 2. Wait for the accept process to complete. + // 3. Wait for the accept process to complete. listener.remove(client.handle()); - - if (client.state() == net::State::AcceptingRead) { - listener.set(client.handle(), net::FlagRead); - } else if (client.state() == net::State::AcceptingWrite) { - listener.set(client.handle(), net::FlagWrite); - } - + listener.set(client.handle(), client.condition()); listener.wait(std::chrono::seconds(WITH_TIMEOUT)); client.accept(); } diff -r f8594d3394ab -r 5a9671dabb15 C++/examples/Socket/non-blocking-connect.cpp --- a/C++/examples/Socket/non-blocking-connect.cpp Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/examples/Socket/non-blocking-connect.cpp Thu Nov 05 22:28:40 2015 +0100 @@ -28,39 +28,33 @@ int main() { #if defined(WITH_SSL) - net::SocketTls socket; + net::SocketTls socket; #else - net::SocketTcp socket; + net::SocketTcp socket; #endif net::Listener<> listener; ElapsedTimer timer; - socket.setBlockMode(false); + // 1. Set to non-blocking. + socket.set(net::option::SockBlockMode{false}); try { std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl; - socket.connect(net::Ipv4{WITH_HOST, WITH_PORT}); + + // 2. Initial connection process. + socket.connect(net::address::Ip{WITH_HOST, WITH_PORT}); while (socket.state() != net::State::Connected) { listener.remove(socket.handle()); - if (socket.state() == net::State::ConnectingRead) { - listener.set(socket.handle(), net::FlagRead); - } else if (socket.state() == net::State::ConnectingWrite) { - listener.set(socket.handle(), net::FlagWrite); - } - + // 2. Now complete by waiting for the appropriate condition. + listener.set(socket.handle(), socket.condition()); listener.wait(std::chrono::seconds(WITH_TIMEOUT)); socket.connect(); } } catch (const net::Error &error) { - if (error.code() == net::Error::Timeout) { - std::cerr << "timeout while connecting" << std::endl; - } else { - std::cerr << "error: " << error.what() << std::endl; - } - + std::cerr << "error: " << error.what() << std::endl; std::exit(1); } diff -r f8594d3394ab -r 5a9671dabb15 C++/examples/Socket/stream-client.cpp --- a/C++/examples/Socket/stream-client.cpp Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/examples/Socket/stream-client.cpp Thu Nov 05 22:28:40 2015 +0100 @@ -20,9 +20,9 @@ #endif #if defined(WITH_SSL) -using Client = net::StreamClient; +using Client = net::StreamClient; #else -using Client = net::StreamClient; +using Client = net::StreamClient; #endif int main() @@ -54,7 +54,7 @@ std::cout << "client: sent: " << data << std::endl; }); - client.connect(net::Ipv4{WITH_HOST, WITH_PORT}); + client.connect(net::address::Ip{WITH_HOST, WITH_PORT}); while (connected) { client.poll(); @@ -63,4 +63,4 @@ std::cout << "client: exiting" << std::endl; return 0; -} \ No newline at end of file +} diff -r f8594d3394ab -r 5a9671dabb15 C++/examples/Socket/stream-server.cpp --- a/C++/examples/Socket/stream-server.cpp Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/examples/Socket/stream-server.cpp Thu Nov 05 22:28:40 2015 +0100 @@ -15,26 +15,26 @@ #endif #if defined(WITH_SSL) -using Server = net::StreamServer; -using Connection = net::StreamConnection; +using Server = net::StreamServer; +using Connection = net::StreamConnection; #else -using Server = net::StreamServer; -using Connection = net::StreamConnection; +using Server = net::StreamServer; +using Connection = net::StreamConnection; #endif int main() { #if defined(WITH_SSL) - net::Tls protocol; + net::protocol::Tls protocol; protocol.setVerify(false); protocol.setPrivateKey("test.key"); protocol.setCertificate("test.crt"); protocol.setMethod(net::ssl::Tlsv1); - Server server{std::move(protocol), net::Ipv4{"*", WITH_PORT}}; + Server server{std::move(protocol), net::address::Ip{"*", WITH_PORT}}; #else - Server server{net::Tcp{}, net::Ipv4{"*", WITH_PORT}}; + Server server{net::protocol::Tcp{}, net::address::Ip{"*", WITH_PORT}}; #endif server.setConnectionHandler([] (const std::shared_ptr &client) { @@ -56,4 +56,4 @@ } return 0; -} \ No newline at end of file +} diff -r f8594d3394ab -r 5a9671dabb15 C++/modules/Socket/Sockets.cpp --- a/C++/modules/Socket/Sockets.cpp Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/modules/Socket/Sockets.cpp Thu Nov 05 22:28:40 2015 +0100 @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define TIMEOUT_MSG "operation timeout" + #include #include #include @@ -130,6 +132,52 @@ /* }}} */ /* + * SSL stuff + * ------------------------------------------------------------------ + */ + +/* {{{ SSL initialization */ + +#if !defined(SOCKET_NO_SSL) + +namespace ssl { + +namespace { + +std::mutex mutex; +std::atomic initialized{false}; + +} // !namespace + +void finish() noexcept +{ + ERR_free_strings(); +} + +void init() noexcept +{ + std::lock_guard lock{mutex}; + + if (!initialized) { + initialized = true; + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + +#if !defined(SOCKET_NO_AUTO_SSL_INIT) + atexit(finish); +#endif // SOCKET_NO_AUTO_SSL_INIT + } +} + +} // !ssl + +#endif // SOCKET_NO_SSL + +/* }}} */ + +/* * Error class * ------------------------------------------------------------------ */ @@ -166,6 +214,8 @@ /* {{{ Addresses */ +namespace address { + /* Default domain */ int Ip::m_default{AF_INET}; @@ -239,9 +289,9 @@ #if !defined(_WIN32) -Local::Local() +Local::Local() noexcept { - std::memset(m_sun, 0, sizeof (sockaddr_un)); + std::memset(&m_sun, 0, sizeof (sockaddr_un)); } Local::Local(std::string path, bool rm) noexcept @@ -272,6 +322,8 @@ #endif // !_WIN32 +} // !address + /* }}} */ /* @@ -316,7 +368,7 @@ throw Error{Error::System, "select"}; } if (error == 0) { - throw Error{Error::Timeout, "select", "Timeout while listening"}; + throw Error{Error::Timeout, "select", TIMEOUT_MSG}; } std::vector sockets; @@ -335,6 +387,11 @@ /* }}} */ +/* + * Poll + * ------------------------------------------------------------------ + */ + /* {{{ Poll */ /* @@ -416,7 +473,7 @@ { auto result = poll(m_fds.data(), m_fds.size(), ms); if (result == 0) { - throw Error{Error::Timeout, "select", "Timeout while listening"}; + throw Error{Error::Timeout, "select", TIMEOUT_MSG}; } if (result < 0) { throw Error{Error::System, "poll"}; @@ -434,11 +491,15 @@ #endif // !SOCKET_HAVE_POLL +/* }}} */ + /* * Epoll implementation * ------------------------------------------------------------------ */ +/* {{{ Epoll */ + #if defined(SOCKET_HAVE_EPOLL) uint32_t Epoll::toEpoll(Condition condition) const noexcept @@ -497,14 +558,17 @@ } /* - * Add a new epoll_event or just update it. + * For set and unset, we need to apply the whole flags required, so if the socket + * was set to Connection::Readable and user add Connection::Writable, we must + * place both. */ -void Epoll::set(const ListenerTable &, Handle h, Condition condition, bool add) +void Epoll::set(const ListenerTable &table, Handle sc, Condition condition, bool add) { - update(h, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, toEpoll(condition)); - if (add) { + update(sc, EPOLL_CTL_ADD, toEpoll(condition)); m_events.resize(m_events.size() + 1); + } else { + update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition)); } } @@ -522,7 +586,7 @@ update(sc, EPOLL_CTL_DEL, 0); m_events.resize(m_events.size() - 1); } else { - update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc)) & ~(toEpoll(condition))); + update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition))); } } @@ -532,7 +596,7 @@ std::vector result; if (ret == 0) { - throw Error{Error::Timeout, "epoll_wait"}; + throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG}; } if (ret < 0) { throw Error{Error::System, "epoll_wait"}; @@ -547,9 +611,14 @@ #endif // !SOCKET_HAVE_EPOLL -/* -------------------------------------------------------- +/* }}} */ + +/* * Kqueue implementation - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ + +/* {{{ Kqueue */ #if defined(SOCKET_HAVE_KQUEUE) @@ -617,7 +686,7 @@ int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts); if (nevents == 0) { - throw Error{Error::Timeout, "kevent"}; + throw Error{Error::Timeout, "kevent", TIMEOUT_MSG}; } if (nevents < 0) { throw Error{Error::System, "kevent"}; @@ -635,41 +704,6 @@ #endif // !SOCKET_HAVE_KQUEUE -#if !defined(SOCKET_NO_SSL) - -namespace ssl { - -namespace { - -std::mutex mutex; -std::atomic initialized{false}; - -} // !namespace - -void finish() noexcept -{ - ERR_free_strings(); -} - -void init() noexcept -{ - std::lock_guard lock{mutex}; - - if (!initialized) { - initialized = true; - - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - -#if !defined(SOCKET_NO_AUTO_SSL_INIT) - atexit(finish); -#endif // SOCKET_NO_AUTO_SSL_INIT - } -} - -} // !ssl - -#endif // SOCKET_NO_SSL +/* }}} */ } // !net diff -r f8594d3394ab -r 5a9671dabb15 C++/modules/Socket/Sockets.h --- a/C++/modules/Socket/Sockets.h Thu Nov 05 16:42:23 2015 +0100 +++ b/C++/modules/Socket/Sockets.h Thu Nov 05 22:28:40 2015 +0100 @@ -93,24 +93,30 @@ * * @note Do not rely on the value shown in doxygen. */ -#if !defined(SOCKET_DEFAULT_BACKEND) -# if defined(_WIN32) +#if defined(_WIN32) +# if !defined(SOCKET_DEFAULT_BACKEND) # if defined(SOCKET_HAVE_POLL) # define SOCKET_DEFAULT_BACKEND Poll # else # define SOCKET_DEFAULT_BACKEND Select # endif -# elif defined(__linux__) -# include - +# endif +#elif defined(__linux__) +# include + +# if !defined(SOCKET_DEFAULT_BACKEND) # define SOCKET_DEFAULT_BACKEND Epoll -# elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) -# include -# include -# include - +# endif +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) +# include +# include +# include + +# if !defined(SOCKET_DEFAULT_BACKEND) # define SOCKET_DEFAULT_BACKEND Kqueue -# else +# endif +#else +# if !defined(SOCKET_DEFAULT_BACKEND) # define SOCKET_DEFAULT_BACKEND Select # endif #endif @@ -255,6 +261,23 @@ #endif +#if !defined(SOCKET_NO_SSL) + +namespace ssl { + +/** + * @enum Method + * @brief Which OpenSSL method to use. + */ +enum Method { + Tlsv1, //!< TLS v1.2 (recommended) + Sslv3 //!< SSLv3 +}; + +} // !ssl + +#endif + /* }}} */ /* @@ -542,7 +565,7 @@ * @param v2 the second value * @return the new value */ -constexpr Condition &operator|=(Condition &v1, Condition v2) noexcept +inline Condition &operator|=(Condition &v1, Condition v2) noexcept { v1 = static_cast(static_cast(v1) | static_cast(v2)); @@ -556,7 +579,7 @@ * @param v2 the second value * @return the new value */ -constexpr Condition &operator&=(Condition &v1, Condition v2) noexcept +inline Condition &operator&=(Condition &v1, Condition v2) noexcept { v1 = static_cast(static_cast(v1) & static_cast(v2)); @@ -570,7 +593,7 @@ * @param v2 the second value * @return the new value */ -constexpr Condition &operator^=(Condition &v1, Condition v2) noexcept +inline Condition &operator^=(Condition &v1, Condition v2) noexcept { v1 = static_cast(static_cast(v1) ^ static_cast(v2)); @@ -592,6 +615,13 @@ /** * @class Socket * @brief Base socket class for socket operations. + * + * **Important:** When using non-blocking sockets, some considerations must be taken. See the implementation of the + * underlying protocol for more details. + * + * @see protocol::Tls + * @see protocol::Tcp + * @see protocol::Udp */ template class Socket { @@ -618,11 +648,11 @@ * @param type the type SOCK_* * @param protocol the protocol * @param iface the implementation - * @throw Error on failures + * @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 = Protocol{}) + Socket(int domain, int type, int protocol, Protocol iface = {}) : m_proto(std::move(iface)) { #if !defined(SOCKET_NO_AUTO_INIT) @@ -647,6 +677,7 @@ * * @param protocol the protocol * @param address which type of address + * @throw net::Error on errors */ explicit inline Socket(Protocol protocol = {}, const Address &address = {}) : Socket{address.domain(), protocol.type(), 0, std::move(protocol)} @@ -802,7 +833,7 @@ * @param level the setting level * @param name the name * @param arg the value - * @throw Error on error + * @throw net::Error on errors */ template void set(int level, int name, const Argument &arg) @@ -818,7 +849,7 @@ * The object must have `set(Socket &) const`. * * @param option the option - * @throw Error on errors + * @throw net::Error on errors */ template inline void set(const Option &option) @@ -831,7 +862,7 @@ * * @param level the setting level * @param name the name - * @throw Error on error + * @throw net::Error on errors */ template Argument get(int level, int name) @@ -855,7 +886,7 @@ * returned from this function. * * @return the same value as get() in the option - * @throw Error on errors + * @throw net::Error on errors */ template inline auto get() -> decltype(std::declval