Mercurial > code
changeset 276:05f0a3e09cbf
Socket:
* Improve exception message by adding the function prefix
* Defaulted sockets are now initialized to INVALID_SOCKET
* Socket::get() is more secure and do not require default value
* Improved unit tests
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 23 Oct 2014 17:08:13 +0200 |
parents | d945fa44f601 |
children | b544a599e08e |
files | C++/Socket.cpp C++/Socket.h C++/SocketAddress.cpp C++/SocketListener.cpp C++/Tests/Sockets/main.cpp |
diffstat | 5 files changed, 532 insertions(+), 348 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Socket.cpp Thu Oct 23 12:25:43 2014 +0200 +++ b/C++/Socket.cpp Thu Oct 23 17:08:13 2014 +0200 @@ -27,19 +27,44 @@ namespace error { +Timeout::Timeout(std::string func) + : m_error(func + ": Timeout exception") +{ +} + const char *Timeout::what() const noexcept { - return "Timeout exception"; + return m_error.c_str(); +} + +InProgress::InProgress(std::string func) + : m_error(func + ": Operation in progress") +{ } const char *InProgress::what() const noexcept { - return "Operation in progress"; + return m_error.c_str(); +} + +WouldBlock::WouldBlock(std::string func) + : m_error(func + ": Operation would block") +{ } const char *WouldBlock::what() const noexcept { - return "Operation would block"; + return m_error.c_str(); +} + +Failure::Failure(std::string func, std::string message) + : m_error(func + ": " + message) +{ +} + +const char *Failure::what() const noexcept +{ + return m_error.c_str(); } } // !error @@ -133,7 +158,7 @@ auto addrlen = addr.length(); if (::bind(s.handle(), (sockaddr *)&sa, addrlen) == SOCKET_ERROR) - throw error::Failure(Socket::syserror()); + throw error::Failure("bind", Socket::syserror()); } void Standard::connect(Socket &s, const SocketAddress &addr) @@ -148,14 +173,14 @@ */ #if defined(_WIN32) if (WSAGetLastError() == WSAEWOULDBLOCK) - throw error::InProgress(); + throw error::InProgress("connect"); - throw error::Failure(Socket::syserror()); + throw error::Failure("connect", Socket::syserror()); #else if (errno == EINPROGRESS) - throw error::InProgress(); + throw error::InProgress("connect"); - throw error::Failure(Socket::syserror()); + throw error::Failure("connect", Socket::syserror()); #endif } } @@ -172,7 +197,7 @@ handle = ::accept(s.handle(), (sockaddr *)&address, &addrlen); if (handle == INVALID_SOCKET) - throw error::Failure(Socket::syserror()); + throw error::Failure("accept", Socket::syserror()); // Usually accept works only with SOCK_STREAM info = SocketAddress(address, addrlen); @@ -183,7 +208,7 @@ void Standard::listen(Socket &s, int max) { if (::listen(s.handle(), max) == SOCKET_ERROR) - throw error::Failure(Socket::syserror()); + throw error::Failure("listen", Socket::syserror()); } unsigned Standard::recv(Socket &s, void *data, unsigned dataLen) @@ -194,14 +219,14 @@ if (nbread == SOCKET_ERROR) { #if defined(_WIN32) if (WSAGetLastError() == WSAEWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("recv"); - throw error::Failure(Socket::syserror()); + throw error::Failure("recv", Socket::syserror()); #else if (errno == EAGAIN || errno == EWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("recv"); - throw error::Failure(Socket::syserror()); + throw error::Failure("recv", Socket::syserror()); #endif } @@ -216,14 +241,14 @@ if (nbsent == SOCKET_ERROR) { #if defined(_WIN32) if (WSAGetLastError() == WSAEWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("send"); - throw error::Failure(Socket::syserror()); + throw error::Failure("send", Socket::syserror()); #else if (errno == EAGAIN || errno == EWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("send"); - throw error::Failure(Socket::syserror()); + throw error::Failure("send", Socket::syserror()); #endif } @@ -246,14 +271,14 @@ if (nbread == SOCKET_ERROR) { #if defined(_WIN32) if (WSAGetLastError() == WSAEWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("recvfrom"); - throw error::Failure(Socket::syserror()); + throw error::Failure("recvfrom", Socket::syserror()); #else if (errno == EAGAIN || errno == EWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("recvfrom"); - throw error::Failure(Socket::syserror()); + throw error::Failure("recvfrom", Socket::syserror()); #endif } @@ -268,14 +293,14 @@ if (nbsent == SOCKET_ERROR) { #if defined(_WIN32) if (WSAGetLastError() == WSAEWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("sendto"); - throw error::Failure(Socket::syserror()); + throw error::Failure("sendto", Socket::syserror()); #else if (errno == EAGAIN || errno == EWOULDBLOCK) - throw error::WouldBlock(); + throw error::WouldBlock("sendto"); - throw error::Failure(Socket::syserror()); + throw error::Failure("sendto", Socket::syserror()); #endif } @@ -297,7 +322,7 @@ m_handle = socket(domain, type, protocol); if (m_handle == INVALID_SOCKET) - throw error::Failure(syserror()); + throw error::Failure("socket", syserror()); } Socket::Socket(Handle handle) @@ -325,12 +350,12 @@ flags |= O_NONBLOCK; if (fcntl(m_handle, F_SETFL, flags) == -1) - throw error::Failure(Socket::syserror()); + throw error::Failure("blockMode", Socket::syserror()); #else unsigned long flags = (block) ? 0 : 1; if (ioctlsocket(m_handle, FIONBIO, &flags) == SOCKET_ERROR) - throw error::Failure(Socket::syserror()); + throw error::Failure("blockMode", Socket::syserror()); #endif }
--- a/C++/Socket.h Thu Oct 23 12:25:43 2014 +0200 +++ b/C++/Socket.h Thu Oct 23 17:08:13 2014 +0200 @@ -19,6 +19,7 @@ #ifndef _SOCKET_H_ #define _SOCKET_H_ +#include <cstring> #include <exception> #include <memory> #include <string> @@ -60,7 +61,11 @@ * Usually thrown on timeout in SocketListener::select. */ class Timeout final : public std::exception { +private: + std::string m_error; + public: + Timeout(std::string func); const char *what() const noexcept override; }; @@ -71,7 +76,11 @@ * Usually thrown in a non-blocking connect call. */ class InProgress final : public std::exception { +private: + std::string m_error; + public: + InProgress(std::string func); const char *what() const noexcept override; }; @@ -82,7 +91,11 @@ * Usually thrown in a non-blocking connect send or receive. */ class WouldBlock final : public std::exception { +private: + std::string m_error; + public: + WouldBlock(std::string func); const char *what() const noexcept override; }; @@ -94,18 +107,11 @@ */ class Failure final : public std::exception { private: - std::string m_message; + std::string m_error; public: - inline Failure(std::string message) - : m_message(std::move(message)) - { - } - - inline const char *what() const noexcept override - { - return m_message.c_str(); - } + Failure(std::string func, std::string message); + const char *what() const noexcept override; }; } // !error @@ -240,8 +246,8 @@ using Iface = std::shared_ptr<SocketInterface>; protected: - Iface m_interface; //!< the interface - Handle m_handle; //!< the socket shared pointer + Iface m_interface; //!< the interface + Handle m_handle { INVALID_SOCKET }; //!< the socket shared pointer public: /** @@ -316,7 +322,7 @@ void set(int level, int name, const Argument &arg) { if (setsockopt(m_handle, level, name, (Socket::ConstArg)&arg, sizeof (arg)) == SOCKET_ERROR) - throw error::Failure(syserror()); + throw error::Failure("set", syserror()); } /** @@ -324,20 +330,20 @@ * * @param level the setting level * @param name the name - * @param def the default value - * @note since getsockopt return the real size of the argument, it is recommended to set integer and such to 0 * @throw error::Failure on error */ template <typename Argument> - Argument get(int level, int name, const Argument &def = Argument()) + Argument get(int level, int name) { - Argument arg(def); - socklen_t size = sizeof (arg); + Argument desired, result{}; + socklen_t size = sizeof (result); - if (getsockopt(m_handle, level, name, (Socket::Arg)&level, &size) == SOCKET_ERROR) - throw error::Failure(syserror()); + if (getsockopt(m_handle, level, name, (Socket::Arg)&desired, &size) == SOCKET_ERROR) + throw error::Failure("get", syserror()); - return arg; + std::memcpy(&result, &desired, size); + + return result; } /**
--- a/C++/SocketAddress.cpp Thu Oct 23 12:25:43 2014 +0200 +++ b/C++/SocketAddress.cpp Thu Oct 23 17:08:13 2014 +0200 @@ -56,7 +56,7 @@ auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); if (error != 0) - throw std::runtime_error(gai_strerror(error)); + throw error::Failure("getaddrinfo", gai_strerror(error)); std::memcpy(&m_addr, res->ai_addr, res->ai_addrlen); m_addrlen = res->ai_addrlen;
--- a/C++/SocketListener.cpp Thu Oct 23 12:25:43 2014 +0200 +++ b/C++/SocketListener.cpp Thu Oct 23 17:08:13 2014 +0200 @@ -133,7 +133,7 @@ auto result = selectMultiple(ms); if (result.size() == 0) - throw error::Failure("No socket found"); + throw error::Failure("select", "No socket found"); return result[0]; } @@ -150,9 +150,9 @@ auto error = ::select(m_max + 1, &m_readset, &m_writeset, NULL, towait); if (error == SOCKET_ERROR) - throw error::Failure(Socket::syserror()); + throw error::Failure("select", Socket::syserror()); if (error == 0) - throw error::Timeout(); + throw error::Timeout("select"); std::vector<SocketStatus> sockets; for (auto &c : m_lookup) @@ -221,7 +221,6 @@ void PollMethod::add(Socket &&s, SocketDirection direction) { - bool found(false); auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const auto &pfd) { return pfd.fd == s.handle(); }); // If found, add the new direction, otherwise add a new socket @@ -271,24 +270,24 @@ { auto result = poll(m_fds.data(), m_fds.size(), ms); if (result == 0) - throw error::Timeout(); + throw error::Timeout("select"); if (result < 0) - throw error::Failure(Socket::syserror()); + throw error::Failure("select", Socket::syserror()); for (auto &fd : m_fds) if (fd.revents != 0) return { m_lookup[fd.fd], todirection(fd.revents) }; - throw error::Failure("no socket found"); + throw error::Failure("select", "No socket found"); } std::vector<SocketStatus> PollMethod::selectMultiple(int ms) { auto result = poll(m_fds.data(), m_fds.size(), ms); if (result == 0) - throw error::Timeout(); + throw error::Timeout("select"); if (result < 0) - throw error::Failure(Socket::syserror()); + throw error::Failure("select", Socket::syserror()); std::vector<SocketStatus> sockets; for (auto &fd : m_fds)
--- a/C++/Tests/Sockets/main.cpp Thu Oct 23 12:25:43 2014 +0200 +++ b/C++/Tests/Sockets/main.cpp Thu Oct 23 17:08:13 2014 +0200 @@ -1,4 +1,23 @@ +/* + * main.cpp -- test sockets + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #include <chrono> +#include <iostream> #include <sstream> #include <string> #include <thread> @@ -19,16 +38,21 @@ TEST(Misc, set) { + Socket s; + try { - Socket s(AF_INET6, SOCK_STREAM, 0); + 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, false); - ASSERT_FALSE(s.get<bool>(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.set(IPPROTO_IPV6, IPV6_V6ONLY, true); - ASSERT_TRUE(s.get<bool>(IPPROTO_IPV6, IPV6_V6ONLY)); - } catch (const std::exception &ex) { - } + s.close(); } /* -------------------------------------------------------- @@ -37,9 +61,11 @@ TEST(ListenerMethodSelect, add) { + Socket s, s2; + try { - Socket s(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Select); listener.add(s, SocketDirection::Read); @@ -47,14 +73,20 @@ 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 { - Socket s(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Select); listener.add(s, SocketDirection::Read); @@ -64,7 +96,11 @@ ASSERT_EQ(0UL, listener.size()); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } + + s.close(); + s2.close(); } /* @@ -73,9 +109,11 @@ */ TEST(ListenerMethodSelect, inOut) { + Socket s, s2; + try { - Socket s(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Select); listener.add(s, SocketDirection::Read | SocketDirection::Write); @@ -101,13 +139,19 @@ 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 { - Socket s(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Select); listener.add(s, SocketDirection::Read); @@ -135,7 +179,10 @@ ASSERT_EQ(0x03, static_cast<int>(dir)); }); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } + + s.close(); } /* -------------------------------------------------------- @@ -144,9 +191,11 @@ TEST(ListenerMethodPoll, add) { + Socket s, s2; + try { - Socket s(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Poll); listener.add(s, SocketDirection::Read); @@ -154,14 +203,20 @@ 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 { - Socket s(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Poll); listener.add(s, SocketDirection::Read); @@ -171,14 +226,20 @@ ASSERT_EQ(0UL, listener.size()); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } + + s.close(); + s2.close(); } TEST(ListenerMethodPoll, inOut) { + Socket s, s2; + try { - Socket s(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Poll); listener.add(s, SocketDirection::Read | SocketDirection::Write); @@ -204,13 +265,19 @@ ASSERT_EQ(0UL, listener.size()); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } + + s.close(); + s2.close(); } TEST(ListenerMethodPoll, addSame) { + Socket s; + try { - Socket s(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(SocketMethod::Poll); listener.add(s, SocketDirection::Read); @@ -238,7 +305,10 @@ ASSERT_EQ(0x03, static_cast<int>(dir)); }); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } + + s.close(); } /* -------------------------------------------------------- @@ -247,135 +317,116 @@ 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, SocketDirection::Read); + + auto client = listener.select(10s); + + ASSERT_TRUE(client.direction == SocketDirection::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(3s); + std::this_thread::sleep_for(500ms); try { - client = Socket(AF_INET, SOCK_STREAM, 0); - client.connect(Internet("localhost", 10000, AF_INET)); + 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(); }); - Socket s; - SocketListener listener; - - try { - s = Socket(AF_INET, SOCK_STREAM, 0); - - s.bind(Internet("localhost", 10000, AF_INET)); - s.listen(8); - - listener.add(s, SocketDirection::Read); - - auto client = listener.select(10s); - - ASSERT_TRUE(client.direction == SocketDirection::Read); - ASSERT_TRUE(client.socket == s); - } catch (const std::exception &ex) { - } - - s.close(); + server.join(); client.join(); } TEST(Listener, connectionAndRead) { - std::thread thread([] () { + 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); + + // Read for master + listener.add(s, SocketDirection::Read); + + auto result = listener.select(10s); + + ASSERT_TRUE(result.direction == SocketDirection::Read); + ASSERT_TRUE(result.socket == s); + + // Wait for client + auto client = s.accept(); + listener.add(client, SocketDirection::Read); + + result = listener.select(10s); + + ASSERT_TRUE(result.direction == SocketDirection::Read); + ASSERT_TRUE(result.socket == client); + + char data[512]; + auto nb = client.recv(data, sizeof (data) - 1); + + data[nb] = '\0'; + + 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(3s); + 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; } client.close(); }); - Socket s; - SocketListener listener; - - try { - s = Socket(AF_INET, SOCK_STREAM, 0); - - s.bind(Internet("localhost", 10000, AF_INET)); - s.listen(8); - - // Read for master - listener.add(s, SocketDirection::Read); - - auto result = listener.select(10s); - - ASSERT_TRUE(result.direction == SocketDirection::Read); - ASSERT_TRUE(result.socket == s); - - // Wait for client - auto client = s.accept(); - listener.add(client, SocketDirection::Read); - - result = listener.select(10s); - - ASSERT_TRUE(result.direction == SocketDirection::Read); - ASSERT_TRUE(result.socket == client); - - char data[512]; - auto nb = client.recv(data, sizeof (data) - 1); - - data[nb] = '\0'; - - client.close(); - ASSERT_STREQ("hello world", data); - } catch (const std::exception &ex) { - } - - s.close(); - thread.join(); + server.join(); + client.join(); } TEST(Listener, bigData) { - auto producer = [] () { - 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(3s); - - Socket client; - SocketListener listener; - - try { - client = Socket(AF_INET, SOCK_STREAM, 0); - - client.connect(Internet("localhost", 10000, AF_INET)); - client.blockMode(false); - listener.add(client, SocketDirection::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) { - } - - client.close(); - }; - - auto consumer = [] () { + std::thread server([] () { std::ostringstream out; Socket server; @@ -383,9 +434,10 @@ bool finished(false); try { - server = Socket(AF_INET, SOCK_STREAM, 0); + server = { AF_INET, SOCK_STREAM, 0 }; - server.bind(Internet("*", 10000, AF_INET)); + server.set(SOL_SOCKET, SO_REUSEADDR, 1); + server.bind(Internet{"*", 10000, AF_INET}); server.listen(10); listener.add(server, SocketDirection::Read); @@ -409,16 +461,48 @@ ASSERT_EQ(9000002UL, out.str().size()); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } server.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; - std::thread tconsumer(consumer); - std::thread tproducer(producer); + try { + client = { AF_INET, SOCK_STREAM, 0 }; + + client.connect(Internet{"localhost", 10000, AF_INET}); + client.blockMode(false); + listener.add(client, SocketDirection::Write); - tconsumer.join(); - tproducer.join(); + 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(); } /* -------------------------------------------------------- @@ -431,50 +515,60 @@ * Normally, 3 sockets added for writing should be marked ready immediately * as there are no data being currently queued to be sent. */ - std::thread tester([] () { + std::thread server([] () { + Socket master; + try { SocketListener masterListener, clientListener; - Socket master(AF_INET, SOCK_STREAM, 0); + master = { AF_INET, SOCK_STREAM, 0 }; - master.bind(Internet("*", 10000, AF_INET)); + master.set(SOL_SOCKET, SO_REUSEADDR, 1); + master.bind(Internet{"*", 10000, AF_INET}); master.listen(8); masterListener.add(master, SocketDirection::Read); while (clientListener.size() != 3) { - masterListener.select(3s); + masterListener.select(500ms); clientListener.add(master.accept(), SocketDirection::Write); } // Now do the test of writing - auto result = clientListener.selectMultiple(3s); + auto result = clientListener.selectMultiple(1s); ASSERT_EQ(3UL, result.size()); clientListener.list([] (auto s, auto) { s.close(); }); - - master.close(); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } + + master.close(); }); - - try { - Socket s1(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); - Socket s3(AF_INET, SOCK_STREAM, 0); + + std::thread client([] () { + Socket s1, s2, s3; - s1.connect(Internet("localhost", 10000, AF_INET)); - s2.connect(Internet("localhost", 10000, AF_INET)); - s3.connect(Internet("localhost", 10000, AF_INET)); + try { + s1 = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; + s3 = { AF_INET, SOCK_STREAM, 0 }; + + 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(); - } catch (const std::exception &ex) { - } + }); - tester.join(); + server.join(); + client.join(); } #if defined(SOCKET_LISTENER_HAVE_POLL) @@ -485,50 +579,60 @@ * Normally, 3 sockets added for writing should be marked ready immediately * as there are no data being currently queued to be sent. */ - std::thread tester([] () { + std::thread server([] () { + Socket master; + try { SocketListener masterListener(SocketMethod::Poll), clientListener(SocketMethod::Poll); - Socket master(AF_INET, SOCK_STREAM, 0); + master = { AF_INET, SOCK_STREAM, 0 }; - master.bind(Internet("*", 10000, AF_INET)); + master.set(SOL_SOCKET, SO_REUSEADDR, 1); + master.bind(Internet{"*", 10000, AF_INET}); master.listen(8); masterListener.add(master, SocketDirection::Read); while (clientListener.size() != 3) { - masterListener.select(3s); + masterListener.select(1s); clientListener.add(master.accept(), SocketDirection::Write); } // Now do the test of writing - auto result = clientListener.selectMultiple(3s); + auto result = clientListener.selectMultiple(500ms); ASSERT_EQ(3UL, result.size()); clientListener.list([] (auto s, auto) { s.close(); }); - - master.close(); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } + + master.close(); }); - try { - Socket s1(AF_INET, SOCK_STREAM, 0); - Socket s2(AF_INET, SOCK_STREAM, 0); - Socket s3(AF_INET, SOCK_STREAM, 0); + std::thread client([] () { + Socket s1, s2, s3; - s1.connect(Internet("localhost", 10000, AF_INET)); - s2.connect(Internet("localhost", 10000, AF_INET)); - s3.connect(Internet("localhost", 10000, AF_INET)); + try { + s1 = { AF_INET, SOCK_STREAM, 0 }; + s2 = { AF_INET, SOCK_STREAM, 0 }; + s3 = { AF_INET, SOCK_STREAM, 0 }; + + 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(); - } catch (const std::exception &ex) { - } + }); - tester.join(); + server.join(); + client.join(); } #endif @@ -538,230 +642,280 @@ * -------------------------------------------------------- */ TEST(BasicTcp, sendipv4) { + std::thread server([] () { + Socket server, client; + + 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; + } + + server.close(); + client.close(); + }); + std::thread client([] () { Socket s; - std::this_thread::sleep_for(3s); + std::this_thread::sleep_for(500ms); try { - s = Socket(AF_INET, SOCK_STREAM, 0); + s = { AF_INET, SOCK_STREAM, 0 }; - s.connect(Internet("localhost", 10000, AF_INET)); + s.connect(Internet{"localhost", 10000, AF_INET}); s.send("hello"); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); - Socket server; - - try { - server = Socket(AF_INET, SOCK_STREAM, 0); - - server.bind(Internet("*", 10000, SOCK_STREAM)); - server.listen(8); - - auto 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) { - } - - server.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; + } + + server.close(); + client.close(); + }); + std::thread client([] () { Socket s; - std::this_thread::sleep_for(3s); + std::this_thread::sleep_for(500ms); try { - s = Socket(AF_INET6, SOCK_STREAM, 0); + s = { AF_INET6, SOCK_STREAM, 0 }; - s.connect(Internet("localhost", 10000, AF_INET6)); + 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(); }); - Socket server; - - try { - server = Socket(AF_INET6, SOCK_STREAM, 0); - - server.bind(Internet("*", 10000, SOCK_STREAM)); - server.listen(8); - - auto 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) { - } - - server.close(); + server.join(); client.join(); } #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(3s); + std::this_thread::sleep_for(500ms); try { - s = Socket(AF_UNIX, SOCK_STREAM, 0); + s = { AF_UNIX, SOCK_STREAM, 0 }; - s.connect(Unix("/tmp/gtest-send-tcp-unix.sock")); + 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(); }); - Socket server; - - try { - server = Socket(AF_UNIX, SOCK_STREAM, 0); - - server.bind(Unix("/tmp/gtest-send-tcp-unix.sock", true)); - server.listen(8); - - auto 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) { - } - - server.close(); + server.join(); client.join(); } #endif +/* -------------------------------------------------------- + * Basic UDP tests + * -------------------------------------------------------- */ + TEST(BasicUdp, sendipv4) { + std::thread server([] () { + Socket server; + + try { + server = { AF_INET, SOCK_DGRAM, 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'; + + 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(3s); + std::this_thread::sleep_for(500ms); try { - s = Socket(AF_INET, SOCK_DGRAM, 0); + s = { AF_INET, SOCK_DGRAM, 0 }; - s.sendto("hello", Internet("localhost", 10000, AF_INET)); + s.sendto("hello", Internet{"localhost", 10000, AF_INET}); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); - Socket server; - - try { - server = Socket(AF_INET, SOCK_DGRAM, 0); - - server.bind(Internet("*", 10000, SOCK_DGRAM)); - - char data[512]; - auto nb = server.recvfrom(data, sizeof (data) - 1); - - data[nb] = '\0'; - - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - } - - server.close(); + 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'; + + 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(3s); + std::this_thread::sleep_for(500ms); try { - s = Socket(AF_INET6, SOCK_DGRAM, 0); + s = { AF_INET6, SOCK_DGRAM, 0 }; - s.sendto("hello", Internet("localhost", 10000, AF_INET6)); + s.sendto("hello", Internet{"ip6-localhost", 10000, AF_INET6}); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); - Socket server; - - try { - server = Socket(AF_INET6, SOCK_DGRAM, 0); - - server.bind(Internet("*", 10000, SOCK_DGRAM)); - - char data[512]; - auto nb = server.recvfrom(data, sizeof (data) - 1); - - data[nb] = '\0'; - - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - } - - 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'; + + 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(3s); + std::this_thread::sleep_for(500ms); try { - s = Socket(AF_UNIX, SOCK_DGRAM, 0); + s = { AF_UNIX, SOCK_DGRAM, 0 }; - s.sendto("hello", Unix("/tmp/gtest-send-udp-unix.sock")); + s.sendto("hello", Unix{"/tmp/gtest-send-udp-unix.sock"}); } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); - Socket server; - - try { - server = Socket(AF_UNIX, SOCK_DGRAM, 0); - - server.bind(Unix("/tmp/gtest-send-udp-unix.sock", true)); - - char data[512]; - auto nb = server.recvfrom(data, sizeof (data) - 1); - - data[nb] = '\0'; - - ASSERT_STREQ("hello", data); - } catch (const std::exception &ex) { - } - - server.close(); + server.join(); client.join(); }