From 67c56b2abac744390a1fd271ecb24f2dc7936007 Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 1 Oct 2018 23:04:53 +0200 Subject: [PATCH] Network/TcpClient: Add PollForConnected --- ChangeLog.md | 1 + include/Nazara/Network/TcpClient.hpp | 2 + src/Nazara/Network/Posix/SocketImpl.cpp | 105 ++++++++++++------------ src/Nazara/Network/Posix/SocketImpl.hpp | 2 +- src/Nazara/Network/TcpClient.cpp | 61 +++++++++++--- src/Nazara/Network/Win32/SocketImpl.cpp | 105 ++++++++++++------------ src/Nazara/Network/Win32/SocketImpl.hpp | 2 +- 7 files changed, 159 insertions(+), 119 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 1b4bc564b..01a31cf2a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -149,6 +149,7 @@ Nazara Engine: - ⚠️ AbstractSocket::OnStateChange has been replaced by OnStateChanged, which is now called after state has been changed (with oldState and newState as parameters). - ⚠️ TcpClient::WaitForconnected now closes the socket on failure. - ⚠️ TcpClient::WaitForconnected now returns the new socket state. +- Added TcpClient::PollForConnected Nazara Development Kit: - Added ImageWidget (#139) diff --git a/include/Nazara/Network/TcpClient.hpp b/include/Nazara/Network/TcpClient.hpp index 7c92e2ce0..13ab9a13b 100644 --- a/include/Nazara/Network/TcpClient.hpp +++ b/include/Nazara/Network/TcpClient.hpp @@ -45,6 +45,8 @@ namespace Nz inline bool IsLowDelayEnabled() const; inline bool IsKeepAliveEnabled() const; + SocketState PollForConnected(UInt64 waitDuration = 0); + bool Receive(void* buffer, std::size_t size, std::size_t* received); bool ReceivePacket(NetPacket* packet); diff --git a/src/Nazara/Network/Posix/SocketImpl.cpp b/src/Nazara/Network/Posix/SocketImpl.cpp index 46afef418..8bccda0db 100644 --- a/src/Nazara/Network/Posix/SocketImpl.cpp +++ b/src/Nazara/Network/Posix/SocketImpl.cpp @@ -138,59 +138,6 @@ namespace Nz return SocketState_Connected; } - SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error) - { - SocketState state = Connect(handle, address, error); - if (state == SocketState_Connecting) - { - // http://developerweb.net/viewtopic.php?id=3196 - fd_set localSet; - FD_ZERO(&localSet); - FD_SET(handle, &localSet); - - timeval tv; - tv.tv_sec = static_cast(msTimeout / 1000ULL); - tv.tv_usec = static_cast((msTimeout % 1000ULL) * 1000ULL); - - int ret = select(handle + 1, nullptr, &localSet, &localSet, (msTimeout > 0) ? &tv : nullptr); - if (ret > 0) - { - int code = GetLastErrorCode(handle, error); - if (code < 0) //< GetLastErrorCode() failed - return SocketState_NotConnected; - - if (code) - { - if (error) - *error = TranslateErrnoToSocketError(code); - - return SocketState_NotConnected; - } - } - else if (ret == 0) - { - if (error) - *error = SocketError_TimedOut; - - return SocketState_NotConnected; - } - else - { - if (error) - *error = TranslateErrnoToSocketError(GetLastErrorCode()); - - return SocketState_NotConnected; - } - - if (error) - *error = SocketError_NoError; - - state = SocketState_Connected; - } - - return state; - } - bool SocketImpl::Initialize() { return true; @@ -462,6 +409,58 @@ namespace Nz return static_cast(result); } + SocketState SocketImpl::PollConnection(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error) + { + // http://developerweb.net/viewtopic.php?id=3196 + fd_set localSet; + FD_ZERO(&localSet); + FD_SET(handle, &localSet); + + timeval tv; + tv.tv_sec = static_cast(msTimeout / 1000ULL); + tv.tv_usec = static_cast((msTimeout % 1000ULL) * 1000ULL); + + int ret = ::select(0, nullptr, &localSet, &localSet, (msTimeout >= 0 && msTimeout != std::numeric_limits::max()) ? &tv : nullptr); + if (ret > 0) + { + int code = GetLastErrorCode(handle, error); + if (code < 0) //< GetLastErrorCode() failed + return SocketState_NotConnected; + + if (code) + { + if (error) + *error = TranslateWSAErrorToSocketError(code); + + return SocketState_NotConnected; + } + } + else if (ret == 0) + { + if (error) + { + if (msTimeout > 0) + *error = SocketError_TimedOut; + else + *error = SocketError_NoError; + } + + return SocketState_Connecting; + } + else + { + if (error) + *error = TranslateWSAErrorToSocketError(WSAGetLastError()); + + return SocketState_NotConnected; + } + + if (error) + *error = SocketError_NoError; + + return SocketState_Connected; + } + bool SocketImpl::Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error) { NazaraAssert(handle != InvalidHandle, "Invalid handle"); diff --git a/src/Nazara/Network/Posix/SocketImpl.hpp b/src/Nazara/Network/Posix/SocketImpl.hpp index af22eb8dd..7365ca600 100644 --- a/src/Nazara/Network/Posix/SocketImpl.hpp +++ b/src/Nazara/Network/Posix/SocketImpl.hpp @@ -40,7 +40,6 @@ namespace Nz static void Close(SocketHandle handle); static SocketState Connect(SocketHandle handle, const IpAddress& address, SocketError* error); - static SocketState Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error); static bool Initialize(); @@ -61,6 +60,7 @@ namespace Nz static std::size_t QuerySendBufferSize(SocketHandle handle, SocketError* error = nullptr); static unsigned int Poll(PollSocket* fdarray, std::size_t nfds, int timeout, SocketError* error); + static SocketState PollConnection(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error); static bool Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error); static bool ReceiveFrom(SocketHandle handle, void* buffer, int length, IpAddress* from, int* read, SocketError* error); diff --git a/src/Nazara/Network/TcpClient.cpp b/src/Nazara/Network/TcpClient.cpp index 441174836..9b43fbea0 100644 --- a/src/Nazara/Network/TcpClient.cpp +++ b/src/Nazara/Network/TcpClient.cpp @@ -173,6 +173,50 @@ namespace Nz return QueryAvailableBytes(); } + /*! + * \brief Polls the connection status of the currently connecting socket + * \return New socket state, which maybe unchanged (if connecting is still pending), SocketState_Connected if connection is successful or SocketState_NotConnected if connection failed + * + * This functions checks if the pending connection has either succeeded, failed or is still processing at the time of the call. + * + * \remark This function doesn't do anything if the socket is not currently connecting. + * + * \see WaitForConnected + */ + SocketState TcpClient::PollForConnected(UInt64 waitDuration) + { + switch (m_state) + { + case SocketState_Connecting: + { + NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); + + SocketState newState = SocketImpl::PollConnection(m_handle, m_peerAddress, waitDuration, &m_lastError); + + // Prevent valid peer address in non-connected state + if (newState == SocketState_NotConnected) + { + m_openMode = OpenMode_NotOpen; + m_peerAddress = IpAddress::Invalid; + } + + UpdateState(newState); + return newState; + } + + case SocketState_Connected: + case SocketState_NotConnected: + return m_state; + + case SocketState_Bound: + case SocketState_Resolving: + break; + } + + NazaraInternalError("Unexpected socket state (0x" + String::Number(m_state, 16) + ')'); + return m_state; + } + /*! * \brief Receives the data available * \return true If data received @@ -439,6 +483,8 @@ namespace Nz * \param msTimeout Time in milliseconds before time out (0 for system-specific duration, like a blocking connect would) * * \remark This function doesn't do anything if the socket is not currently connecting. + * + * \see PollForConnected */ SocketState TcpClient::WaitForConnected(UInt64 msTimeout) { @@ -448,18 +494,11 @@ namespace Nz { NazaraAssert(m_handle != SocketImpl::InvalidHandle, "Invalid handle"); - CallOnExit restoreBlocking; - if (m_isBlockingEnabled) - { - SocketImpl::SetBlocking(m_handle, false); - restoreBlocking.Reset([this] () - { - SocketImpl::SetBlocking(m_handle, true); - }); - } + SocketState newState = SocketImpl::PollConnection(m_handle, m_peerAddress, (msTimeout > 0) ? msTimeout : std::numeric_limits::max(), &m_lastError); - SocketState newState = SocketImpl::Connect(m_handle, m_peerAddress, msTimeout, &m_lastError); - NazaraAssert(newState != SocketState_Connecting, "Invalid internal return"); //< Connect cannot return Connecting is a timeout was specified + // If connection is still pending after waiting, cancel it + if (newState == SocketState_Connecting) + newState = SocketState_NotConnected; // Prevent valid stats in non-connected state if (newState == SocketState_NotConnected) diff --git a/src/Nazara/Network/Win32/SocketImpl.cpp b/src/Nazara/Network/Win32/SocketImpl.cpp index e1191e07d..f9e4b42f5 100644 --- a/src/Nazara/Network/Win32/SocketImpl.cpp +++ b/src/Nazara/Network/Win32/SocketImpl.cpp @@ -154,59 +154,6 @@ namespace Nz return SocketState_Connected; } - SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error) - { - SocketState state = Connect(handle, address, error); - if (state == SocketState_Connecting) - { - // http://developerweb.net/viewtopic.php?id=3196 - fd_set localSet; - FD_ZERO(&localSet); - FD_SET(handle, &localSet); - - timeval tv; - tv.tv_sec = static_cast(msTimeout / 1000ULL); - tv.tv_usec = static_cast((msTimeout % 1000ULL) * 1000ULL); - - int ret = select(0, nullptr, &localSet, &localSet, (msTimeout > 0) ? &tv : nullptr); - if (ret > 0) - { - int code = GetLastErrorCode(handle, error); - if (code < 0) //< GetLastErrorCode() failed - return SocketState_NotConnected; - - if (code) - { - if (error) - *error = TranslateWSAErrorToSocketError(code); - - return SocketState_NotConnected; - } - } - else if (ret == 0) - { - if (error) - *error = SocketError_TimedOut; - - return SocketState_NotConnected; - } - else - { - if (error) - *error = TranslateWSAErrorToSocketError(WSAGetLastError()); - - return SocketState_NotConnected; - } - - if (error) - *error = SocketError_NoError; - - state = SocketState_Connected; - } - - return state; - } - bool SocketImpl::Initialize() { int errorCode = WSAStartup(MAKEWORD(2, 2), &s_WSA); @@ -502,6 +449,58 @@ namespace Nz return 0; #endif } + + SocketState SocketImpl::PollConnection(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error) + { + // http://developerweb.net/viewtopic.php?id=3196 + fd_set localSet; + FD_ZERO(&localSet); + FD_SET(handle, &localSet); + + timeval tv; + tv.tv_sec = static_cast(msTimeout / 1000ULL); + tv.tv_usec = static_cast((msTimeout % 1000ULL) * 1000ULL); + + int ret = ::select(0, nullptr, &localSet, &localSet, (msTimeout != std::numeric_limits::max()) ? &tv : nullptr); + if (ret > 0) + { + int code = GetLastErrorCode(handle, error); + if (code < 0) //< GetLastErrorCode() failed + return SocketState_NotConnected; + + if (code) + { + if (error) + *error = TranslateWSAErrorToSocketError(code); + + return SocketState_NotConnected; + } + } + else if (ret == 0) + { + if (error) + { + if (msTimeout > 0) + *error = SocketError_TimedOut; + else + *error = SocketError_NoError; + } + + return SocketState_Connecting; + } + else + { + if (error) + *error = TranslateWSAErrorToSocketError(WSAGetLastError()); + + return SocketState_NotConnected; + } + + if (error) + *error = SocketError_NoError; + + return SocketState_Connected; + } bool SocketImpl::Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error) { diff --git a/src/Nazara/Network/Win32/SocketImpl.hpp b/src/Nazara/Network/Win32/SocketImpl.hpp index ef49cd14d..36ba874ee 100644 --- a/src/Nazara/Network/Win32/SocketImpl.hpp +++ b/src/Nazara/Network/Win32/SocketImpl.hpp @@ -40,7 +40,6 @@ namespace Nz static void Close(SocketHandle handle); static SocketState Connect(SocketHandle handle, const IpAddress& address, SocketError* error); - static SocketState Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error); static bool Initialize(); @@ -61,6 +60,7 @@ namespace Nz static std::size_t QuerySendBufferSize(SocketHandle handle, SocketError* error = nullptr); static unsigned int Poll(PollSocket* fdarray, std::size_t nfds, int timeout, SocketError* error); + static SocketState PollConnection(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error); static bool Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error); static bool ReceiveFrom(SocketHandle handle, void* buffer, int length, IpAddress* from, int* read, SocketError* error);