changeset 316:4c0af1143fc4

Add wait operation (no tests yet)
author David Demelier <markand@malikania.fr>
date Tue, 03 Mar 2015 18:48:54 +0100
parents c9356cb38c86
children 890729b8cb60
files C++/Socket.cpp C++/Socket.h C++/SocketListener.h C++/SocketTcp.cpp C++/SocketTcp.h C++/Tests/Sockets/main.cpp
diffstat 6 files changed, 299 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- 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<const sockaddr *>(&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<int>(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
--- 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 <typename... Args>
 	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.
 	 *
--- 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 <typename Rep, typename Ratio>
 	inline SocketStatus select(const std::chrono::duration<Rep, Ratio> &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 <typename Rep, typename Ratio>
 	inline std::vector<SocketStatus> selectMultiple(const std::chrono::duration<Rep, Ratio> &duration)
@@ -275,8 +269,6 @@
 	 * Overload with milliseconds.
 	 *
 	 * @return the socket ready
-	 * @throw SocketError on error
-	 * @throw SocketTimeout on timeout
 	 */
 	inline std::vector<SocketStatus> selectMultiple(int timeout = -1)
 	{
--- 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<int>(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);
+}
--- 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<char *>(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_
--- 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<int>(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");
+	});
+}
+
+
+
+
+