# HG changeset patch # User David Demelier # Date 1430308284 -7200 # Node ID 6c0015524ca92208c3681873e3cde9ff5cc10945 # Parent 99484c154d8a6fa28c2849ca8d9a2eed8864b0b2 Socket: fallback to Select until we update backends diff -r 99484c154d8a -r 6c0015524ca9 C++/modules/Socket/SocketListener.cpp --- a/C++/modules/Socket/SocketListener.cpp Wed Apr 29 11:17:56 2015 +0200 +++ b/C++/modules/Socket/SocketListener.cpp Wed Apr 29 13:51:24 2015 +0200 @@ -27,40 +27,9 @@ namespace backend { -void Select::set(Socket s, int flags) -{ - if (m_table.count(s.handle()) > 0) { - m_table.at(s.handle()).second |= flags; - } else { - m_table.emplace(s.handle(), std::make_pair(s, flags)); - } -} - -void Select::unset(Socket s, int flags) +SocketStatus Select::wait(const SocketTable &table, int ms) { - if (m_table.count(s.handle()) != 0) { - m_table.at(s.handle()).second &= ~(flags); - - // If no read, no write is requested, remove it - if (m_table.at(s.handle()).second == 0) { - m_table.erase(s.handle()); - } - } -} - -void Select::remove(Socket sc) -{ - m_table.erase(sc.handle()); -} - -void Select::clear() -{ - m_table.clear(); -} - -SocketStatus Select::wait(int ms) -{ - std::vector result = waitMultiple(ms); + std::vector result = waitMultiple(table, ms); if (result.size() == 0) { throw SocketError(SocketError::System, "select", "No socket found"); @@ -69,7 +38,7 @@ return result[0]; } -std::vector Select::waitMultiple(int ms) +std::vector Select::waitMultiple(const SocketTable &table, int ms) { timeval maxwait, *towait; fd_set readset; @@ -80,7 +49,7 @@ Socket::Handle max = 0; - for (auto &s : m_table) { + for (const auto &s : table) { if (s.second.second & SocketListener::Read) { FD_SET(s.first, &readset); } @@ -109,7 +78,7 @@ std::vector sockets; - for (auto &c : m_table) { + for (auto &c : table) { if (FD_ISSET(c.first, &readset)) { sockets.push_back(SocketStatus{c.second.first, SocketListener::Read}); } @@ -169,7 +138,7 @@ return flags; } -void Poll::set(Socket s, int flags) +void Poll::set(Socket s, int flags, bool) { auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); @@ -184,7 +153,7 @@ } } -void Poll::unset(Socket s, int flags) +void Poll::unset(Socket s, int flags, bool) { for (auto i = m_fds.begin(); i != m_fds.end();) { if (i->fd == s.handle()) { @@ -202,23 +171,7 @@ } } -void Poll::remove(Socket s) -{ - auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); }); - - if (it != m_fds.end()) { - m_fds.erase(it); - m_lookup.erase(s.handle()); - } -} - -void Poll::clear() -{ - m_fds.clear(); - m_lookup.clear(); -} - -SocketStatus Poll::wait(int ms) +SocketStatus Poll::wait(const SocketTable &table, int ms) { auto result = poll(m_fds.data(), m_fds.size(), ms); if (result == 0) { @@ -237,7 +190,7 @@ throw SocketError(SocketError::System, "select", "No socket found"); } -std::vector Poll::waitMultiple(int ms) +std::vector Poll::waitMultiple(const SocketTable &, int ms) { auto result = poll(m_fds.data(), m_fds.size(), ms); if (result == 0) { diff -r 99484c154d8a -r 6c0015524ca9 C++/modules/Socket/SocketListener.h --- a/C++/modules/Socket/SocketListener.h Wed Apr 29 11:17:56 2015 +0200 +++ b/C++/modules/Socket/SocketListener.h Wed Apr 29 13:51:24 2015 +0200 @@ -68,15 +68,15 @@ # define SOCKET_DEFAULT_BACKEND backend::Select # endif #elif defined(__linux__) - // TODO NOT READY YET - //# define SOCKET_DEFAULT_BACKEND backend::Epoll -# define SOCKET_DEFAULT_BACKEND backend::Poll +//# define SOCKET_DEFAULT_BACKEND backend::Epoll +# define SOCKET_DEFAULT_BACKEND backend::Select #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) # include # include # include -# define SOCKET_DEFAULT_BACKEND backend::Kqueue +//# define SOCKET_DEFAULT_BACKEND backend::Kqueue +# define SOCKET_DEFAULT_BACKEND backend::Select #else # define SOCKET_DEFAULT_BACKEND backend::Select #endif @@ -108,6 +108,12 @@ int flags; //!< the flags }; +/** + * Table used in the socket listener to store which sockets have been + * set in which directions. + */ +using SocketTable = std::map>; + namespace backend { /** @@ -117,16 +123,25 @@ * This class is the fallback of any other method, it is not preferred at all for many reasons. */ class Select { -private: - std::map> m_table; +public: + /* + * No-op, uses the SocketTable directly. + */ + inline void set(Socket, int, bool) noexcept + { + + } -public: - void set(Socket s, int flags); - void unset(Socket s, int flags); - void remove(Socket sc); - void clear(); - SocketStatus wait(int ms); - std::vector waitMultiple(int ms); + /* + * No-op, uses the SocketTable directly. + */ + inline void unset(Socket, int, bool) noexcept + { + + } + + SocketStatus wait(const SocketTable &table, int ms); + std::vector waitMultiple(const SocketTable &table, int ms); }; #if defined(SOCKET_HAVE_POLL) @@ -147,22 +162,16 @@ int toflags(short &event) const noexcept; public: - void set(Socket s, int flags); - void unset(Socket s, int flags); - void remove(Socket sc); - void clear(); - SocketStatus wait(int ms); - std::vector waitMultiple(int ms); + void set(Socket sc, int flags, bool add); + void unset(Socket sc, int flags, bool remove); + SocketStatus wait(const SocketTable &, int ms); + std::vector waitMultiple(const SocketTable &, int ms); }; #endif #if defined(SOCKET_HAVE_EPOLL) -class Epoll { - // TODO -}; - #endif #if defined(SOCKET_HAVE_KQUEUE) @@ -176,7 +185,6 @@ */ class Kqueue { private: - std::map> m_table; std::vector m_result; int m_handle; @@ -191,10 +199,8 @@ Kqueue(); ~Kqueue(); - void set(Socket sc, int flags); - void unset(Socket sc, int flags); - void remove(Socket sc); - void clear(); + void set(Socket sc, int flags, bool add); + void unset(Socket sc, int flags, bool remove); SocketStatus wait(int ms); std::vector waitMultiple(int ms); }; @@ -224,11 +230,9 @@ static const int Read; static const int Write; - using Map = std::map; - private: Backend m_backend; - Map m_map; + SocketTable m_table; public: inline SocketListenerBase() @@ -248,13 +252,23 @@ } /** - * Return an iterator to the beginning. + * Get the backend. * - * @return the iterator + * @return the backend */ - inline auto begin() noexcept + inline const Backend &backend() const noexcept { - return m_map.begin(); + return m_backend; + } + + /** + * Get the non-modifiable table. + * + * @return the table + */ + inline const SocketTable &table() const noexcept + { + return m_table; } /** @@ -264,7 +278,7 @@ */ inline auto begin() const noexcept { - return m_map.begin(); + return m_table.begin(); } /** @@ -274,17 +288,7 @@ */ inline auto cbegin() const noexcept { - return m_map.cbegin(); - } - - /** - * Return an iterator to the end. - * - * @return the iterator - */ - inline auto end() noexcept - { - return m_map.end(); + return m_table.cbegin(); } /** @@ -294,7 +298,7 @@ */ inline auto end() const noexcept { - return m_map.end(); + return m_table.end(); } /** @@ -304,25 +308,22 @@ */ inline auto cend() const noexcept { - return m_map.cend(); + return m_table.cend(); } /** - * Add a socket to the listener. + * Add or update a socket to the listener. + * + * If the socket is already placed with the appropriate flags, the + * function is a no-op. + * + * If incorrect flags are passed, the function does nothing. * * @param sc the socket * @param flags (may be OR'ed) + * @throw SocketError if the backend failed to set */ - void set(Socket sc, int flags) - { - if (m_map.count(sc) > 0) { - m_map[sc] |= flags; - m_backend.set(sc, flags); - } else { - m_map.insert({sc, flags}); - m_backend.set(sc, flags); - } - } + void set(Socket sc, int flags); /** * Unset a socket from the listener, only the flags is removed @@ -335,45 +336,36 @@ * @param flags the flags (may be OR'ed) * @see remove */ - void unset(Socket sc, int flags) noexcept - { - if (m_map.count(sc) > 0) { - m_map[sc] &= ~(flags); - m_backend.unset(sc, flags); - - // No more flags, remove it - if (m_map[sc] == 0) { - m_map.erase(sc); - } - } - } + void unset(Socket sc, int flags); /** * Remove completely the socket from the listener. * + * It is a shorthand for unset(sc, SocketListener::Read | SocketListener::Write); + * * @param sc the socket */ - inline void remove(Socket sc) noexcept + inline void remove(Socket sc) { - m_map.erase(sc); - m_backend.remove(sc); + unset(sc, Read | Write); } /** * Remove all sockets. */ - inline void clear() noexcept + inline void clear() { - m_map.clear(); - m_backend.clear(); + while (!m_table.empty()) { + remove(m_table.begin()->second.first); + } } /** * Get the number of sockets in the listener. */ - unsigned size() const noexcept + inline auto size() const noexcept { - return m_map.size(); + return m_table.size(); } /** @@ -398,7 +390,7 @@ */ inline SocketStatus wait(int timeout = -1) { - return m_backend.wait(timeout); + return m_backend.wait(m_table, timeout); } /** @@ -412,7 +404,7 @@ { auto cvt = std::chrono::duration_cast(duration); - return m_backend.waitMultiple(cvt.count()); + return m_backend.waitMultiple(m_table, cvt.count()); } /** @@ -422,10 +414,76 @@ */ inline std::vector waitMultiple(int timeout = -1) { - return m_backend.waitMultiple(timeout); + return m_backend.waitMultiple(m_table, timeout); } }; +template +void SocketListenerBase::set(Socket sc, int flags) +{ + /* Invalid or useless flags */ + if (flags == 0 || flags > 0x3) + return; + + auto it = m_table.find(sc.handle()); + + /* + * Do not update the table if the backend failed to add + * or update. + */ + if (it == m_table.end()) { + m_backend.set(sc, flags, true); + m_table.emplace(sc.handle(), std::make_pair(sc, flags)); + } else { + if ((flags & Read) && (it->second.second & Read)) { + flags &= ~(Read); + } + if ((flags & Write) && (it->second.second & Write)) { + flags &= ~(Write); + } + + /* Still need a call? */ + if (flags != 0) { + m_backend.set(sc, flags, false); + it->second.second |= flags; + } + } +} + +template +void SocketListenerBase::unset(Socket sc, int flags) +{ + auto it = m_table.find(sc.handle()); + + /* Invalid or useless flags */ + if (flags == 0 || flags > 0x3 || it == m_table.end()) + return; + + /* + * Like set, do not update if the socket is already at the appropriate + * state. + */ + if ((flags & Read) && !(it->second.second & Read)) { + flags &= ~(Read); + } + if ((flags & Write) && !(it->second.second & Write)) { + flags &= ~(Write); + } + + if (flags != 0) { + /* Determine if it's a complete removal */ + bool removal = ((it->second.second) & ~(flags)) == 0; + + m_backend.unset(sc, flags, removal); + + if (removal) { + m_table.erase(it); + } else { + it->second.second &= ~(flags); + } + } +} + /** * Helper to use the default. */ diff -r 99484c154d8a -r 6c0015524ca9 C++/tests/Socket/main.cpp --- a/C++/tests/Socket/main.cpp Wed Apr 29 11:17:56 2015 +0200 +++ b/C++/tests/Socket/main.cpp Wed Apr 29 13:51:24 2015 +0200 @@ -24,12 +24,12 @@ #include -#include "Socket.h" -#include "SocketAddress.h" -#include "SocketListener.h" -#include "SocketSsl.h" -#include "SocketTcp.h" -#include "SocketUdp.h" +#include +#include +#include +#include +#include +#include using namespace address; using namespace std::literals::chrono_literals; @@ -169,175 +169,225 @@ } /* -------------------------------------------------------- - * Listener tests (standard) + * Listener: set function * -------------------------------------------------------- */ -class ListenerTest : public testing::Test { -protected: - SocketListenerBase m_listener; - SocketTcp socket1{AF_INET, 0}; - SocketUdp socket2{AF_INET, 0}; +class TestBackendSet { +public: + int m_callcount{0}; + bool m_added{false}; + int m_flags{0}; + + inline void set(Socket sc, int flags, bool add) noexcept + { + m_callcount ++; + m_added = add; + m_flags |= flags; + } + inline void unset(Socket, int, bool) noexcept {} + SocketStatus wait(const SocketTable &table, int ms) {} + std::vector waitMultiple(const SocketTable &table, int ms) {} +}; + +class TestBackendSetFail { public: - ~ListenerTest() + inline void set(Socket, int, bool) { - socket1.close(); - socket2.close(); + throw "fail"; } + + inline void unset(Socket, int, bool) noexcept {} + SocketStatus wait(const SocketTable &table, int ms) {} + std::vector waitMultiple(const SocketTable &table, int ms) {} }; -TEST_F(ListenerTest, set) +TEST(ListenerSet, initialAdd) { - m_listener.set(socket1, SocketListener::Read); + SocketListenerBase listener; + + listener.set(Socket(0), SocketListener::Read); - ASSERT_EQ(1, static_cast(m_listener.size())); - ASSERT_EQ(SocketListener::Read, m_listener.begin()->second); - - m_listener.set(socket1, SocketListener::Write); - - ASSERT_EQ(1, static_cast(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); + ASSERT_EQ(1U, listener.size()); + ASSERT_EQ(1, listener.backend().m_callcount); + ASSERT_TRUE(listener.backend().m_added); + ASSERT_TRUE(listener.backend().m_flags == SocketListener::Read); +} - // Fake a re-insert of the same socket - m_listener.set(socket1, SocketListener::Write); - - ASSERT_EQ(1, static_cast(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); +TEST(ListenerSet, readThenWrite) +{ + SocketListenerBase listener; + Socket sc(0); - // Add an other socket now - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); - - ASSERT_EQ(2, static_cast(m_listener.size())); + listener.set(sc, SocketListener::Read); + listener.set(sc, SocketListener::Write); - for (auto &pair : m_listener) { - ASSERT_EQ(0x3, pair.second); - ASSERT_TRUE(pair.first == socket1 || pair.first == socket2); - } + ASSERT_EQ(1U, listener.size()); + ASSERT_EQ(2, listener.backend().m_callcount); + ASSERT_FALSE(listener.backend().m_added); + ASSERT_TRUE(listener.backend().m_flags == 0x3); } -TEST_F(ListenerTest, unset) +TEST(ListenerSet, allOneShot) { - m_listener.set(socket1, SocketListener::Read | SocketListener::Write); - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); + SocketListenerBase listener; + Socket sc(0); + + listener.set(sc, SocketListener::Read | SocketListener::Write); - m_listener.unset(socket1, SocketListener::Read); + ASSERT_EQ(1U, listener.size()); + ASSERT_EQ(1, listener.backend().m_callcount); + ASSERT_TRUE(listener.backend().m_added); + ASSERT_TRUE(listener.backend().m_flags == 0x3); +} + +TEST(ListenerSet, readTwice) +{ + SocketListenerBase listener; + Socket sc(0); - ASSERT_EQ(2, static_cast(m_listener.size())); + listener.set(sc, SocketListener::Read); + listener.set(sc, SocketListener::Read); + + ASSERT_EQ(1U, listener.size()); + ASSERT_EQ(1, listener.backend().m_callcount); + ASSERT_TRUE(listener.backend().m_added); + ASSERT_TRUE(listener.backend().m_flags == SocketListener::Read); +} - // Use a for loop since it can be ordered differently - for (auto &pair : m_listener) { - if (pair.first == socket1) { - ASSERT_EQ(0x2, pair.second); - } else if (pair.first == socket2) { - ASSERT_EQ(0x3, pair.second); - } +TEST(ListenerSet, failure) +{ + SocketListenerBase listener; + + try { + listener.set(Socket(0), SocketListener::Read); + FAIL() << "exception expected"; + } catch (...) { } - m_listener.unset(socket1, SocketListener::Write); - - ASSERT_EQ(1, static_cast(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); -} - -TEST_F(ListenerTest, remove) -{ - m_listener.set(socket1, SocketListener::Read | SocketListener::Write); - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); - m_listener.remove(socket1); - - ASSERT_EQ(1, static_cast(m_listener.size())); - ASSERT_EQ(0x3, m_listener.begin()->second); -} - -TEST_F(ListenerTest, clear) -{ - m_listener.set(socket1, SocketListener::Read | SocketListener::Write); - m_listener.set(socket2, SocketListener::Read | SocketListener::Write); - m_listener.clear(); - - ASSERT_EQ(0, static_cast(m_listener.size())); + ASSERT_EQ(0U, listener.size()); } /* -------------------------------------------------------- - * Listener: poll + * Listener: unset / remove functions * -------------------------------------------------------- */ -#if defined(SOCKET_HAVE_POLL) - -class ListenerPollTest : public testing::Test { -protected: - SocketListenerBase m_listener; - SocketTcp m_masterTcp{AF_INET, 0}; - SocketTcp m_clientTcp{AF_INET, 0}; +class TestBackendUnset { +public: + bool m_isset{false}; + bool m_isunset{false}; + int m_flags{0}; + bool m_removal{false}; - std::thread m_tserver; - std::thread m_tclient; - -public: - ListenerPollTest() + inline void set(Socket, int flags, bool) noexcept { - m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_masterTcp.bind(Internet("*", 16000, AF_INET)); - m_masterTcp.listen(); + m_isset = true; + m_flags |= flags; } - ~ListenerPollTest() + inline void unset(Socket, int flags, bool remove) noexcept { - if (m_tserver.joinable()) { - m_tserver.join(); - } - if (m_tclient.joinable()) { - m_tclient.join(); - } + m_isunset = true; + m_flags &= ~(flags); + m_removal = remove; } + + SocketStatus wait(const SocketTable &table, int ms) {} + std::vector waitMultiple(const SocketTable &table, int ms) {} +}; + +class TestBackendUnsetFail { +public: + inline void set(Socket, int, bool) noexcept {} + + inline void unset(Socket, int, bool) + { + throw "fail"; + } + + SocketStatus wait(const SocketTable &table, int ms) {} + std::vector waitMultiple(const SocketTable &table, int ms) {} }; -TEST_F(ListenerPollTest, accept) +TEST(ListenerUnsetRemove, unset) { - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.wait(); - m_masterTcp.accept(); - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); + SocketListenerBase listener; + Socket sc(0); + + listener.set(sc, SocketListener::Read); + listener.unset(sc, SocketListener::Read); + + ASSERT_EQ(0U, listener.size()); + ASSERT_TRUE(listener.backend().m_isset); + ASSERT_TRUE(listener.backend().m_isunset); + ASSERT_TRUE(listener.backend().m_flags == 0); + ASSERT_TRUE(listener.backend().m_removal); +} - std::this_thread::sleep_for(100ms); +TEST(ListenerUnsetRemove, unsetOne) +{ + SocketListenerBase listener; + Socket sc(0); - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - }); + listener.set(sc, SocketListener::Read | SocketListener::Write); + listener.unset(sc, SocketListener::Read); + + ASSERT_EQ(1U, listener.size()); + ASSERT_TRUE(listener.backend().m_isset); + ASSERT_TRUE(listener.backend().m_isunset); + ASSERT_TRUE(listener.backend().m_flags == SocketListener::Write); + ASSERT_FALSE(listener.backend().m_removal); } -TEST_F(ListenerPollTest, recv) +TEST(ListenerUnsetRemove, unsetAll) { - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.wait(); - - auto sc = m_masterTcp.accept(); - - ASSERT_EQ("hello", sc.recv(512)); + SocketListenerBase listener; + Socket sc(0); - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); + listener.set(sc, SocketListener::Read | SocketListener::Write); + listener.unset(sc, SocketListener::Read); + listener.unset(sc, SocketListener::Write); - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_clientTcp.send("hello"); - }); + ASSERT_EQ(0U, listener.size()); + ASSERT_TRUE(listener.backend().m_isset); + ASSERT_TRUE(listener.backend().m_isunset); + ASSERT_TRUE(listener.backend().m_flags == 0); + ASSERT_TRUE(listener.backend().m_removal); } -#endif +TEST(ListenerUnsetRemove, remove) +{ + SocketListenerBase listener; + Socket sc(0); + + listener.set(sc, SocketListener::Read | SocketListener::Write); + listener.remove(sc); + + ASSERT_EQ(0U, listener.size()); + ASSERT_TRUE(listener.backend().m_isset); + ASSERT_TRUE(listener.backend().m_isunset); + ASSERT_TRUE(listener.backend().m_flags == 0); + ASSERT_TRUE(listener.backend().m_removal); +} + +TEST(ListenerUnsetRemove, failure) +{ + SocketListenerBase listener; + Socket sc(0); + + listener.set(sc, SocketListener::Read | SocketListener::Write); + + try { + listener.remove(sc); + FAIL() << "exception expected"; + } catch (...) { + } + + /* If fail, kept into the table */ + ASSERT_EQ(1U, listener.size()); +} + +#if 0 /* -------------------------------------------------------- * Listener: select @@ -416,86 +466,7 @@ }); } -/* -------------------------------------------------------- - * Listener: kqueue - * -------------------------------------------------------- */ - -#if defined(SOCKET_HAVE_KQUEUE) - -class ListenerKqueueTest : public testing::Test { -protected: - SocketListenerBase m_listener; - SocketTcp m_masterTcp{AF_INET, 0}; - SocketTcp m_clientTcp{AF_INET, 0}; - - std::thread m_tserver; - std::thread m_tclient; - -public: - ListenerKqueueTest() - { - m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); - m_masterTcp.bind(Internet("*", 16000, AF_INET)); - m_masterTcp.listen(); - } - - ~ListenerKqueueTest() - { - if (m_tserver.joinable()) { - m_tserver.join(); - } - if (m_tclient.joinable()) { - m_tclient.join(); - } - } -}; - -TEST_F(ListenerKqueueTest, accept) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.wait(); - m_masterTcp.accept(); - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - }); -} - -TEST_F(ListenerKqueueTest, recv) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp, SocketListener::Read); - m_listener.wait(); - - auto sc = m_masterTcp.accept(); - - ASSERT_EQ("hello", sc.recv(512)); - - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Internet("127.0.0.1", 16000, AF_INET)); - m_clientTcp.send("hello"); - }); -} - -#endif // !SOCKET_HAVE_KQUEUE +#endif /* -------------------------------------------------------- * Non-blocking connect