浏览代码

core: add FD_SET/select() abstraction for TCPSocket

Daniele Bartolini 4 年之前
父节点
当前提交
a9acfb0c9c
共有 2 个文件被更改,包括 159 次插入0 次删除
  1. 111 0
      src/core/network/socket.cpp
  2. 48 0
      src/core/network/socket.h

+ 111 - 0
src/core/network/socket.cpp

@@ -327,4 +327,115 @@ WriteResult TCPSocket::write_nonblock(const void* data, u32 size)
 	return socket_internal::write(_priv->socket, data, size);
 }
 
+bool operator==(const TCPSocket& aa, const TCPSocket& bb)
+{
+	return aa._priv->socket == bb._priv->socket;
+}
+
+struct SocketSetPrivate
+{
+	fd_set fdset;
+#if CROWN_PLATFORM_POSIX
+	SOCKET maxfd;
+#endif
+};
+
+SocketSet::SocketSet()
+{
+	CE_STATIC_ASSERT(sizeof(_data) >= sizeof(*_priv));
+	_priv = new (_data) SocketSetPrivate();
+
+	FD_ZERO(&_priv->fdset);
+#if CROWN_PLATFORM_POSIX
+	_priv->maxfd = INVALID_SOCKET;
+#endif
+}
+
+SocketSet& SocketSet::operator=(const SocketSet& other)
+{
+	_priv->fdset = other._priv->fdset;
+#if CROWN_PLATFORM_POSIX
+	_priv->maxfd = other._priv->maxfd;
+#endif
+	return *this;
+}
+
+void SocketSet::set(TCPSocket* socket)
+{
+	FD_SET(socket->_priv->socket, &_priv->fdset);
+#if CROWN_PLATFORM_POSIX
+	if (_priv->maxfd < socket->_priv->socket)
+		_priv->maxfd = socket->_priv->socket;
+#endif
+}
+
+void SocketSet::clr(TCPSocket* socket)
+{
+	FD_CLR(socket->_priv->socket, &_priv->fdset);
+}
+
+bool SocketSet::isset(TCPSocket* socket)
+{
+	return FD_ISSET(socket->_priv->socket, &_priv->fdset) != 0;
+}
+
+u32 SocketSet::num()
+{
+#if CROWN_PLATFORM_POSIX
+	return _priv->maxfd + 1;
+#elif CROWN_PLATFORM_WINDOWS
+	return _priv->fdset.fd_count;
+#endif
+}
+
+TCPSocket SocketSet::get(u32 index)
+{
+	TCPSocket socket;
+#if CROWN_PLATFORM_POSIX
+	CE_ENSURE((int)index < FD_SETSIZE);
+	socket._priv->socket = (int)index;
+#elif CROWN_PLATFORM_WINDOWS
+	CE_ENSURE(index < _priv->fdset.fd_count);
+	socket._priv->socket = _priv->fdset.fd_array[index];
+#endif
+	return socket;
+}
+
+SelectResult SocketSet::select(u32 timeout_ms)
+{
+	struct timeval tv;
+	tv.tv_sec  = timeout_ms / 1000;
+	tv.tv_usec = timeout_ms % 1000 * 1000;
+
+	SelectResult sr;
+	sr.num_ready = 0;
+	int ret = ::select(
+#if CROWN_PLATFORM_POSIX
+		_priv->maxfd + 1
+#elif CROWN_PLATFORM_WINDOWS
+		0 // Ignored: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select
+#endif
+		, &_priv->fdset
+		, NULL
+		, NULL
+		, (timeout_ms == UINT32_MAX) ? NULL : &tv
+		);
+
+	if (ret == SOCKET_ERROR)
+	{
+		sr.error = SelectResult::GENERIC_ERROR;
+	}
+	else if (ret == 0)
+	{
+		sr.error = SelectResult::TIMEOUT;
+	}
+	else
+	{
+		sr.error = SelectResult::SUCCESS;
+		sr.num_ready = ret;
+	}
+
+	return sr;
+}
+
 } // namespace crown

+ 48 - 0
src/core/network/socket.h

@@ -119,4 +119,52 @@ struct TCPSocket
 	WriteResult write_nonblock(const void* data, u32 size);
 };
 
+/// Returns whether the sockets @a aa and @a bb represent the same socket.
+bool operator==(const TCPSocket& aa, const TCPSocket& bb);
+
+struct SelectResult
+{
+	enum Enum
+	{
+		SUCCESS,
+		TIMEOUT,
+		GENERIC_ERROR
+	} error;
+	u32 num_ready; ///< The number of descriptors ready.
+};
+
+/// FD_SET abstraction.
+///
+/// @ingroup Network
+struct SocketSet
+{
+	struct SocketSetPrivate* _priv;
+	CE_ALIGN_DECL(16, u8 _data[512+16]);
+
+	///
+	SocketSet();
+
+	///
+	SocketSet& operator=(const SocketSet& other);
+
+	///
+	void set(TCPSocket* socket);
+
+	///
+	void clr(TCPSocket* socket);
+
+	///
+	bool isset(TCPSocket* socket);
+
+	/// Returns the number of sockets in the set.
+	u32 num();
+
+	/// Returns the socket at @a index.
+	TCPSocket get(u32 index);
+
+	/// If @a timeout_ms is UINT32_MAX, it blocks indefinitely waiting for a
+	/// socket to become ready. If @a timeout is 0, it returns immediately.
+	SelectResult select(u32 timeout_ms);
+};
+
 } // namespace crown