diff --git a/Code/Client/Private/NetImgui_Api.cpp b/Code/Client/Private/NetImgui_Api.cpp index bb0c659..7041dbd 100644 --- a/Code/Client/Private/NetImgui_Api.cpp +++ b/Code/Client/Private/NetImgui_Api.cpp @@ -143,7 +143,7 @@ bool IsConnected(void) // If disconnected in middle of a remote frame drawing, // want to behave like it is still connected to finish frame properly - return client.IsConnected() || IsDrawingRemote(); + return client.IsConnected() || IsDrawingRemote(); } //================================================================================================= diff --git a/Code/Client/Private/NetImgui_Client.cpp b/Code/Client/Private/NetImgui_Client.cpp index 9135bab..713ad59 100644 --- a/Code/Client/Private/NetImgui_Client.cpp +++ b/Code/Client/Private/NetImgui_Client.cpp @@ -359,6 +359,8 @@ bool Communications_Initialize(ClientInfo& client) { CmdVersion cmdVersionSend, cmdVersionRcv; PendingCom PendingRcv, PendingSend; + + client.mbComInitActive = true; //--------------------------------------------------------------------- // Handshake confirming connection validity @@ -391,30 +393,25 @@ bool Communications_Initialize(ClientInfo& client) //--------------------------------------------------------------------- // Connection established, init client //--------------------------------------------------------------------- - if( bCanConnect && !PendingSend.IsError() ) + if( bCanConnect && !PendingSend.IsError() && (!client.IsConnected() || bForceConnect) ) { - Network::SocketInfo* pNewConnect = client.mpSocketPending.exchange(nullptr); + Network::SocketInfo* pNewComSocket = client.mpSocketPending.exchange(nullptr); + + // If we detect an active connection with Server and 'ForceConnect was requested, close it first if( client.IsConnected() ) { - // Force connect requested, disconnect current client and wait until it is free to connect to new server - if( bForceConnect ){ - client.mbDisconnectPending = true; - while( client.IsConnected() ); - } - // Close communication with server, client isn't available to connect to it - else{ - NetImgui::Internal::Network::Disconnect(pNewConnect); - return false; - } + client.mbDisconnectPending = true; + while( client.IsConnected() ); } for(auto& texture : client.mTextures) { texture.mbSent = false; } + + client.mpSocketComs = pNewComSocket; // Take ownerhip of socket client.mbHasTextureUpdate = true; // Force sending the client textures client.mBGSettingSent.mTextureId = client.mBGSetting.mTextureId-1u; // Force sending the Background settings (by making different than current settings) - client.mpSocketComs = pNewConnect; client.mFrameIndex = 0; client.mPendingSendNext = 0; client.mServerForceConnectEnabled = (cmdVersionRcv.mFlags & static_cast(CmdVersion::eFlags::ConnectExclusive)) == 0; @@ -423,12 +420,14 @@ bool Communications_Initialize(ClientInfo& client) } // Disconnect pending socket if init failed - Network::SocketInfo* SocketPending = client.mpSocketPending.exchange(nullptr); + Network::SocketInfo* SocketPending = client.mpSocketPending.exchange(nullptr); + bool bValidConnection = SocketPending == nullptr; if( SocketPending ){ NetImgui::Internal::Network::Disconnect(SocketPending); - return false; } - return true; + + client.mbComInitActive = false; + return bValidConnection; } //================================================================================================= @@ -604,9 +603,9 @@ void ClientInfo::ContextOverride() } #endif #if IMGUI_VERSION_NUM < 19110 - newIO.GetClipboardTextFn = GetClipboardTextFn_NetImguiImpl; - newIO.SetClipboardTextFn = SetClipboardTextFn_NetImguiImpl; - newIO.ClipboardUserData = this; + newIO.GetClipboardTextFn = GetClipboardTextFn_NetImguiImpl; + newIO.SetClipboardTextFn = SetClipboardTextFn_NetImguiImpl; + newIO.ClipboardUserData = this; #else ImGuiPlatformIO& platformIO = ImGui::GetPlatformIO(); platformIO.Platform_GetClipboardTextFn = GetClipboardTextFn_NetImguiImpl; @@ -614,7 +613,7 @@ void ClientInfo::ContextOverride() platformIO.Platform_ClipboardUserData = this; #endif #if defined(IMGUI_HAS_VIEWPORT) - newIO.ConfigFlags &= ~(ImGuiConfigFlags_ViewportsEnable); // Viewport unsupported at the moment + newIO.ConfigFlags &= ~(ImGuiConfigFlags_ViewportsEnable); // Viewport unsupported at the moment #endif } } diff --git a/Code/Client/Private/NetImgui_Client.h b/Code/Client/Private/NetImgui_Client.h index c60d24d..f5621da 100644 --- a/Code/Client/Private/NetImgui_Client.h +++ b/Code/Client/Private/NetImgui_Client.h @@ -116,6 +116,7 @@ struct ClientInfo bool mbClientThreadActive = false; // True when connected and communicating with Server bool mbListenThreadActive = false; // True when listening from connection request from Server + bool mbComInitActive = false; // True when attempting to initialize a new connection bool mbHasTextureUpdate = false; bool mbIsDrawing = false; // We are inside a 'NetImgui::NewFrame' / 'NetImgui::EndFrame' (even if not for a remote draw) bool mbIsRemoteDrawing = false; // True if the rendering it meant for the remote netImgui server diff --git a/Code/Client/Private/NetImgui_Client.inl b/Code/Client/Private/NetImgui_Client.inl index 3c94395..1f03a23 100644 --- a/Code/Client/Private/NetImgui_Client.inl +++ b/Code/Client/Private/NetImgui_Client.inl @@ -22,7 +22,7 @@ bool ClientInfo::IsConnected()const bool ClientInfo::IsConnectPending()const { - return mpSocketPending.load() != nullptr || mpSocketListen.load() != nullptr; + return mbComInitActive || mpSocketPending.load() != nullptr || mpSocketListen.load() != nullptr; } bool ClientInfo::IsActive()const diff --git a/Code/Client/Private/NetImgui_NetworkPosix.cpp b/Code/Client/Private/NetImgui_NetworkPosix.cpp index 0e377f0..da51bdf 100644 --- a/Code/Client/Private/NetImgui_NetworkPosix.cpp +++ b/Code/Client/Private/NetImgui_NetworkPosix.cpp @@ -17,9 +17,6 @@ // -Sockets set to non blocking and immediate sending // -Added 'DataReceivePending' function // -Reworked 'DataReceive' and 'DataSend' to be non blocking socket operation -// but wait until all data has been processed -// -Connect now handle timeout instead of letting the socket timeout internally -// after a long time static_assert(0) namespace NetImgui { namespace Internal { namespace Network diff --git a/Code/Client/Private/NetImgui_NetworkUE4.cpp b/Code/Client/Private/NetImgui_NetworkUE4.cpp index c8e1141..4bf6dcb 100644 --- a/Code/Client/Private/NetImgui_NetworkUE4.cpp +++ b/Code/Client/Private/NetImgui_NetworkUE4.cpp @@ -187,7 +187,7 @@ void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& Pend else { const ESocketErrors SocketError = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode(); - if( SocketError != ESocketErrors::SE_EWOULDBLOCK ){ + if( (SocketError != ESocketErrors::SE_EWOULDBLOCK) || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected) ){ PendingComRcv.bError = true; } } @@ -217,7 +217,7 @@ void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& Pending else { const ESocketErrors SocketError = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode(); - if( SocketError != ESocketErrors::SE_EWOULDBLOCK ) + if( (SocketError != ESocketErrors::SE_EWOULDBLOCK) || (pClientSocket->mpSocket->GetConnectionState() != ESocketConnectionState::SCS_Connected)) { PendingComSend.bError = true; } diff --git a/Code/Client/Private/NetImgui_NetworkWin32.cpp b/Code/Client/Private/NetImgui_NetworkWin32.cpp index 7d8359b..083403e 100644 --- a/Code/Client/Private/NetImgui_NetworkWin32.cpp +++ b/Code/Client/Private/NetImgui_NetworkWin32.cpp @@ -27,14 +27,15 @@ struct SocketInfo constexpr DWORD kComsNoDelay = 1; setsockopt(mSocket, SOL_SOCKET, TCP_NODELAY, reinterpret_cast(&kComsNoDelay), sizeof(kComsNoDelay)); - //constexpr int kComsSendBuffer = 1014*1024; - //setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&kComsSendBuffer), sizeof(kComsSendBuffer)); + const int kComsSendBuffer = 2*mSendSizeMax; + setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&kComsSendBuffer), sizeof(kComsSendBuffer)); //constexpr int kComsRcvBuffer = 1014*1024; //setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&kComsRcvBuffer), sizeof(kComsRcvBuffer)); } SOCKET mSocket; + int mSendSizeMax = 1024*1024; // Limit tx data to avoid socket error on large amount (texture) }; bool Startup() @@ -83,7 +84,7 @@ SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort) FD_ZERO(&SocketSet); FD_SET(ClientSocket, &SocketSet); Result = select(0, nullptr, &SocketSet, nullptr, &kConnectTimeout); - Connected = Result != SOCKET_ERROR; + Connected = Result == 1; // when 1 socket ready for write, otherwise it's -1 or 0 } if( Connected ) @@ -166,10 +167,21 @@ void Disconnect(SocketInfo* pClientSocket) //================================================================================================= bool DataReceivePending(SocketInfo* pClientSocket) { - char Unused[4]; - int resultRcv = recv(pClientSocket->mSocket, Unused, 1, MSG_PEEK); - // Note: return true on a connection error, to exit code looping on the data wait - return !pClientSocket || resultRcv > 0 || (resultRcv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK); + const timeval kConnectTimeout = {0, 0}; // No wait time + if( pClientSocket ) + { + fd_set fdSetRead; + fd_set fdSetErr; + FD_ZERO(&fdSetRead); + FD_ZERO(&fdSetErr); + FD_SET(pClientSocket->mSocket, &fdSetRead); + FD_SET(pClientSocket->mSocket, &fdSetErr); + + // Note: return true if data ready or connection error (to exit parent loop waiting on data) + int result = select(0, &fdSetRead, nullptr, &fdSetErr, &kConnectTimeout); + return result != 0; + } + return true; } //================================================================================================= @@ -188,7 +200,9 @@ void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& Pend static_cast(PendingComRcv.pCommand->mSize-PendingComRcv.SizeCurrent), 0); - if( resultRcv != SOCKET_ERROR ){ + // Note: 'DataReceive' is called after pending data has been confirm. + // 0 received data means connection lost + if( resultRcv != SOCKET_ERROR && resultRcv > 0 ){ PendingComRcv.SizeCurrent += static_cast(resultRcv); } // Connection error, abort transmission @@ -208,9 +222,11 @@ void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& Pending } // Send data to remote connection - int resultSent = send( pClientSocket->mSocket, + int sizeToSend = static_cast(PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent); + sizeToSend = sizeToSend > pClientSocket->mSendSizeMax ? pClientSocket->mSendSizeMax : sizeToSend; + int resultSent = send( pClientSocket->mSocket, &reinterpret_cast(PendingComSend.pCommand)[PendingComSend.SizeCurrent], - static_cast(PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent), + sizeToSend, 0); if( resultSent != SOCKET_ERROR ){ diff --git a/Code/ServerApp/Source/NetImguiServer_Network.cpp b/Code/ServerApp/Source/NetImguiServer_Network.cpp index 74c9dc7..88b8099 100644 --- a/Code/ServerApp/Source/NetImguiServer_Network.cpp +++ b/Code/ServerApp/Source/NetImguiServer_Network.cpp @@ -224,7 +224,6 @@ void Communications_ClientExchangeLoop(RemoteClient::Client* pClient) NetImguiServer::Config::Client::SetProperty_Status(pClient->mClientConfigID, NetImguiServer::Config::Client::eStatus::Disconnected); NetImgui::Internal::Network::Disconnect(pClient->mpSocket); pClient->Release(); - pClient->mbIsConnected = false; gActiveClientThreadCount--; } @@ -376,8 +375,9 @@ void NetworkConnectRequest_Receive() NetImguiServer::App::HAL_GetSocketInfo(pClientSocket, newClient.mConnectHost, sizeof(newClient.mConnectHost), newClient.mConnectPort); NetworkConnectionNew(pClientSocket, &newClient, false); } - else + else{ NetImgui::Internal::Network::Disconnect(pClientSocket); + } } } std::this_thread::sleep_for(std::chrono::milliseconds(16)); diff --git a/Code/ServerApp/Source/NetImguiServer_RemoteClient.h b/Code/ServerApp/Source/NetImguiServer_RemoteClient.h index b70c8e8..84027ca 100644 --- a/Code/ServerApp/Source/NetImguiServer_RemoteClient.h +++ b/Code/ServerApp/Source/NetImguiServer_RemoteClient.h @@ -91,7 +91,7 @@ struct Client bool mbIsVisible = false; //!< If currently shown bool mbIsActive = false; //!< Is the current active window (will receive input, only one is true at a time) bool mbIsReleased = false; //!< If released in com thread and main thread should delete resources - bool mbIsConnected = false; //!< If connected to a remote client + bool mbIsConnected = false; //!< If connected to a remote client. Set to false in Unitialize, after mIsRelease is set to unload resources std::atomic_bool mbIsFree; //!< If available to use for a new connected client std::atomic_bool mbCompressionSkipOncePending; //!< When we detect invalid previous DrawFrame command, cancel compression for 1 frame, to get good data std::atomic_bool mbDisconnectPending; //!< Terminate Client/Server coms