# HG changeset patch # User David Demelier # Date 1425404934 -3600 # Node ID 4c0af1143fc4db759afcc8ce0752a19264d8b787 # Parent c9356cb38c8600e4d059664deacce233b0287893 Add wait operation (no tests yet) diff -r c9356cb38c86 -r 4c0af1143fc4 C++/Socket.cpp --- a/C++/Socket.cpp Mon Mar 02 14:00:48 2015 +0100 +++ b/C++/Socket.cpp Tue Mar 03 18:48:54 2015 +0100 @@ -92,6 +92,8 @@ if (::bind(m_handle, reinterpret_cast(&sa), addrlen) == Error) throw SocketError("bind", Socket::syserror(), errno); + + m_state = SocketState::Bound; } void Socket::close() @@ -101,6 +103,8 @@ #else ::close(m_handle); #endif + + SocketState::Closed; } void Socket::setBlockMode(bool block) @@ -151,52 +155,7 @@ #if 0 void SocketStandard::tryConnect(Socket &s, const SocketAddress &address, int timeout) { - if (m_connected) - return; - // Initial try - try { - connect(s, address); - } catch (const error::InProgress &) { - SocketListener listener{{s, Write}}; - - listener.select(timeout); - - // Socket is writable? Check if there is an error - auto error = s.get(SOL_SOCKET, SO_ERROR); - - if (error) - throw error::Error("connect", Socket::syserror(error), error); - } - - m_connected = true; -} - -Socket SocketStandard::tryAccept(Socket &s, SocketAddress &info, int timeout) -{ - SocketListener listener{{s, Read}}; - - listener.select(timeout); - - return accept(s, info); -} - -unsigned SocketStandard::tryRecv(Socket &s, void *data, unsigned len, int timeout) -{ - SocketListener listener{{s, Read}}; - - listener.select(timeout); - - return recv(s, data, len); -} - -unsigned SocketStandard::trySend(Socket &s, const void *data, unsigned len, int timeout) -{ - SocketListener listener{{s, Write}}; - - listener.select(timeout); - - return send(s, data, len); } unsigned SocketStandard::tryRecvfrom(Socket &s, void *data, unsigned len, SocketAddress &info, int timeout) @@ -217,29 +176,6 @@ return sendto(s, data, len, info); } -void Socket::blockMode(bool block) -{ -#if defined(O_NONBLOCK) && !defined(_WIN32) - int flags; - - if ((flags = fcntl(m_handle, F_GETFL, 0)) == -1) - flags = 0; - - if (block) - flags &= ~(O_NONBLOCK); - else - flags |= O_NONBLOCK; - - if (fcntl(m_handle, F_SETFL, flags) == -1) - throw error::Error("blockMode", Socket::syserror(), errno); -#else - unsigned long flags = (block) ? 0 : 1; - - if (ioctlsocket(m_handle, FIONBIO, &flags) == SOCKET_ERROR) - throw error::Error("blockMode", Socket::syserror(), WSAGetLastError()); -#endif -} - Socket Socket::tryAccept(int timeout) { SocketAddress dummy; @@ -247,25 +183,4 @@ return tryAccept(dummy, timeout); } -Socket Socket::accept() -{ - SocketAddress dummy; - - return accept(dummy); -} - -unsigned Socket::recvfrom(void *data, unsigned dataLen) -{ - SocketAddress dummy; - - return m_interface->recvfrom(*this, data, dataLen, dummy); -} - -std::string Socket::recvfrom(unsigned count) -{ - SocketAddress dummy; - - return recvfrom(count, dummy); -} - #endif diff -r c9356cb38c86 -r 4c0af1143fc4 C++/Socket.h --- a/C++/Socket.h Mon Mar 02 14:00:48 2015 +0100 +++ b/C++/Socket.h Tue Mar 03 18:48:54 2015 +0100 @@ -71,24 +71,41 @@ class SocketError : public std::exception { public: - enum { - WouldBlock, ///!< The operation requested would block + enum Code { + WouldBlockRead, ///!< The operation would block for reading + WouldBlockWrite, ///!< The operation would block for writing InProgress, ///!< The operation is in progress Timeout, ///!< The action did timeout System ///!< There is a system error }; + Code m_code; + std::string m_error; + template inline SocketError(const Args&... args) { } + inline Code code() const noexcept + { + return m_code; + } + const char *what() const noexcept { return "Error failure"; } }; +enum class SocketState { + Opened, ///!< Socket is opened + Closed, ///!< Socket has been closed + Bound, ///!< Socket is bound to address + Connected, ///!< Socket is connected to an end point + Disconnected ///!< Socket is disconnected +}; + /** * @class Socket * @brief Base socket class for socket operations @@ -183,6 +200,7 @@ protected: Handle m_handle; + SocketState m_state{SocketState::Opened}; inline Socket(Handle handle) : m_handle(handle) @@ -275,6 +293,11 @@ return m_handle; } + inline SocketState state() const noexcept + { + return m_state; + } + /** * Bind to an address. * diff -r c9356cb38c86 -r 4c0af1143fc4 C++/SocketListener.h --- a/C++/SocketListener.h Mon Mar 02 14:00:48 2015 +0100 +++ b/C++/SocketListener.h Tue Mar 03 18:48:54 2015 +0100 @@ -184,7 +184,7 @@ { return m_map.begin(); } - + inline auto cbegin() const noexcept { return m_map.cbegin(); @@ -231,8 +231,6 @@ * * @param duration the duration * @return the socket ready - * @throw SocketError on error - * @throw SocketTimeout on timeout */ template inline SocketStatus select(const std::chrono::duration &duration) @@ -247,8 +245,6 @@ * * @param timeout the optional timeout in milliseconds * @return the socket ready - * @throw SocketError on error - * @throw SocketTimeout on timeout */ inline SocketStatus select(int timeout = -1) { @@ -260,8 +256,6 @@ * * @param duration the duration * @return the socket ready - * @throw SocketError on error - * @throw SocketTimeout on timeout */ template inline std::vector selectMultiple(const std::chrono::duration &duration) @@ -275,8 +269,6 @@ * Overload with milliseconds. * * @return the socket ready - * @throw SocketError on error - * @throw SocketTimeout on timeout */ inline std::vector selectMultiple(int timeout = -1) { diff -r c9356cb38c86 -r 4c0af1143fc4 C++/SocketTcp.cpp --- a/C++/SocketTcp.cpp Mon Mar 02 14:00:48 2015 +0100 +++ b/C++/SocketTcp.cpp Tue Mar 03 18:48:54 2015 +0100 @@ -17,6 +17,7 @@ */ #include "SocketAddress.h" +#include "SocketListener.h" #include "SocketTcp.h" /* -------------------------------------------------------- @@ -73,6 +74,9 @@ void SocketTcp::connect(const SocketAddress &address) { + if (m_state == SocketState::Connected) + return; + auto &sa = address.address(); auto addrlen = address.length(); @@ -93,6 +97,50 @@ throw SocketError("connect", Socket::syserror(), errno); #endif } + + m_state = SocketState::Connected; +} + +void SocketTcp::waitConnect(const SocketAddress &address, int timeout) +{ + if (m_state == SocketState::Connected) + return; + + // Initial try + try { + connect(address); + } catch (const SocketError &ex) { + // TODO: HANDLE ERROR CODE + + SocketListener listener{{*this, SocketListener::Write}}; + + listener.select(timeout); + + // Socket is writable? Check if there is an error + int error = get(SOL_SOCKET, SO_ERROR); + + if (error) { + throw SocketError("connect", Socket::syserror(error), error); + } + } + + m_state = SocketState::Connected; +} + +SocketTcp SocketTcp::waitAccept(int timeout) +{ + SocketAddress dummy; + + return waitAccept(dummy, timeout); +} + +SocketTcp SocketTcp::waitAccept(SocketAddress &info, int timeout) +{ + SocketListener listener{{*this, SocketListener::Read}}; + + listener.select(timeout); + + return accept(info); } unsigned SocketTcp::recv(void *data, unsigned dataLen) @@ -112,11 +160,21 @@ throw SocketError("recv", Socket::syserror(), errno); #endif - } + } else if (nbread == 0) + m_state = SocketState::Closed; return (unsigned)nbread; } +unsigned SocketTcp::waitRecv(void *data, unsigned length, int timeout) +{ + SocketListener listener{{*this, SocketListener::Read}}; + + listener.select(timeout); + + return recv(data, length); +} + unsigned SocketTcp::send(const void *data, unsigned length) { int nbsent; @@ -138,3 +196,12 @@ return (unsigned)nbsent; } + +unsigned SocketTcp::waitSend(const void *data, unsigned length, int timeout) +{ + SocketListener listener{{*this, SocketListener::Write}}; + + listener.select(timeout); + + return send(data, length); +} diff -r c9356cb38c86 -r 4c0af1143fc4 C++/SocketTcp.h --- a/C++/SocketTcp.h Mon Mar 02 14:00:48 2015 +0100 +++ b/C++/SocketTcp.h Tue Mar 03 18:48:54 2015 +0100 @@ -47,14 +47,34 @@ return result; } + inline std::string waitRecv(unsigned count, int timeout) + { + std::string result; + + result.resize(count); + auto n = waitRecv(const_cast(result.data()), count, timeout); + result.resize(n); + + return result; + } + inline unsigned send(const std::string &data) { return send(data.c_str(), data.size()); } + inline unsigned waitSend(const std::string &data, int timeout) + { + return waitSend(data.c_str(), data.size(), timeout); + } + virtual unsigned recv(void *data, unsigned length) = 0; + virtual unsigned waitRecv(void *data, unsigned length, int timeout) = 0; + virtual unsigned send(const void *data, unsigned length) = 0; + + virtual unsigned waitSend(const void *data, unsigned length, int timeout) = 0; }; /** @@ -71,11 +91,21 @@ SocketTcp accept(SocketAddress &info); + SocketTcp waitAccept(int timeout); + + SocketTcp waitAccept(SocketAddress &info, int timeout); + void connect(const SocketAddress &address); + void waitConnect(const SocketAddress &address, int timeout); + unsigned recv(void *data, unsigned length) override; + unsigned waitRecv(void *data, unsigned length, int timeout) override; + unsigned send(const void *data, unsigned length) override; + + unsigned waitSend(const void *data, unsigned length, int timeout) override; }; #endif // !_SOCKET_TCP_NG_3_H_ diff -r c9356cb38c86 -r 4c0af1143fc4 C++/Tests/Sockets/main.cpp --- a/C++/Tests/Sockets/main.cpp Mon Mar 02 14:00:48 2015 +0100 +++ b/C++/Tests/Sockets/main.cpp Tue Mar 03 18:48:54 2015 +0100 @@ -64,15 +64,21 @@ { m_tserver = std::thread([this] () { m_server.bind(Internet("*", 16000, AF_INET)); + + ASSERT_EQ(SocketState::Bound, m_server.state()); + m_server.listen(); m_server.accept(); m_server.close(); }); - std::this_thread::sleep_for(500ms); + std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); + + ASSERT_EQ(SocketState::Connected, m_client.state()); + m_client.close(); }); } @@ -94,7 +100,7 @@ m_server.close(); }); - std::this_thread::sleep_for(500ms); + std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { m_client.connect(Internet("127.0.0.1", 16000, AF_INET)); @@ -148,7 +154,7 @@ m_server.close(); }); - std::this_thread::sleep_for(500ms); + std::this_thread::sleep_for(100ms); m_tclient = std::thread([this] () { Internet info("127.0.0.1", 16000, AF_INET); @@ -251,6 +257,168 @@ ASSERT_EQ(0, static_cast(m_listener.size())); } +/* -------------------------------------------------------- + * Listener: poll + * -------------------------------------------------------- */ + +#if defined(SOCKET_HAVE_POLL) + +class ListenerPollTest : public testing::Test { +protected: + SocketListener m_listener{SocketMethod::Poll}; + SocketTcp m_masterTcp{AF_INET, 0}; + SocketTcp m_clientTcp{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + ListenerPollTest() + { + m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_masterTcp.bind(Internet("*", 16000, AF_INET)); + m_masterTcp.listen(); + } + + ~ListenerPollTest() + { + if (m_tserver.joinable()) { + m_tserver.join(); + } + if (m_tclient.joinable()) { + m_tclient.join(); + } + } +}; + +TEST_F(ListenerPollTest, accept) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + 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(ListenerPollTest, recv) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + + 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 + +/* -------------------------------------------------------- + * Listener: select + * -------------------------------------------------------- */ + +class ListenerSelectTest : public testing::Test { +protected: + SocketListener m_listener{SocketMethod::Select}; + SocketTcp m_masterTcp{AF_INET, 0}; + SocketTcp m_clientTcp{AF_INET, 0}; + + std::thread m_tserver; + std::thread m_tclient; + +public: + ListenerSelectTest() + { + m_masterTcp.set(SOL_SOCKET, SO_REUSEADDR, 1); + m_masterTcp.bind(Internet("*", 16000, AF_INET)); + m_masterTcp.listen(); + } + + ~ListenerSelectTest() + { + if (m_tserver.joinable()) { + m_tserver.join(); + } + if (m_tclient.joinable()) { + m_tclient.join(); + } + } +}; + +TEST_F(ListenerSelectTest, accept) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + 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(ListenerSelectTest, recv) +{ + m_tserver = std::thread([this] () { + try { + m_listener.set(m_masterTcp, SocketListener::Read); + m_listener.select(); + + 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"); + }); +} + + + + +