Mercurial > code
changeset 318:68ae6d7dea1f
Sockets: improve errors
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 06 Mar 2015 17:30:16 +0100 |
parents | 890729b8cb60 |
children | cba77da58496 |
files | C++/Socket.h C++/SocketTcp.cpp C++/SocketTcp.h C++/Tests/Sockets/main.cpp |
diffstat | 4 files changed, 183 insertions(+), 1073 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Socket.h Wed Mar 04 13:45:56 2015 +0100 +++ b/C++/Socket.h Fri Mar 06 17:30:16 2015 +0100 @@ -125,7 +125,7 @@ const char *what() const noexcept { - return "Error failure"; + return m_error.c_str(); } };
--- a/C++/SocketTcp.cpp Wed Mar 04 13:45:56 2015 +0100 +++ b/C++/SocketTcp.cpp Fri Mar 06 17:30:16 2015 +0100 @@ -91,12 +91,12 @@ int error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) - throw SocketError(SocketError::WouldBlockWrite, "connect", error); + throw SocketError(SocketError::InProgress, "connect", error); throw SocketError(SocketError::System, "connect", error); #else if (errno == EINPROGRESS) - throw SocketError(SocketError::WouldBlockWrite, "connect"); + throw SocketError(SocketError::InProgress, "connect"); throw SocketError(SocketError::System, "connect"); #endif @@ -114,17 +114,20 @@ try { connect(address); } catch (const SocketError &ex) { - // TODO: HANDLE ERROR CODE + if (ex.code() == SocketError::InProgress) { + SocketListener listener{{*this, SocketListener::Write}}; - SocketListener listener{{*this, SocketListener::Write}}; + listener.select(timeout); - listener.select(timeout); + // Socket is writable? Check if there is an error - // Socket is writable? Check if there is an error - int error = get<int>(SOL_SOCKET, SO_ERROR); + int error = get<int>(SOL_SOCKET, SO_ERROR); - if (error) { - throw SocketError(SocketError::System, "connect", error); + if (error) { + throw SocketError(SocketError::System, "connect", error); + } + } else { + throw; } }
--- a/C++/SocketTcp.h Wed Mar 04 13:45:56 2015 +0100 +++ b/C++/SocketTcp.h Fri Mar 06 17:30:16 2015 +0100 @@ -85,7 +85,9 @@ public: using SocketAbstractTcp::SocketAbstractTcp; using SocketAbstractTcp::recv; + using SocketAbstractTcp::waitRecv; using SocketAbstractTcp::send; + using SocketAbstractTcp::waitSend; SocketTcp accept();
--- a/C++/Tests/Sockets/main.cpp Wed Mar 04 13:45:56 2015 +0100 +++ b/C++/Tests/Sockets/main.cpp Fri Mar 06 17:30:16 2015 +0100 @@ -415,1150 +415,255 @@ }); } - - - - - - - - - - - - -#if 0 - -#include <Socket.h> -#include <SocketListener.h> -#include <SocketAddress.h> - -using namespace std::literals::chrono_literals; - -using namespace error; -using namespace address; - /* -------------------------------------------------------- - * Miscellaneous - * -------------------------------------------------------- */ - -TEST(Misc, set) -{ - Socket s; - - try { - s = { AF_INET6, SOCK_STREAM, 0 }; - - s.set(IPPROTO_IPV6, IPV6_V6ONLY, 0); - ASSERT_EQ(0, s.get<int>(IPPROTO_IPV6, IPV6_V6ONLY)); - - s.set(IPPROTO_IPV6, IPV6_V6ONLY, 1); - ASSERT_EQ(1, s.get<int>(IPPROTO_IPV6, IPV6_V6ONLY)); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); -} - -TEST(Misc, initializer) -{ - Socket s1, s2; - - try { - s1 = { AF_INET6, SOCK_STREAM, 0 }; - s2 = { AF_INET6, SOCK_STREAM, 0 }; - - SocketListener listener { - { s1, direction::Read }, - { s2, direction::Read }, - }; - - ASSERT_EQ(2UL, listener.size()); - - listener.list([&] (const auto &so, auto direction) { - ASSERT_TRUE(so == s1 || so == s2); - ASSERT_EQ(direction::Read, direction); - }); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s1.close(); - s2.close(); -} - -/* -------------------------------------------------------- - * Select tests - * -------------------------------------------------------- */ - -TEST(ListenerMethodSelect, timeout) -{ - std::thread server([] () { - Socket s, client; - SocketListener listener(Select); - bool running = true; - int tries = 0; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s.set(SOL_SOCKET, SO_REUSEADDR, 1); - s.bind(Internet{"*", 10000, AF_INET}); - s.listen(10); - - listener.add(s, direction::Read); - - while (running) { - try { - listener.select(500ms); - client = s.accept(); - running = false; - - // Abort if no client connected - if (tries >= 10) - running = false; - } catch (const Timeout &) { - } - } - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - client.close(); - }); - - std::thread client([] () { - std::this_thread::sleep_for(2s); - - Socket s; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s.connect(Internet{"localhost", 10000, AF_INET}); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); - - server.join(); - client.join(); -} - -TEST(ListenerMethodSelect, add) -{ - Socket s, s2; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Select); - - listener.add(s, direction::Read); - listener.add(s2, direction::Read); - - ASSERT_EQ(2UL, listener.size()); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - s2.close(); -} - -TEST(ListenerMethodSelect, remove) -{ - Socket s, s2; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Select); - - listener.add(s, direction::Read); - listener.add(s2, direction::Read); - listener.remove(s, direction::Read); - listener.remove(s2, direction::Read); - - ASSERT_EQ(0UL, listener.size()); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - s2.close(); -} - -/* - * Add two sockets for both direction::Reading and writing, them remove only direction::Reading and then - * move only writing. - */ -TEST(ListenerMethodSelect, inOut) -{ - Socket s, s2; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Select); - - listener.add(s, direction::Read | direction::Write); - listener.add(s2, direction::Read | direction::Write); - - listener.list([&] (Socket &si, int dir) { - ASSERT_TRUE(si == s || si == s2); - ASSERT_EQ(0x03, static_cast<int>(dir)); - }); - - listener.remove(s, direction::Write); - listener.remove(s2, direction::Write); - - ASSERT_EQ(2UL, listener.size()); - - listener.list([&] (Socket &si, int dir) { - ASSERT_TRUE(si == s || si == s2); - ASSERT_EQ(direction::Read, dir); - }); - - listener.remove(s, direction::Read); - listener.remove(s2, direction::Read); - - ASSERT_EQ(0UL, listener.size()); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - s2.close(); -} - -TEST(ListenerMethodSelect, addSame) -{ - Socket s; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Select); - - listener.add(s, direction::Read); - ASSERT_EQ(1UL, listener.size()); - listener.list([&] (const Socket &si, int dir) { - ASSERT_TRUE(si == s); - ASSERT_EQ(direction::Read, dir); - }); - - listener.add(s, direction::Write); - ASSERT_EQ(1UL, listener.size()); - listener.list([&] (const Socket &si, int dir) { - ASSERT_TRUE(si == s); - ASSERT_EQ(0x03, static_cast<int>(dir)); - }); - - // Oops, added the same - listener.add(s, direction::Read); - listener.add(s, direction::Write); - listener.add(s, direction::Read | direction::Write); - - ASSERT_EQ(1UL, listener.size()); - listener.list([&] (const Socket &si, int dir) { - ASSERT_TRUE(si == s); - ASSERT_EQ(0x03, static_cast<int>(dir)); - }); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); -} - -/* -------------------------------------------------------- - * Poll tests + * Non-blocking connect * -------------------------------------------------------- */ -TEST(ListenerMethodPoll, timeout) -{ - std::thread server([] () { - Socket s, client; - SocketListener listener(Poll); - bool running = true; - int tries = 0; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s.set(SOL_SOCKET, SO_REUSEADDR, 1); - s.bind(Internet{"*", 10000, AF_INET}); - s.listen(10); - - listener.add(s, direction::Read); - - while (running) { - try { - listener.select(500ms); - client = s.accept(); - running = false; - - // Abort if no client connected - if (tries >= 10) - running = false; - } catch (const Timeout &) { - } - } - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - client.close(); - }); - - std::thread client([] () { - std::this_thread::sleep_for(2s); - - Socket s; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s.connect(Internet{"localhost", 10000, AF_INET}); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); - - server.join(); - client.join(); -} - -TEST(ListenerMethodPoll, add) -{ - Socket s, s2; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Poll); +class NonBlockingConnectTest : public testing::Test { +protected: + SocketTcp m_server{AF_INET, 0}; + SocketTcp m_client{AF_INET, 0}; - listener.add(s, direction::Read); - listener.add(s2, direction::Read); - - ASSERT_EQ(2UL, listener.size()); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - s2.close(); -} - -TEST(ListenerMethodPoll, remove) -{ - Socket s, s2; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Poll); - - listener.add(s, direction::Read); - listener.add(s2, direction::Read); - listener.remove(s, direction::Read); - listener.remove(s2, direction::Read); - - ASSERT_EQ(0UL, listener.size()); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } + std::thread m_tserver; + std::thread m_tclient; - s.close(); - s2.close(); -} - -TEST(ListenerMethodPoll, inOut) -{ - Socket s, s2; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Poll); - - listener.add(s, direction::Read | direction::Write); - listener.add(s2, direction::Read | direction::Write); - - listener.list([&] (Socket &si, int dir) { - ASSERT_TRUE(si == s || si == s2); - ASSERT_EQ(0x03, static_cast<int>(dir)); - }); - - listener.remove(s, direction::Write); - listener.remove(s2, direction::Write); - - ASSERT_EQ(2UL, listener.size()); - - listener.list([&] (Socket &si, int dir) { - ASSERT_TRUE(si == s || si == s2); - ASSERT_EQ(direction::Read, dir); - }); - - listener.remove(s, direction::Read); - listener.remove(s2, direction::Read); - - ASSERT_EQ(0UL, listener.size()); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; +public: + NonBlockingConnectTest() + { + m_client.setBlockMode(false); } - s.close(); - s2.close(); -} - -TEST(ListenerMethodPoll, addSame) -{ - Socket s; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - SocketListener listener(Poll); - - listener.add(s, direction::Read); - ASSERT_EQ(1UL, listener.size()); - listener.list([&] (const Socket &si, int dir) { - ASSERT_TRUE(si == s); - ASSERT_EQ(direction::Read, dir); - }); - - listener.add(s, direction::Write); - ASSERT_EQ(1UL, listener.size()); - listener.list([&] (const Socket &si, int dir) { - ASSERT_TRUE(si == s); - ASSERT_EQ(0x03, static_cast<int>(dir)); - }); - - // Oops, added the same - listener.add(s, direction::Read); - listener.add(s, direction::Write); - listener.add(s, direction::Read | direction::Write); - - ASSERT_EQ(1UL, listener.size()); - listener.list([&] (const Socket &si, int dir) { - ASSERT_TRUE(si == s); - ASSERT_EQ(0x03, static_cast<int>(dir)); - }); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; + ~NonBlockingConnectTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); } - - s.close(); -} - -/* -------------------------------------------------------- - * Socket listener class - * -------------------------------------------------------- */ - -TEST(Listener, connection) -{ - std::thread server([] () { - Socket s; - SocketListener listener; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - - s.set(SOL_SOCKET, SO_REUSEADDR, 1); - s.bind(Internet{"*", 10000, AF_INET}); - s.listen(8); - - listener.add(s, direction::Read); - - auto client = listener.select(10s); - - ASSERT_TRUE(client.direction == direction::Read); - ASSERT_TRUE(client.socket == s); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); +}; - std::thread client([] () { - Socket client; - - std::this_thread::sleep_for(500ms); - - try { - client = { AF_INET, SOCK_STREAM, 0 }; - client.connect(Internet{"localhost", 10000, AF_INET}); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - client.close(); - }); - - server.join(); - client.join(); -} - -TEST(Listener, connectionAndRead) +TEST_F(NonBlockingConnectTest, success) { - std::thread server([] () { - Socket s; - SocketListener listener; - - try { - s = { AF_INET, SOCK_STREAM, 0 }; - - s.set(SOL_SOCKET, SO_REUSEADDR, 1); - s.bind(Internet("*", 10000, AF_INET)); - s.listen(8); - - // direction::Read for master - listener.add(s, direction::Read); - - auto result = listener.select(10s); + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.bind(Internet("*", 16000, AF_INET)); + m_server.listen(); - ASSERT_TRUE(result.direction == direction::Read); - ASSERT_TRUE(result.socket == s); - - // Wait for client - auto client = s.accept(); - listener.add(client, direction::Read); - - result = listener.select(10s); - - ASSERT_TRUE(result.direction == direction::Read); - ASSERT_TRUE(result.socket == client); - - char data[512]; - auto nb = client.recv(data, sizeof (data) - 1); - - data[nb] = '\0'; + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); - client.close(); - ASSERT_STREQ("hello world", data); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); - - std::thread client([] () { - Socket client; - - std::this_thread::sleep_for(500ms); - - try { - client = Socket(AF_INET, SOCK_STREAM, 0); - client.connect(Internet("localhost", 10000, AF_INET)); - client.send("hello world"); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - + m_server.close(); client.close(); }); - server.join(); - client.join(); + std::this_thread::sleep_for(100ms); + + m_tclient = std::thread([this] () { + try { + m_client.waitConnect(Internet("127.0.0.1", 16000, AF_INET), 3000); + } catch (const SocketError &error) { + FAIL() << error.what(); + } + + ASSERT_EQ(SocketState::Connected, m_client.state()); + + m_client.close(); + }); } -TEST(Listener, bigData) +TEST_F(NonBlockingConnectTest, fail) { - std::thread server([] () { - std::ostringstream out; - - Socket server; - SocketListener listener; - bool finished(false); - + /* + * /!\ If you find a way to test this locally please tell me /!\ + */ + m_tclient = std::thread([this] () { try { - server = { AF_INET, SOCK_STREAM, 0 }; - - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.bind(Internet{"*", 10000, AF_INET}); - server.listen(10); - listener.add(server, direction::Read); + m_client.waitConnect(Internet("google.fr", 9000, AF_INET), 100); - while (!finished) { - auto s = listener.select(60s).socket; - - if (s == server) { - listener.add(s.accept(), direction::Read); - } else { - char data[512]; - auto nb = s.recv(data, sizeof (data) - 1); - - if (nb == 0) - finished = true; - else { - data[nb] = '\0'; - out << data; - } - } - } - - ASSERT_EQ(9000002UL, out.str().size()); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; + FAIL() << "Expected exception, got success"; + } catch (const SocketError &error) { + ASSERT_EQ(SocketError::Timeout, error.code()); } - server.close(); + m_client.close(); }); - - std::thread client([] () { - std::string data; - - data.reserve(9000000); - for (int i = 0; i < 9000000; ++i) - data.push_back('a'); - - data.push_back('\r'); - data.push_back('\n'); - - std::this_thread::sleep_for(500ms); - - Socket client; - SocketListener listener; - - try { - client = { AF_INET, SOCK_STREAM, 0 }; - - client.connect(Internet{"localhost", 10000, AF_INET}); - client.blockMode(false); - listener.add(client, direction::Write); - - while (data.size() > 0) { - auto s = listener.select(30s).socket; - auto nb = s.send(data.data(), data.size()); - data.erase(0, nb); - } - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - client.close(); - }); - - server.join(); - client.join(); } /* -------------------------------------------------------- - * Multiple selection tests + * TCP accept * -------------------------------------------------------- */ -TEST(MultipleSelection, select) -{ - /* - * Normally, 3 sockets added for writing should be marked direction::Ready immediately - * as there are no data being currently queued to be sent. - */ - std::thread server([] () { - Socket master; +class TcpAcceptTest : public testing::Test { +protected: + SocketTcp m_server{AF_INET, 0}; + SocketTcp m_client{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; - try { - SocketListener masterListener, clientListener; - master = { AF_INET, SOCK_STREAM, 0 }; - - master.set(SOL_SOCKET, SO_REUSEADDR, 1); - master.bind(Internet{"*", 10000, AF_INET}); - master.listen(8); +public: + TcpAcceptTest() + { + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.bind(Internet("*", 16000, AF_INET)); + m_server.listen(); + } - masterListener.add(master, direction::Read); - - while (clientListener.size() != 3) { - masterListener.select(3s); - clientListener.add(master.accept(), direction::Write); - } + ~TcpAcceptTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); + } +}; - // Now do the test of writing - auto result = clientListener.selectMultiple(3s); - ASSERT_EQ(3UL, result.size()); - - clientListener.list([] (auto s, auto) { - s.close(); - }); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; +TEST_F(TcpAcceptTest, blockingWaitSuccess) +{ + m_tserver = std::thread([this] () { + try { + m_server.waitAccept(3000).close(); + } catch (const SocketError &error) { + FAIL() << error.what(); } - master.close(); - }); - - std::thread client([] () { - Socket s1, s2, s3; - - try { - s1 = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - s3 = { AF_INET, SOCK_STREAM, 0 }; - - std::this_thread::sleep_for(1s); - - s1.connect(Internet{"localhost", 10000, AF_INET}); - s2.connect(Internet{"localhost", 10000, AF_INET}); - s3.connect(Internet{"localhost", 10000, AF_INET}); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s1.close(); - s2.close(); - s3.close(); - }); - - server.join(); - client.join(); -} - -#if defined(SOCKET_LISTENER_HAVE_POLL) - -TEST(MultipleSelection, poll) -{ - /* - * Normally, 3 sockets added for writing should be marked direction::Ready immediately - * as there are no data being currently queued to be sent. - */ - std::thread server([] () { - Socket master; - - try { - SocketListener masterListener(Poll), clientListener(Poll); - master = { AF_INET, SOCK_STREAM, 0 }; - - master.set(SOL_SOCKET, SO_REUSEADDR, 1); - master.bind(Internet{"*", 10000, AF_INET}); - master.listen(8); - - masterListener.add(master, direction::Read); - - while (clientListener.size() != 3) { - masterListener.select(3s); - clientListener.add(master.accept(), direction::Write); - } - - // Now do the test of writing - auto result = clientListener.selectMultiple(3s); - ASSERT_EQ(3UL, result.size()); - - clientListener.list([] (auto s, auto) { - s.close(); - }); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - master.close(); + m_server.close(); }); - std::thread client([] () { - Socket s1, s2, s3; - - try { - s1 = { AF_INET, SOCK_STREAM, 0 }; - s2 = { AF_INET, SOCK_STREAM, 0 }; - s3 = { AF_INET, SOCK_STREAM, 0 }; - - std::this_thread::sleep_for(1s); + std::this_thread::sleep_for(100ms); - s1.connect(Internet("localhost", 10000, AF_INET)); - s2.connect(Internet("localhost", 10000, AF_INET)); - s3.connect(Internet("localhost", 10000, AF_INET)); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s1.close(); - s2.close(); - s3.close(); + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.close(); }); - - server.join(); - client.join(); } -#endif - -/* -------------------------------------------------------- - * Basic TCP tests - * -------------------------------------------------------- */ - -TEST(BasicTcp, sendipv4) { - std::thread server([] () { - Socket server, client; - +TEST_F(TcpAcceptTest, nonBlockingWaitSuccess) +{ + m_tserver = std::thread([this] () { try { - server = { AF_INET, SOCK_STREAM, 0 }; - - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.bind(Internet{"*", 10000, AF_INET}); - server.listen(8); - - client = server.accept(); - - char data[512]; - auto nb = client.recv(data, sizeof (data) - 1); - - data[nb] = '\0'; - - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; + m_server.setBlockMode(false); + m_server.waitAccept(3000).close(); + } catch (const SocketError &error) { + FAIL() << error.what(); } - server.close(); - client.close(); + m_server.close(); }); - std::thread client([] () { - Socket s; - - std::this_thread::sleep_for(500ms); - try { - s = { AF_INET, SOCK_STREAM, 0 }; + std::this_thread::sleep_for(100ms); - s.connect(Internet{"localhost", 10000, AF_INET}); - s.send("hello"); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.close(); }); - - server.join(); - client.join(); } -TEST(BasicTcp, sendipv6) { - std::thread server([] () { - Socket server, client; - - try { - server = { AF_INET6, SOCK_STREAM, 0 }; - - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.bind(Internet("*", 10000, AF_INET6)); - server.listen(8); - - client = server.accept(); - - char data[512]; - auto nb = client.recv(data, sizeof (data) - 1); - - data[nb] = '\0'; - - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } +TEST_F(TcpAcceptTest, nonBlockingWaitFail) +{ + // No client, no accept + try { + m_server.setBlockMode(false); + m_server.waitAccept(100).close(); - server.close(); - client.close(); - }); - - std::thread client([] () { - Socket s; - - std::this_thread::sleep_for(500ms); - try { - s = { AF_INET6, SOCK_STREAM, 0 }; + FAIL() << "Expected exception, got success"; + } catch (const SocketError &error) { + ASSERT_EQ(SocketError::Timeout, error.code()); + } - s.connect(Internet{"ip6-localhost", 10000, AF_INET6}); - s.send("hello"); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); - - server.join(); - client.join(); + m_server.close(); } -#if !defined(_WIN32) - -TEST(BasicTcp, sendunix) { - std::thread server([] () { - Socket server, client; - - try { - server = { AF_UNIX, SOCK_STREAM, 0 }; - - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.bind(Unix("/tmp/gtest-send-tcp-unix.sock", true)); - server.listen(8); - - client = server.accept(); - - char data[512]; - auto nb = client.recv(data, sizeof (data) - 1); - - data[nb] = '\0'; - - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - server.close(); - client.close(); - }); - - std::thread client([] () { - Socket s; - - std::this_thread::sleep_for(500ms); - try { - s = { AF_UNIX, SOCK_STREAM, 0 }; - - s.connect(Unix{"/tmp/gtest-send-tcp-unix.sock"}); - s.send("hello"); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); - - server.join(); - client.join(); -} - -#endif - /* -------------------------------------------------------- - * Basic UDP tests + * TCP recv * -------------------------------------------------------- */ -TEST(BasicUdp, sendipv4) { - std::thread server([] () { - Socket server; - - try { - server = { AF_INET, SOCK_DGRAM, 0 }; +class TcpRecvTest : public testing::Test { +protected: + SocketTcp m_server{AF_INET, 0}; + SocketTcp m_client{AF_INET, 0}; - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.bind(Internet("*", 10000, AF_INET)); - - char data[512]; - auto nb = server.recvfrom(data, sizeof (data) - 1); - - data[nb] = '\0'; + std::thread m_tserver; + std::thread m_tclient; - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - server.close(); - }); - - std::thread client([] () { - Socket s; - - std::this_thread::sleep_for(500ms); - try { - s = { AF_INET, SOCK_DGRAM, 0 }; - - s.sendto("hello", Internet{"localhost", 10000, AF_INET}); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); +public: + TcpRecvTest() + { + m_server.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_server.bind(Internet("*", 16000, AF_INET)); + m_server.listen(); + } - server.join(); - client.join(); -} - -TEST(BasicUdp, sendipv6) { - std::thread server([] () { - Socket server; - - try { - server = { AF_INET6, SOCK_DGRAM, 0 }; - - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.set(IPPROTO_IPV6, IPV6_V6ONLY, 1); - server.bind(Internet{"*", 10000, AF_INET6}); - - char data[512]; - auto nb = server.recvfrom(data, sizeof (data) - 1); - - data[nb] = '\0'; + ~TcpRecvTest() + { + if (m_tserver.joinable()) + m_tserver.join(); + if (m_tclient.joinable()) + m_tclient.join(); + } +}; - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - server.close(); - }); - - std::thread client([] () { - Socket s; +TEST_F(TcpRecvTest, blockingSuccess) +{ + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); - std::this_thread::sleep_for(500ms); - try { - s = { AF_INET6, SOCK_DGRAM, 0 }; + ASSERT_EQ("hello", client.recv(32)); - s.sendto("hello", Internet{"ip6-localhost", 10000, AF_INET6}); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); + client.close(); + m_server.close(); }); - server.join(); - client.join(); -} - -#if !defined(_WIN32) - -TEST(BasicUdp, sendunix) { - std::thread server([] () { - Socket server; - - try { - server = { AF_UNIX, SOCK_DGRAM, 0 }; - - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.bind(Unix{"/tmp/gtest-send-udp-unix.sock", true}); - - char data[512]; - auto nb = server.recvfrom(data, sizeof (data) - 1); - - data[nb] = '\0'; + std::this_thread::sleep_for(100ms); - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - server.close(); + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.send("hello"); + m_client.close(); }); - - std::thread client([] () { - Socket s; - - std::this_thread::sleep_for(500ms); - try { - s = { AF_UNIX, SOCK_DGRAM, 0 }; - - s.sendto("hello", Unix{"/tmp/gtest-send-udp-unix.sock"}); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - } - - s.close(); - }); - - server.join(); - client.join(); } -#endif +TEST_F(TcpRecvTest, blockingWaitSuccess) +{ + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); -/* -------------------------------------------------------- - * Non-blocking functions failures - * -------------------------------------------------------- */ - -TEST(NonBlockingFailures, connect) -{ - Socket s; + ASSERT_EQ("hello", client.waitRecv(32, 3000)); - try { - s = {AF_INET, SOCK_STREAM, 0}; - s.blockMode(false); - } catch (const std::exception &ex) { - std::cerr << ex.what() << std::endl; - return; - } + client.close(); + m_server.close(); + }); - auto time1 = std::chrono::system_clock::now(); + std::this_thread::sleep_for(100ms); - try { - s.tryConnect(Internet{"google.fr", 9000, AF_INET}, 1000); - - FAIL() << "unexpected code path"; - } catch (const std::exception &ex) {} - - auto time2 = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(time2 - time1).count(); - - // Assert between 0,9 and 1,1 seconds - ASSERT_GT(duration, 900); - ASSERT_LT(duration, 1100); - - s.close(); + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.send("hello"); + m_client.close(); + }); } -TEST(NonBlockingFailures, recv) +TEST_F(TcpRecvTest, nonBlockingWaitSuccess) { - Socket s; - Socket server; - bool ready{false}; - - try { - s = {AF_INET, SOCK_STREAM, 0}; + m_tserver = std::thread([this] () { + SocketTcp client = m_server.accept(); - server = {AF_INET, SOCK_STREAM, 0}; - server.set(SOL_SOCKET, SO_REUSEADDR, 1); - server.bind(Internet{"localhost", 10000, AF_INET}); - server.listen(10); + client.setBlockMode(false); - ready = true; - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; - return; - } - - if (!ready) { - s.close(); - server.close(); - } + ASSERT_EQ("hello", client.waitRecv(32, 3000)); - try { - s.connect(Internet{"localhost", 10000, AF_INET}); - s.blockMode(false); - } catch (const std::exception &ex) { - std::cerr << "warning: " << ex.what() << std::endl; + client.close(); + m_server.close(); + }); - s.close(); - server.close(); - - return; - } - - auto time1 = std::chrono::system_clock::now(); + std::this_thread::sleep_for(100ms); - try { - s.tryRecv(100, 1000); - - FAIL() << "unexpected code path"; - } catch (const std::exception &) {} - - auto time2 = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(time2 - time1).count(); - - // Assert between 0,9 and 1,1 seconds - ASSERT_GT(duration, 900); - ASSERT_LT(duration, 1100); - - s.close(); + m_tclient = std::thread([this] () { + m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + m_client.send("hello"); + m_client.close(); + }); } -#endif - int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);