From ec7343d6e86ddac5067f3ce5c3040b0b9069f7b5 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 21 Jan 2025 15:00:05 +0000 Subject: [PATCH 01/41] Add --enabletcp option for server --- src/main.cpp | 13 +++++++++++++ src/server.cpp | 2 ++ src/server.h | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index a034218f8a..760894b369 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,6 +97,7 @@ int main ( int argc, char** argv ) bool bUseTranslation = true; bool bCustomPortNumberGiven = false; bool bEnableIPv6 = false; + bool bEnableTcp = false; int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS; quint16 iPortNumber = DEFAULT_PORT_NUMBER; int iJsonRpcPortNumber = INVALID_PORT; @@ -252,6 +253,16 @@ int main ( int argc, char** argv ) // Server only: + // Enable TCP server --------------------------------------------------- + if ( GetFlagArgument ( argv, i, "--enabletcp", "--enabletcp" ) ) + { + bEnableTcp = true; + qInfo() << "- TCP server enabled"; + CommandLineOptions << "--enabletcp"; + ServerOnlyOptions << "--enabletcp"; + continue; + } + // Disconnect all clients on quit -------------------------------------- if ( GetFlagArgument ( argv, i, "-d", "--discononquit" ) ) { @@ -1008,6 +1019,7 @@ int main ( int argc, char** argv ) bDisableRecording, bDelayPan, bEnableIPv6, + bEnableTcp, eLicenceType ); #ifndef NO_JSON_RPC @@ -1133,6 +1145,7 @@ QString UsageArguments ( char** argv ) " --noraw disable raw audio\n" " -s, --server start Server\n" " --serverbindip IP address the Server will bind to (rather than all)\n" + " --enabletcp enable TCP server for Jamulus protocol\n" " -T, --multithreading use multithreading to make better use of\n" " multi-core CPUs and support more Clients\n" " -u, --numchannels maximum number of channels\n" diff --git a/src/server.cpp b/src/server.cpp index d6cd2bf0cc..470c4dcdfc 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -45,6 +45,7 @@ CServer::CServer ( const int iNewMaxNumChan, const bool bDisableRecording, const bool bNDelayPan, const bool bNEnableIPv6, + const bool bNEnableTcp, const ELicenceType eNLicenceType ) : bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ), bUseMultithreading ( bNUseMultithreading ), @@ -71,6 +72,7 @@ CServer::CServer ( const int iNewMaxNumChan, bAutoRunMinimized ( false ), bDelayPan ( bNDelayPan ), bEnableIPv6 ( bNEnableIPv6 ), + bEnableTcp ( bNEnableTcp ), eLicenceType ( eNLicenceType ), bDisconnectAllClientsOnQuit ( bNDisconnectAllClientsOnQuit ), pSignalHandler ( CSignalHandler::getSingletonP() ) diff --git a/src/server.h b/src/server.h index 6a42a8f9b8..6317d8720c 100644 --- a/src/server.h +++ b/src/server.h @@ -105,6 +105,7 @@ class CServer : public QObject, public CServerSlots const bool bDisableRecording, const bool bNDelayPan, const bool bNEnableIPv6, + const bool bNEnableTcp, const ELicenceType eNLicenceType ); virtual ~CServer(); @@ -303,6 +304,9 @@ class CServer : public QObject, public CServerSlots // enable IPv6 bool bEnableIPv6; + // enable TCP Server + bool bEnableTcp; + // messaging QString strWelcomeMessage; ELicenceType eLicenceType; From 92f4420d9f60f44b1adcf54a0274e1152dcde488 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 22 Mar 2024 23:52:50 +0000 Subject: [PATCH 02/41] Initial skeleton for TCP protocol server --- Jamulus.pro | 2 + src/protocol.cpp | 10 +++ src/protocol.h | 2 + src/server.cpp | 5 ++ src/server.h | 2 + src/tcpserver.cpp | 178 ++++++++++++++++++++++++++++++++++++++++++++++ src/tcpserver.h | 78 ++++++++++++++++++++ 7 files changed, 277 insertions(+) create mode 100644 src/tcpserver.cpp create mode 100644 src/tcpserver.h diff --git a/Jamulus.pro b/Jamulus.pro index 421a7c6218..9ce4fd04da 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -399,6 +399,7 @@ HEADERS += src/plugins/audioreverb.h \ src/serverlogging.h \ src/settings.h \ src/socket.h \ + src/tcpserver.h \ src/util.h \ src/recorder/jamrecorder.h \ src/recorder/creaperproject.h \ @@ -507,6 +508,7 @@ SOURCES += src/plugins/audioreverb.cpp \ src/settings.cpp \ src/signalhandler.cpp \ src/socket.cpp \ + src/tcpserver.cpp \ src/util.cpp \ src/recorder/jamrecorder.cpp \ src/recorder/creaperproject.cpp \ diff --git a/src/protocol.cpp b/src/protocol.cpp index a30534bc72..a0a4fc58e4 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2601,6 +2601,16 @@ bool CProtocol::EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, con /******************************************************************************\ * Message generation and parsing * \******************************************************************************/ +int CProtocol::GetBodyLength ( const CVector& vecbyData ) +{ + int iCurPos = 5; // position of length calculation + + // 2 bytes length + const int iLenBy = static_cast ( GetValFromStream ( vecbyData, iCurPos, 2 ) ); + + return iLenBy + 2; // remaining length to read, including CRC +} + bool CProtocol::ParseMessageFrame ( const CVector& vecbyData, const int iNumBytesIn, CVector& vecbyMesBodyData, diff --git a/src/protocol.h b/src/protocol.h index 03ac9f328f..dfc8a4984b 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -156,6 +156,8 @@ class CProtocol : public QObject void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); + static int GetBodyLength ( const CVector& vecbyData ); + static bool ParseMessageFrame ( const CVector& vecbyData, const int iNumBytesIn, CVector& vecbyMesBodyData, diff --git a/src/server.cpp b/src/server.cpp index 470c4dcdfc..e839c1039b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -53,6 +53,7 @@ CServer::CServer ( const int iNewMaxNumChan, iCurNumChannels ( 0 ), bDisableRaw ( bNDisableRaw ), Socket ( this, iPortNumber, iQosNumber, strServerBindIP, bNEnableIPv6 ), + TcpServer ( this, strServerBindIP, iPortNumber, bNEnableIPv6 ), Logging(), iFrameCount ( 0 ), bWriteStatusHTMLFile ( false ), @@ -308,6 +309,10 @@ CServer::CServer ( const int iNewMaxNumChan, // start the socket (it is important to start the socket after all // initializations and connections) Socket.Start(); + if ( bEnableTcp ) + { + TcpServer.Start(); + } } template diff --git a/src/server.h b/src/server.h index 6317d8720c..fc69d1a37a 100644 --- a/src/server.h +++ b/src/server.h @@ -42,6 +42,7 @@ #include "util.h" #include "serverlogging.h" #include "serverlist.h" +#include "tcpserver.h" #include "recorder/jamcontroller.h" #include "threadpool.h" @@ -275,6 +276,7 @@ class CServer : public QObject, public CServerSlots // actual working objects CHighPrioSocket Socket; + CTcpServer TcpServer; // logging CServerLogging Logging; diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp new file mode 100644 index 0000000000..a165abb942 --- /dev/null +++ b/src/tcpserver.cpp @@ -0,0 +1,178 @@ +/******************************************************************************\ + * Copyright (c) 2024 + * + * Author(s): + * Tony Mountifield + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + \******************************************************************************/ + +#include "tcpserver.h" + +#include "server.h" +#include "channel.h" + +CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ) : + pServer ( pNServP ), + strServerBindIP ( strServerBindIP ), + iPort ( iPort ), + bEnableIPv6 ( bEnableIPv6 ), + pTcpServer ( new QTcpServer ( this ) ) +{ + //// connect ( this, &CTcpServer::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); + connect ( pTcpServer, &QTcpServer::newConnection, this, &CTcpServer::OnNewConnection ); +} + +CTcpServer::~CTcpServer() +{ + if ( pTcpServer->isListening() ) + { + qInfo() << "- stopping Jamulus-TCP server"; + pTcpServer->close(); + } +} + +bool CTcpServer::Start() +{ + if ( iPort < 0 ) + { + return false; + } + + // default to any-address for either both IP protocols or just IPv4 + QHostAddress hostAddress = bEnableIPv6 ? QHostAddress::Any : QHostAddress::AnyIPv4; + + if ( !bEnableIPv6 ) + { + if ( !strServerBindIP.isEmpty() ) + { + hostAddress = QHostAddress ( strServerBindIP ); + } + } + + if ( pTcpServer->listen ( hostAddress, iPort ) ) + { + qInfo() << qUtf8Printable ( + QString ( "- Jamulus-TCP: Server started on %1:%2" ).arg ( pTcpServer->serverAddress().toString() ).arg ( pTcpServer->serverPort() ) ); + return true; + } + qInfo() << "- Jamulus-TCP: Unable to start server:" << pTcpServer->errorString(); + return false; +} + +void CTcpServer::OnNewConnection() +{ + QTcpSocket* pSocket = pTcpServer->nextPendingConnection(); + if ( !pSocket ) + { + return; + } + + // express IPv4 address as IPv4 + CHostAddress peerAddress ( pSocket->peerAddress(), pSocket->peerPort() ); + + if ( peerAddress.InetAddr.protocol() == QAbstractSocket::IPv6Protocol ) + { + bool ok; + quint32 ip4 = peerAddress.InetAddr.toIPv4Address ( &ok ); + if ( ok ) + { + peerAddress.InetAddr.setAddress ( ip4 ); + } + } + + CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, peerAddress ); + + qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.InetAddr.toString(); + + // allocate memory for network receive and send buffer in samples + CVector vecbyRecBuf; + vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); + + connect ( pSocket, &QTcpSocket::disconnected, [this, pTcpConnection]() { + qDebug() << "- Jamulus-TCP: connection from:" << pTcpConnection->tcpAddress.InetAddr.toString() << "closed"; + pTcpConnection->pTcpSocket->deleteLater(); + delete pTcpConnection; + } ); + + connect ( pSocket, &QTcpSocket::readyRead, [this, pTcpConnection, vecbyRecBuf]() { + // handle received Jamulus protocol packet + + // check if this is a protocol message + int iRecCounter; + int iRecID; + CVector vecbyMesBodyData; + + long iNumBytesRead = pTcpConnection->pTcpSocket->read ( (char*) &vecbyRecBuf[0], MESS_HEADER_LENGTH_BYTE ); + if ( iNumBytesRead == -1 ) + { + return; + } + + if ( iNumBytesRead < MESS_HEADER_LENGTH_BYTE ) + { + qDebug() << "-- short read: expected" << MESS_HEADER_LENGTH_BYTE << "bytes, got" << iNumBytesRead; + return; + } + + long iPayloadLength = CProtocol::GetBodyLength ( vecbyRecBuf ); + + long iNumBytesRead2 = pTcpConnection->pTcpSocket->read ( (char*) &vecbyRecBuf[MESS_HEADER_LENGTH_BYTE], iPayloadLength ); + if ( iNumBytesRead2 == -1 ) + { + return; + } + + if ( iNumBytesRead2 < iPayloadLength ) + { + qDebug() << "-- short read: expected" << iPayloadLength << "bytes, got" << iNumBytesRead2; + return; + } + + iNumBytesRead += iNumBytesRead2; + + qDebug() << "- Jamulus-TCP: received protocol message of length" << iNumBytesRead; + + if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, iNumBytesRead, vecbyMesBodyData, iRecCounter, iRecID ) ) + { + qDebug() << "- Jamulus-TCP: message parsed OK, ID =" << iRecID; + + // this is a protocol message, check the type of the message + if ( CProtocol::IsConnectionLessMessageID ( iRecID ) ) + { + //### TODO: BEGIN ###// + // a copy of the vector is used -> avoid malloc in real-time routine + //// emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, peerAddress, pSocket ); + //### TODO: END ###// + } + else + { + //### TODO: BEGIN ###// + // a copy of the vector is used -> avoid malloc in real-time routine + // emit ProtocolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, peerAddress, pSocket ); + //### TODO: END ###// + } + } + } ); +} + +#if 0 +void CTcpServer::Send ( QTcpSocket* pSocket ) { + // pSocket->write ( ); +} +#endif diff --git a/src/tcpserver.h b/src/tcpserver.h new file mode 100644 index 0000000000..2d57eb7a8a --- /dev/null +++ b/src/tcpserver.h @@ -0,0 +1,78 @@ +/******************************************************************************\ + * Copyright (c) 2024 + * + * Author(s): + * Tony Mountifield + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "global.h" +#include "protocol.h" +#include "util.h" + +// The header files channel.h and server.h require to include this header file +// so we get a cyclic dependency. To solve this issue, a prototype of the +// channel class and server class is defined here. +class CServer; // forward declaration of CServer +class CChannel; // forward declaration of CChannel + +/* Classes ********************************************************************/ +class CTcpServer : public QObject +{ + Q_OBJECT + +public: + CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ); + virtual ~CTcpServer(); + + bool Start(); + +private: + CServer* pServer; // for server + const QString strServerBindIP; + const int iPort; + const bool bEnableIPv6; + QTcpServer* pTcpServer; + +signals: + void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, QTcpSocket* pTcpSocket ); + +protected slots: + void OnNewConnection(); +}; + +class CTcpConnection +{ +public: + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ) {} + ~CTcpConnection() {} + + QTcpSocket* pTcpSocket; + CHostAddress tcpAddress; + CHostAddress udpAddress; +}; From 6de67b377d90005f37e79a825051b68d7b49d9e9 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 25 Mar 2024 17:16:21 +0000 Subject: [PATCH 03/41] Add handling of CL msgs for Server and client list --- src/channel.h | 12 ++++++++---- src/client.cpp | 16 ++++++++++++---- src/client.h | 4 ++-- src/protocol.cpp | 32 +++++++++++++++++++------------- src/protocol.h | 25 ++++++++++++++++--------- src/server.cpp | 16 ++++++++++++---- src/server.h | 11 +++++++---- src/serverlist.cpp | 10 +++++++--- src/serverlist.h | 2 +- src/socket.h | 3 ++- src/tcpserver.cpp | 7 ++++--- src/tcpserver.h | 8 ++++---- src/testbench.h | 4 ++-- 13 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/channel.h b/src/channel.h index 096a88c9e9..e952909b93 100644 --- a/src/channel.h +++ b/src/channel.h @@ -86,6 +86,9 @@ class CChannel : public QObject void SetAddress ( const CHostAddress& NAddr ) { InetAddr = NAddr; } const CHostAddress& GetAddress() const { return InetAddr; } + void SetTcpConnection ( CTcpConnection* pConnection ) { pTcpConnection = pConnection; } + CTcpConnection* GetTcpConnection() { return pTcpConnection; } + void ResetInfo() { bIsIdentified = false; @@ -187,7 +190,8 @@ class CChannel : public QObject } // connection parameters - CHostAddress InetAddr; + CHostAddress InetAddr; + CTcpConnection* pTcpConnection; // channel info CChannelCoreInfo ChannelInfo; @@ -256,9 +260,9 @@ public slots: PutProtocolData ( iRecCounter, iRecID, vecbyMesBodyData, RecHostAddr ); } - void OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr ) + void OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ) { - emit DetectedCLMessage ( vecbyMesBodyData, iRecID, RecHostAddr ); + emit DetectedCLMessage ( vecbyMesBodyData, iRecID, RecHostAddr, pTcpConnection ); } void OnNewConnection() { emit NewConnection(); } @@ -284,7 +288,7 @@ public slots: void RecorderStateReceived ( ERecorderState eRecorderState ); void Disconnected(); - void DetectedCLMessage ( CVector vecbyMesBodyData, int iRecID, CHostAddress RecHostAddr ); + void DetectedCLMessage ( CVector vecbyMesBodyData, int iRecID, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ); void ParseMessageBody ( CVector vecbyMesBodyData, int iRecCounter, int iRecID ); }; diff --git a/src/client.cpp b/src/client.cpp index 13eac289b6..f6da70521e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -252,11 +252,19 @@ void CClient::OnSendProtMessage ( CVector vecMessage ) Socket.SendPacket ( vecMessage, Channel.GetAddress() ); } -void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage ) +void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ) { // the protocol queries me to call the function to send the message // send it through the network - Socket.SendPacket ( vecMessage, InetAddr ); + if ( pTcpConnection ) + { + // send to the connected socket directly + pTcpConnection->pTcpSocket->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); + } + else + { + Socket.SendPacket ( vecMessage, InetAddr ); + } } void CClient::OnInvalidPacketReceived ( CHostAddress RecHostAddr ) @@ -271,10 +279,10 @@ void CClient::OnInvalidPacketReceived ( CHostAddress RecHostAddr ) } } -void CClient::OnDetectedCLMessage ( CVector vecbyMesBodyData, int iRecID, CHostAddress RecHostAddr ) +void CClient::OnDetectedCLMessage ( CVector vecbyMesBodyData, int iRecID, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ) { // connection less messages are always processed - ConnLessProtocol.ParseConnectionLessMessageBody ( vecbyMesBodyData, iRecID, RecHostAddr ); + ConnLessProtocol.ParseConnectionLessMessageBody ( vecbyMesBodyData, iRecID, RecHostAddr, pTcpConnection ); } void CClient::OnJittBufSizeChanged ( int iNewJitBufSize ) diff --git a/src/client.h b/src/client.h index c71fc414ab..d82a60917d 100644 --- a/src/client.h +++ b/src/client.h @@ -428,7 +428,7 @@ protected slots: void OnSendProtMessage ( CVector vecMessage ); void OnInvalidPacketReceived ( CHostAddress RecHostAddr ); - void OnDetectedCLMessage ( CVector vecbyMesBodyData, int iRecID, CHostAddress RecHostAddr ); + void OnDetectedCLMessage ( CVector vecbyMesBodyData, int iRecID, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ); void OnReqJittBufSize() { CreateServerJitterBufferMessage(); } void OnJittBufSizeChanged ( int iNewJitBufSize ); @@ -443,7 +443,7 @@ protected slots: } void OnCLPingReceived ( CHostAddress InetAddr, int iMs ); - void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage ); + void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ); void OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, int iMs, int iNumClients ); diff --git a/src/protocol.cpp b/src/protocol.cpp index a0a4fc58e4..a13af0c3a6 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -610,7 +610,10 @@ void CProtocol::CreateAndImmSendAcknMess ( const int& iID, const int& iCnt ) emit MessReadyForSending ( vecAcknMessage ); } -void CProtocol::CreateAndImmSendConLessMessage ( const int iID, const CVector& vecData, const CHostAddress& InetAddr ) +void CProtocol::CreateAndImmSendConLessMessage ( const int iID, + const CVector& vecData, + const CHostAddress& InetAddr, + CTcpConnection* pTcpConnection ) { CVector vecNewMessage; @@ -619,7 +622,7 @@ void CProtocol::CreateAndImmSendConLessMessage ( const int iID, const CVector& vecbyMesBodyData, const int iRecCounter, const int iRecID ) @@ -847,7 +850,10 @@ void CProtocol::ParseMessageBody ( const CVector& vecbyMesBodyData, con } } -void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, const int iRecID, const CHostAddress& InetAddr ) +void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, + const int iRecID, + const CHostAddress& InetAddr, + CTcpConnection* pTcpConnection ) { //### TEST: BEGIN ###// // Test channel implementation: randomly delete protocol messages (50 % loss) @@ -879,7 +885,7 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe break; case PROTMESSID_CLM_REQ_SERVER_LIST: - EvaluateCLReqServerListMes ( InetAddr ); + EvaluateCLReqServerListMes ( InetAddr, pTcpConnection ); break; case PROTMESSID_CLM_SEND_EMPTY_MESSAGE: @@ -915,7 +921,7 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe break; case PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST: - EvaluateCLReqConnClientsListMes ( InetAddr ); + EvaluateCLReqConnClientsListMes ( InetAddr, pTcpConnection ); break; case PROTMESSID_CLM_CHANNEL_LEVEL_LIST: @@ -2024,7 +2030,7 @@ bool CProtocol::EvaluateCLUnregisterServerMes ( const CHostAddress& InetAddr ) return false; // no error } -void CProtocol::CreateCLServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo ) +void CProtocol::CreateCLServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo, CTcpConnection* pTcpConnection ) { const int iNumServers = vecServerInfo.Size(); @@ -2079,7 +2085,7 @@ void CProtocol::CreateCLServerListMes ( const CHostAddress& InetAddr, const CVec PutStringUTF8OnStream ( vecData, iPos, strUTF8City ); } - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_SERVER_LIST, vecData, InetAddr ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_SERVER_LIST, vecData, InetAddr, pTcpConnection ); } bool CProtocol::EvaluateCLServerListMes ( const CHostAddress& InetAddr, const CVector& vecData ) @@ -2244,10 +2250,10 @@ void CProtocol::CreateCLReqServerListMes ( const CHostAddress& InetAddr ) CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_SERVER_LIST, CVector ( 0 ), InetAddr ); } -bool CProtocol::EvaluateCLReqServerListMes ( const CHostAddress& InetAddr ) +bool CProtocol::EvaluateCLReqServerListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) { // invoke message action - emit CLReqServerList ( InetAddr ); + emit CLReqServerList ( InetAddr, pTcpConnection ); return false; // no error } @@ -2382,7 +2388,7 @@ bool CProtocol::EvaluateCLReqVersionAndOSMes ( const CHostAddress& InetAddr ) return false; // no error } -void CProtocol::CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecChanInfo ) +void CProtocol::CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecChanInfo, CTcpConnection* pTcpConnection ) { const int iNumClients = vecChanInfo.Size(); @@ -2430,7 +2436,7 @@ void CProtocol::CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const PutStringUTF8OnStream ( vecData, iPos, strUTF8City ); } - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_CONN_CLIENTS_LIST, vecData, InetAddr ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_CONN_CLIENTS_LIST, vecData, InetAddr, pTcpConnection ); } bool CProtocol::EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData ) @@ -2497,10 +2503,10 @@ void CProtocol::CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr ) CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST, CVector ( 0 ), InetAddr ); } -bool CProtocol::EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr ) +bool CProtocol::EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) { // invoke message action - emit CLReqConnClientsList ( InetAddr ); + emit CLReqConnClientsList ( InetAddr, pTcpConnection ); return false; // no error } diff --git a/src/protocol.h b/src/protocol.h index dfc8a4984b..3e3a323202 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -31,6 +31,7 @@ #include #include "global.h" #include "util.h" +#include "tcpserver.h" /* Definitions ****************************************************************/ // protocol message IDs @@ -143,7 +144,7 @@ class CProtocol : public QObject void CreateCLRegisterServerMes ( const CHostAddress& InetAddr, const CHostAddress& LInetAddr, const CServerCoreInfo& ServerInfo ); void CreateCLRegisterServerExMes ( const CHostAddress& InetAddr, const CHostAddress& LInetAddr, const CServerCoreInfo& ServerInfo ); void CreateCLUnregisterServerMes ( const CHostAddress& InetAddr ); - void CreateCLServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo ); + void CreateCLServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo, CTcpConnection* pTcpConnection ); void CreateCLRedServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo ); void CreateCLReqServerListMes ( const CHostAddress& InetAddr ); void CreateCLSendEmptyMesMes ( const CHostAddress& InetAddr, const CHostAddress& TargetInetAddr ); @@ -151,7 +152,7 @@ class CProtocol : public QObject void CreateCLDisconnection ( const CHostAddress& InetAddr ); void CreateCLVersionAndOSMes ( const CHostAddress& InetAddr ); void CreateCLReqVersionAndOSMes ( const CHostAddress& InetAddr ); - void CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecChanInfo ); + void CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecChanInfo, CTcpConnection* pTcpConnection ); void CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr ); void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); @@ -166,7 +167,10 @@ class CProtocol : public QObject void ParseMessageBody ( const CVector& vecbyMesBodyData, const int iRecCounter, const int iRecID ); - void ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, const int iRecID, const CHostAddress& InetAddr ); + void ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, + const int iRecID, + const CHostAddress& InetAddr, + CTcpConnection* pTcpConnection ); static bool IsConnectionLessMessageID ( const int iID ) { return ( iID >= 1000 ) && ( iID < 2000 ); } @@ -245,7 +249,10 @@ class CProtocol : public QObject void CreateAndSendMessage ( const int iID, const CVector& vecData ); - void CreateAndImmSendConLessMessage ( const int iID, const CVector& vecData, const CHostAddress& InetAddr ); + void CreateAndImmSendConLessMessage ( const int iID, + const CVector& vecData, + const CHostAddress& InetAddr, + CTcpConnection* pTcpConnection = nullptr ); bool EvaluateJitBufMes ( const CVector& vecData ); bool EvaluateReqJitBufMes(); @@ -275,13 +282,13 @@ class CProtocol : public QObject bool EvaluateCLUnregisterServerMes ( const CHostAddress& InetAddr ); bool EvaluateCLServerListMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLRedServerListMes ( const CHostAddress& InetAddr, const CVector& vecData ); - bool EvaluateCLReqServerListMes ( const CHostAddress& InetAddr ); + bool EvaluateCLReqServerListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ); bool EvaluateCLSendEmptyMesMes ( const CVector& vecData ); bool EvaluateCLDisconnectionMes ( const CHostAddress& InetAddr ); bool EvaluateCLVersionAndOSMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLReqVersionAndOSMes ( const CHostAddress& InetAddr ); bool EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData ); - bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr ); + bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ); bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, const CVector& vecData ); @@ -306,7 +313,7 @@ public slots: signals: // transmitting void MessReadyForSending ( CVector vecMessage ); - void CLMessReadyForSending ( CHostAddress InetAddr, CVector vecMessage ); + void CLMessReadyForSending ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ); // receiving void ChangeJittBufSize ( int iNewJitBufSize ); @@ -342,13 +349,13 @@ public slots: void CLUnregisterServerReceived ( CHostAddress InetAddr ); void CLServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ); void CLRedServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ); - void CLReqServerList ( CHostAddress InetAddr ); + void CLReqServerList ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ); void CLSendEmptyMes ( CHostAddress TargetInetAddr ); void CLDisconnection ( CHostAddress InetAddr ); void CLVersionAndOSReceived ( CHostAddress InetAddr, COSUtil::EOpSystemType eOSType, QString strVersion ); void CLReqVersionAndOS ( CHostAddress InetAddr ); void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); - void CLReqConnClientsList ( CHostAddress InetAddr ); + void CLReqConnClientsList ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ); void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); }; diff --git a/src/server.cpp b/src/server.cpp index e839c1039b..44e0cf3c86 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -476,11 +476,19 @@ void CServer::OnServerFull ( CHostAddress RecHostAddr ) ConnLessProtocol.CreateCLServerFullMes ( RecHostAddr ); } -void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage ) +void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ) { // the protocol queries me to call the function to send the message // send it through the network - Socket.SendPacket ( vecMessage, InetAddr ); + if ( pTcpConnection ) + { + // send to the connected socket directly + pTcpConnection->pTcpSocket->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); + } + else + { + Socket.SendPacket ( vecMessage, InetAddr ); + } } void CServer::OnCLDisconnection ( CHostAddress InetAddr ) @@ -1477,12 +1485,12 @@ void CServer::DumpChannels ( const QString& title ) } } -void CServer::OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr ) +void CServer::OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ) { QMutexLocker locker ( &Mutex ); // connection less messages are always processed - ConnLessProtocol.ParseConnectionLessMessageBody ( vecbyMesBodyData, iRecID, RecHostAddr ); + ConnLessProtocol.ParseConnectionLessMessageBody ( vecbyMesBodyData, iRecID, RecHostAddr, pTcpConnection ); } void CServer::OnProtocolMessageReceived ( int iRecCounter, int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr ) diff --git a/src/server.h b/src/server.h index fc69d1a37a..155781a264 100644 --- a/src/server.h +++ b/src/server.h @@ -344,9 +344,9 @@ public slots: void OnServerFull ( CHostAddress RecHostAddr ); - void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage ); + void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ); - void OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr ); + void OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ); void OnProtocolMessageReceived ( int iRecCounter, int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr ); @@ -366,11 +366,14 @@ public slots: } } - void OnCLReqServerList ( CHostAddress InetAddr ) { ServerListManager.RetrieveAll ( InetAddr ); } + void OnCLReqServerList ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ) { ServerListManager.RetrieveAll ( InetAddr, pTcpConnection ); } void OnCLReqVersionAndOS ( CHostAddress InetAddr ) { ConnLessProtocol.CreateCLVersionAndOSMes ( InetAddr ); } - void OnCLReqConnClientsList ( CHostAddress InetAddr ) { ConnLessProtocol.CreateCLConnClientsListMes ( InetAddr, CreateChannelList() ); } + void OnCLReqConnClientsList ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ) + { + ConnLessProtocol.CreateCLConnClientsListMes ( InetAddr, CreateChannelList(), pTcpConnection ); + } void OnCLRegisterServerReceived ( CHostAddress InetAddr, CHostAddress LInetAddr, CServerCoreInfo ServerInfo ) { diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 4dabe4c7ef..582513045c 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -663,7 +663,7 @@ void CServerListManager::Remove ( const CHostAddress& InetAddr ) and allow the client connect dialogue instead to use the IP and Port from which the list was received. */ -void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) +void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) { QMutexLocker locker ( &Mutex ); @@ -722,8 +722,12 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) // send the server list to the client, since we do not know that the client // has a UDP fragmentation issue, we send both lists, the reduced and the // normal list after each other - pConnLessProtocol->CreateCLRedServerListMes ( InetAddr, vecServerInfo ); - pConnLessProtocol->CreateCLServerListMes ( InetAddr, vecServerInfo ); + if ( !pTcpConnection ) + { + // no need for reduced list if on TCP + pConnLessProtocol->CreateCLRedServerListMes ( InetAddr, vecServerInfo ); + } + pConnLessProtocol->CreateCLServerListMes ( InetAddr, vecServerInfo, pTcpConnection ); } } diff --git a/src/serverlist.h b/src/serverlist.h index cdf1d0f7e6..8edb843123 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -168,7 +168,7 @@ class CServerListManager : public QObject void Append ( const CHostAddress& InetAddr, const CHostAddress& LInetAddr, const CServerCoreInfo& ServerInfo, const QString strVersion = "" ); void Remove ( const CHostAddress& InetAddr ); - void RetrieveAll ( const CHostAddress& InetAddr ); + void RetrieveAll ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ); void StoreRegistrationResult ( ESvrRegResult eStatus ); diff --git a/src/socket.h b/src/socket.h index 77fdf24452..a8919fcc01 100644 --- a/src/socket.h +++ b/src/socket.h @@ -31,6 +31,7 @@ #include "global.h" #include "protocol.h" #include "util.h" +#include "tcpserver.h" #ifndef _WIN32 # include # include @@ -103,7 +104,7 @@ class CSocket : public QObject void ProtocolMessageReceived ( int iRecCounter, int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr ); - void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr ); + void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection = nullptr ); }; /* Socket which runs in a separate high priority thread --------------------- */ diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index a165abb942..2a461b129d 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -24,6 +24,7 @@ #include "tcpserver.h" +#include "protocol.h" #include "server.h" #include "channel.h" @@ -34,7 +35,7 @@ CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int i bEnableIPv6 ( bEnableIPv6 ), pTcpServer ( new QTcpServer ( this ) ) { - //// connect ( this, &CTcpServer::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); + connect ( this, &CTcpServer::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); connect ( pTcpServer, &QTcpServer::newConnection, this, &CTcpServer::OnNewConnection ); } @@ -157,14 +158,14 @@ void CTcpServer::OnNewConnection() { //### TODO: BEGIN ###// // a copy of the vector is used -> avoid malloc in real-time routine - //// emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, peerAddress, pSocket ); + emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, pTcpConnection->tcpAddress, pTcpConnection ); //### TODO: END ###// } else { //### TODO: BEGIN ###// // a copy of the vector is used -> avoid malloc in real-time routine - // emit ProtocolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, peerAddress, pSocket ); + // emit ProtocolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, pTcpConnection->tcpAddress, pTcpConnection ); //### TODO: END ###// } } diff --git a/src/tcpserver.h b/src/tcpserver.h index 2d57eb7a8a..d052f6e2b6 100644 --- a/src/tcpserver.h +++ b/src/tcpserver.h @@ -32,14 +32,14 @@ #include #include "global.h" -#include "protocol.h" #include "util.h" // The header files channel.h and server.h require to include this header file // so we get a cyclic dependency. To solve this issue, a prototype of the // channel class and server class is defined here. -class CServer; // forward declaration of CServer -class CChannel; // forward declaration of CChannel +class CServer; // forward declaration of CServer +class CChannel; // forward declaration of CChannel +class CTcpConnection; // forward declaration of CTcpConnection /* Classes ********************************************************************/ class CTcpServer : public QObject @@ -60,7 +60,7 @@ class CTcpServer : public QObject QTcpServer* pTcpServer; signals: - void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, QTcpSocket* pTcpSocket ); + void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); protected slots: void OnNewConnection(); diff --git a/src/testbench.h b/src/testbench.h index 3e5568ff9e..704b785d93 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -218,7 +218,7 @@ public slots: vecServerInfo[0].strCity = GenRandomString(); vecServerInfo[0].strName = GenRandomString(); - Protocol.CreateCLServerListMes ( CurHostAddress, vecServerInfo ); + Protocol.CreateCLServerListMes ( CurHostAddress, vecServerInfo, nullptr ); break; case 20: // PROTMESSID_CLM_REQ_SERVER_LIST @@ -261,7 +261,7 @@ public slots: vecChanInfo[0].iChanID = GenRandomIntInRange ( -2, 20 ); vecChanInfo[0].strName = GenRandomString(); - Protocol.CreateCLConnClientsListMes ( CurHostAddress, vecChanInfo ); + Protocol.CreateCLConnClientsListMes ( CurHostAddress, vecChanInfo, nullptr ); break; case 29: // PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST From 0900a79a459900c1aa864a840391f0da9655e655 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 21 Jan 2025 17:52:46 +0000 Subject: [PATCH 04/41] Create CLM_TCP_SUPPORTED and related methods --- src/protocol.cpp | 23 +++++++++++++++++++++++ src/protocol.h | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/src/protocol.cpp b/src/protocol.cpp index a13af0c3a6..a620141275 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -437,6 +437,12 @@ CONNECTION LESS MESSAGES five times for one registration request at 500ms intervals. Beyond this, it should "ping" every 15 minutes (standard re-registration timeout). + + +- PROTMESSID_CLM_TCP_SUPPORTED: TCP supported message + + note: does not have any data -> n = 0 + */ #include "protocol.h" @@ -931,6 +937,10 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe case PROTMESSID_CLM_REGISTER_SERVER_RESP: EvaluateCLRegisterServerResp ( InetAddr, vecbyMesBodyData ); break; + + case PROTMESSID_CLM_TCP_SUPPORTED: + EvaluateCLTcpSupportedMes ( InetAddr ); + break; } } @@ -2604,6 +2614,19 @@ bool CProtocol::EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, con return false; // no error } +void CProtocol::CreateCLTcpSupportedMes ( const CHostAddress& InetAddr ) +{ + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_TCP_SUPPORTED, CVector ( 0 ), InetAddr ); +} + +bool CProtocol::EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr ) +{ + // invoke message action + emit CLTcpSupported ( InetAddr ); + + return false; // no error +} + /******************************************************************************\ * Message generation and parsing * \******************************************************************************/ diff --git a/src/protocol.h b/src/protocol.h index 3e3a323202..7b79151101 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -85,6 +85,7 @@ #define PROTMESSID_CLM_REGISTER_SERVER_RESP 1016 // status of server registration request #define PROTMESSID_CLM_REGISTER_SERVER_EX 1017 // register server with extended information #define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list +#define PROTMESSID_CLM_TCP_SUPPORTED 1019 // TCP is supported // special IDs #define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages @@ -156,6 +157,7 @@ class CProtocol : public QObject void CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr ); void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); + void CreateCLTcpSupportedMes ( const CHostAddress& InetAddr ); static int GetBodyLength ( const CVector& vecbyData ); @@ -291,6 +293,7 @@ class CProtocol : public QObject bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ); bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr ); int iOldRecID; int iOldRecCnt; @@ -358,4 +361,5 @@ public slots: void CLReqConnClientsList ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ); void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); + void CLTcpSupported ( CHostAddress InetAddr ); }; From a18d5410d861f17f406a4c7f4e06076cdc85ebdd Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 9 Mar 2026 16:00:33 +0000 Subject: [PATCH 05/41] Add CLM_TCP_SUPPORTED message generation when TCP enabled --- src/server.cpp | 7 +++++++ src/server.h | 6 ++++++ src/serverlist.cpp | 12 +++++++++++- src/serverlist.h | 2 ++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index 44e0cf3c86..5466328af9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -67,6 +67,7 @@ CServer::CServer ( const int iNewMaxNumChan, strServerListFilter, iNewMaxNumChan, bNEnableIPv6, + bNEnableTcp, &ConnLessProtocol ), JamController ( this ), bDisableRecording ( bDisableRecording ), @@ -388,6 +389,12 @@ void CServer::OnNewConnection ( int iChID, int iTotChans, CHostAddress RecHostAd { QMutexLocker locker ( &Mutex ); + // if TCP is enabled, we need to announce this first, before sending Client ID + if ( bEnableTcp ) + { + ConnLessProtocol.CreateCLTcpSupportedMes ( vecChannels[iChID].GetAddress() ); + } + // inform the client about its own ID at the server (note that this // must be the first message to be sent for a new connection) vecChannels[iChID].CreateClientIDMes ( iChID ); diff --git a/src/server.h b/src/server.h index 155781a264..8f7fbe8544 100644 --- a/src/server.h +++ b/src/server.h @@ -373,6 +373,12 @@ public slots: void OnCLReqConnClientsList ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ) { ConnLessProtocol.CreateCLConnClientsListMes ( InetAddr, CreateChannelList(), pTcpConnection ); + + // if TCP is enabled but this request is on UDP, say TCP is supported + if ( bEnableTcp && !pTcpConnection ) + { + ConnLessProtocol.CreateCLTcpSupportedMes ( InetAddr ); + } } void OnCLRegisterServerReceived ( CHostAddress InetAddr, CHostAddress LInetAddr, CServerCoreInfo ServerInfo ) diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 582513045c..5d07498a61 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -128,9 +128,11 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, const QString& strServerPublicIP, const int iNumChannels, const bool bNEnableIPv6, + const bool bNEnableTcp, CProtocol* pNConLProt ) : DirectoryType ( AT_NONE ), bEnableIPv6 ( bNEnableIPv6 ), + bEnableTcp ( bNEnableTcp ), ServerListFileName ( strServerListFileName ), strDirectoryAddress ( "" ), bIsDirectory ( false ), @@ -709,7 +711,9 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr, CTcpConnect } // do not send a "ping" to a server local to the directory (no need) - if ( !serverIsInternal ) + // also only do so if processing a request over UDP, not TCP, + // as the client will always try UDP before TCP. + if ( !serverIsInternal && !pTcpConnection ) { // create "send empty message" for all other registered servers // this causes the server (vecServerInfo[iIdx].HostAddr) @@ -728,6 +732,12 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr, CTcpConnect pConnLessProtocol->CreateCLRedServerListMes ( InetAddr, vecServerInfo ); } pConnLessProtocol->CreateCLServerListMes ( InetAddr, vecServerInfo, pTcpConnection ); + + // if TCP is enabled but this request is on UDP, say TCP is supported + if ( bEnableTcp && !pTcpConnection ) + { + pConnLessProtocol->CreateCLTcpSupportedMes ( InetAddr ); + } } } diff --git a/src/serverlist.h b/src/serverlist.h index 8edb843123..fe7012a461 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -141,6 +141,7 @@ class CServerListManager : public QObject const QString& strServerPublicIP, const int iNumChannels, const bool bNEnableIPv6, + const bool bNEnableTcp, CProtocol* pNConLProt ); void SetServerName ( const QString& strNewName ); @@ -192,6 +193,7 @@ class CServerListManager : public QObject EDirectoryType DirectoryType; bool bEnableIPv6; + bool bEnableTcp; CHostAddress ServerPublicIP; CHostAddress ServerPublicIP6; From 27cc94d8acc25f73fd61c3b50293b2eeaf49f706 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 16 Mar 2026 11:28:54 +0000 Subject: [PATCH 06/41] Add flag to request TCP client use --- src/client.h | 10 ++++++++-- src/clientdlg.h | 7 +++++-- src/clientrpc.cpp | 2 +- src/connectdlg.cpp | 6 +++--- src/connectdlg.h | 4 ++-- src/protocol.cpp | 11 ++++++----- src/protocol.h | 7 ++++--- src/testbench.h | 4 ++-- 8 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/client.h b/src/client.h index d82a60917d..fe742c4074 100644 --- a/src/client.h +++ b/src/client.h @@ -278,9 +278,15 @@ class CClient : public QObject void CreateCLServerListReqVerAndOSMes ( const CHostAddress& InetAddr ) { ConnLessProtocol.CreateCLReqVersionAndOSMes ( InetAddr ); } - void CreateCLServerListReqConnClientsListMes ( const CHostAddress& InetAddr ) { ConnLessProtocol.CreateCLReqConnClientsListMes ( InetAddr ); } + void CreateCLServerListReqConnClientsListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) + { + ConnLessProtocol.CreateCLReqConnClientsListMes ( InetAddr, bUseTcpClient ); + } - void CreateCLReqServerListMes ( const CHostAddress& InetAddr ) { ConnLessProtocol.CreateCLReqServerListMes ( InetAddr ); } + void CreateCLReqServerListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) + { + ConnLessProtocol.CreateCLReqServerListMes ( InetAddr, bUseTcpClient ); + } int EstimatedOverallDelay ( const int iPingTimeMs ); diff --git a/src/clientdlg.h b/src/clientdlg.h index a5d327676a..9abb70f974 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -202,13 +202,16 @@ public slots: void OnNewLocalInputText ( QString strChatText ) { pClient->CreateChatTextMes ( strChatText ); } - void OnReqServerListQuery ( CHostAddress InetAddr ) { pClient->CreateCLReqServerListMes ( InetAddr ); } + void OnReqServerListQuery ( CHostAddress InetAddr, bool bUseTcpClient ) { pClient->CreateCLReqServerListMes ( InetAddr, bUseTcpClient ); } void OnCreateCLServerListPingMes ( CHostAddress InetAddr ) { pClient->CreateCLServerListPingMes ( InetAddr ); } void OnCreateCLServerListReqVerAndOSMes ( CHostAddress InetAddr ) { pClient->CreateCLServerListReqVerAndOSMes ( InetAddr ); } - void OnCreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr ) { pClient->CreateCLServerListReqConnClientsListMes ( InetAddr ); } + void OnCreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr, bool bUseTcpClient ) + { + pClient->CreateCLServerListReqConnClientsListMes ( InetAddr, bUseTcpClient ); + } void OnCLServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ) { diff --git a/src/clientrpc.cpp b/src/clientrpc.cpp index c2efc59b25..367f2e3669 100644 --- a/src/clientrpc.cpp +++ b/src/clientrpc.cpp @@ -171,7 +171,7 @@ CClientRpc::CClientRpc ( CClient* pClient, CClientSettings* pSettings, CRpcServe if ( NetworkUtil::ParseNetworkAddress ( jsonDirectoryIp.toString(), haDirectoryAddress, false ) ) { // send the request for the server list - pClient->CreateCLReqServerListMes ( haDirectoryAddress ); + pClient->CreateCLReqServerListMes ( haDirectoryAddress, false ); // UDP response["result"] = "ok"; } else diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 212e35d684..e70dd243f4 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -336,7 +336,7 @@ void CConnectDlg::RequestServerList() false ) ) { // send the request for the server list - emit ReqServerListQuery ( haDirectoryAddress ); + emit ReqServerListQuery ( haDirectoryAddress, false ); // UDP // start timer, if this message did not get any respond to retransmit // the server list request message @@ -379,7 +379,7 @@ void CConnectDlg::OnTimerReRequestServList() { // note that this is a connection less message which may get lost // and therefore it makes sense to re-transmit it - emit ReqServerListQuery ( haDirectoryAddress ); + emit ReqServerListQuery ( haDirectoryAddress, false ); // UDP } } @@ -987,7 +987,7 @@ void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, // connected clients, if not then request the client names if ( iNumClients != pCurListViewItem->childCount() ) { - emit CreateCLServerListReqConnClientsListMes ( InetAddr ); + emit CreateCLServerListReqConnClientsListMes ( InetAddr, false ); // UDP } // this is the first time a ping time was received, set item to visible diff --git a/src/connectdlg.h b/src/connectdlg.h index 435169edf7..ff880c389d 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -135,8 +135,8 @@ public slots: void OnCurrentServerItemChanged ( QTreeWidgetItem* current, QTreeWidgetItem* previous ); signals: - void ReqServerListQuery ( CHostAddress InetAddr ); + void ReqServerListQuery ( CHostAddress InetAddr, bool bUseTcpClient ); void CreateCLServerListPingMes ( CHostAddress InetAddr ); void CreateCLServerListReqVerAndOSMes ( CHostAddress InetAddr ); - void CreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr ); + void CreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr, bool bUseTcpClient ); }; diff --git a/src/protocol.cpp b/src/protocol.cpp index a620141275..55ad4a71d9 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -619,7 +619,8 @@ void CProtocol::CreateAndImmSendAcknMess ( const int& iID, const int& iCnt ) void CProtocol::CreateAndImmSendConLessMessage ( const int iID, const CVector& vecData, const CHostAddress& InetAddr, - CTcpConnection* pTcpConnection ) + CTcpConnection* pTcpConnection, + bool bUseTcpClient ) { CVector vecNewMessage; @@ -2255,9 +2256,9 @@ bool CProtocol::EvaluateCLRedServerListMes ( const CHostAddress& InetAddr, const return false; // no error } -void CProtocol::CreateCLReqServerListMes ( const CHostAddress& InetAddr ) +void CProtocol::CreateCLReqServerListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) { - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_SERVER_LIST, CVector ( 0 ), InetAddr ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_SERVER_LIST, CVector ( 0 ), InetAddr, nullptr, bUseTcpClient ); } bool CProtocol::EvaluateCLReqServerListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) @@ -2508,9 +2509,9 @@ bool CProtocol::EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, con return false; // no error } -void CProtocol::CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr ) +void CProtocol::CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) { - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST, CVector ( 0 ), InetAddr ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST, CVector ( 0 ), InetAddr, nullptr, bUseTcpClient ); } bool CProtocol::EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) diff --git a/src/protocol.h b/src/protocol.h index 7b79151101..5d1bde4a1b 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -147,14 +147,14 @@ class CProtocol : public QObject void CreateCLUnregisterServerMes ( const CHostAddress& InetAddr ); void CreateCLServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo, CTcpConnection* pTcpConnection ); void CreateCLRedServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo ); - void CreateCLReqServerListMes ( const CHostAddress& InetAddr ); + void CreateCLReqServerListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ); void CreateCLSendEmptyMesMes ( const CHostAddress& InetAddr, const CHostAddress& TargetInetAddr ); void CreateCLEmptyMes ( const CHostAddress& InetAddr ); void CreateCLDisconnection ( const CHostAddress& InetAddr ); void CreateCLVersionAndOSMes ( const CHostAddress& InetAddr ); void CreateCLReqVersionAndOSMes ( const CHostAddress& InetAddr ); void CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecChanInfo, CTcpConnection* pTcpConnection ); - void CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr ); + void CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ); void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); void CreateCLTcpSupportedMes ( const CHostAddress& InetAddr ); @@ -254,7 +254,8 @@ class CProtocol : public QObject void CreateAndImmSendConLessMessage ( const int iID, const CVector& vecData, const CHostAddress& InetAddr, - CTcpConnection* pTcpConnection = nullptr ); + CTcpConnection* pTcpConnection = nullptr, + bool bUseTcpClient = false ); bool EvaluateJitBufMes ( const CVector& vecData ); bool EvaluateReqJitBufMes(); diff --git a/src/testbench.h b/src/testbench.h index 704b785d93..6191a8659e 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -222,7 +222,7 @@ public slots: break; case 20: // PROTMESSID_CLM_REQ_SERVER_LIST - Protocol.CreateCLReqServerListMes ( CurHostAddress ); + Protocol.CreateCLReqServerListMes ( CurHostAddress, false ); break; case 21: // PROTMESSID_CLM_SEND_EMPTY_MESSAGE @@ -265,7 +265,7 @@ public slots: break; case 29: // PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST - Protocol.CreateCLReqConnClientsListMes ( CurHostAddress ); + Protocol.CreateCLReqConnClientsListMes ( CurHostAddress, false ); break; case 30: // PROTMESSID_CLM_CHANNEL_LEVEL_LIST From 474aefa48fb9dc55a72f1f47f2c0a6d30c1e51a1 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 16 Mar 2026 14:22:38 +0000 Subject: [PATCH 07/41] Add handlers for TCP Supported message --- src/client.cpp | 2 ++ src/client.h | 2 ++ src/clientdlg.cpp | 2 ++ src/clientdlg.h | 5 +++++ src/connectdlg.cpp | 5 +++++ src/connectdlg.h | 2 ++ 6 files changed, 18 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index f6da70521e..5fd058aafd 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -148,6 +148,8 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &ConnLessProtocol, &CProtocol::CLRedServerListReceived, this, &CClient::CLRedServerListReceived ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLTcpSupported, this, &CClient::CLTcpSupported ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLConnClientsListMesReceived, this, &CClient::CLConnClientsListMesReceived ); QObject::connect ( &ConnLessProtocol, &CProtocol::CLPingReceived, this, &CClient::OnCLPingReceived ); diff --git a/src/client.h b/src/client.h index fe742c4074..da5a7ed511 100644 --- a/src/client.h +++ b/src/client.h @@ -479,6 +479,8 @@ protected slots: void CLRedServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ); + void CLTcpSupported ( CHostAddress InetAddr ); + void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); void CLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int iPingTime, int iNumClients ); diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 9fdb52f3a1..ec46f035d6 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -509,6 +509,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QObject::connect ( pClient, &CClient::CLRedServerListReceived, this, &CClientDlg::OnCLRedServerListReceived ); + QObject::connect ( pClient, &CClient::CLTcpSupported, this, &CClientDlg::OnCLTcpSupported ); + QObject::connect ( pClient, &CClient::CLConnClientsListMesReceived, this, &CClientDlg::OnCLConnClientsListMesReceived ); QObject::connect ( pClient, &CClient::CLPingTimeWithNumClientsReceived, this, &CClientDlg::OnCLPingTimeWithNumClientsReceived ); diff --git a/src/clientdlg.h b/src/clientdlg.h index 9abb70f974..b5071fa54a 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -223,6 +223,11 @@ public slots: ConnectDlg.SetServerList ( InetAddr, vecServerInfo, true ); } + void OnCLTcpSupported ( CHostAddress InetAddr ) + { + ConnectDlg.SetTcpSupported ( InetAddr ); + } + void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ) { ConnectDlg.SetConnClientsList ( InetAddr, vecChanInfo ); diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index e70dd243f4..c616b5469c 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -544,6 +544,11 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector& vecChanInfo ) { // find the server with the correct address diff --git a/src/connectdlg.h b/src/connectdlg.h index ff880c389d..5f34428b08 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -69,6 +69,8 @@ class CConnectDlg : public CBaseDlg, private Ui_CConnectDlgBase void SetServerList ( const CHostAddress& InetAddr, const CVector& vecServerInfo, const bool bIsReducedServerList = false ); + void SetTcpSupported ( const CHostAddress& InetAddr ); + void SetConnClientsList ( const CHostAddress& InetAddr, const CVector& vecChanInfo ); void SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, const int iPingTime, const int iNumClients ); From 21ea915650c544582e8b45a2031a7214c43a14e1 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 16 Mar 2026 15:02:49 +0000 Subject: [PATCH 08/41] Propagate TCP client flag down to OnSendCLProtMessage --- src/client.cpp | 13 +++++++++---- src/client.h | 2 +- src/clientdlg.h | 5 +---- src/connectdlg.cpp | 5 +---- src/protocol.cpp | 2 +- src/protocol.h | 2 +- src/server.cpp | 8 +++++++- src/server.h | 2 +- 8 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 5fd058aafd..4b17cf8a78 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -254,14 +254,19 @@ void CClient::OnSendProtMessage ( CVector vecMessage ) Socket.SendPacket ( vecMessage, Channel.GetAddress() ); } -void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ) +void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ) { + if ( pTcpConnection ) + { + qWarning() << "Client send cannot use TCP server"; + return; + } + // the protocol queries me to call the function to send the message // send it through the network - if ( pTcpConnection ) + if ( bUseTcpClient ) { - // send to the connected socket directly - pTcpConnection->pTcpSocket->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); + // create a TCP client connection and send message } else { diff --git a/src/client.h b/src/client.h index da5a7ed511..0192455ab6 100644 --- a/src/client.h +++ b/src/client.h @@ -449,7 +449,7 @@ protected slots: } void OnCLPingReceived ( CHostAddress InetAddr, int iMs ); - void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ); + void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ); void OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, int iMs, int iNumClients ); diff --git a/src/clientdlg.h b/src/clientdlg.h index b5071fa54a..b36e297593 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -223,10 +223,7 @@ public slots: ConnectDlg.SetServerList ( InetAddr, vecServerInfo, true ); } - void OnCLTcpSupported ( CHostAddress InetAddr ) - { - ConnectDlg.SetTcpSupported ( InetAddr ); - } + void OnCLTcpSupported ( CHostAddress InetAddr ) { ConnectDlg.SetTcpSupported ( InetAddr ); } void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ) { diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index c616b5469c..6c7e9c77fd 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -544,10 +544,7 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector& vecChanInfo ) { diff --git a/src/protocol.cpp b/src/protocol.cpp index 55ad4a71d9..2bfd249a10 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -629,7 +629,7 @@ void CProtocol::CreateAndImmSendConLessMessage ( const int iID, GenMessageFrame ( vecNewMessage, 0, iID, vecData ); // immediately send message - emit CLMessReadyForSending ( InetAddr, vecNewMessage, pTcpConnection ); + emit CLMessReadyForSending ( InetAddr, vecNewMessage, pTcpConnection, bUseTcpClient ); } void CProtocol::ParseMessageBody ( const CVector& vecbyMesBodyData, const int iRecCounter, const int iRecID ) diff --git a/src/protocol.h b/src/protocol.h index 5d1bde4a1b..3fdf421753 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -317,7 +317,7 @@ public slots: signals: // transmitting void MessReadyForSending ( CVector vecMessage ); - void CLMessReadyForSending ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ); + void CLMessReadyForSending ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ); // receiving void ChangeJittBufSize ( int iNewJitBufSize ); diff --git a/src/server.cpp b/src/server.cpp index 5466328af9..298cf8ac8f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -483,8 +483,14 @@ void CServer::OnServerFull ( CHostAddress RecHostAddr ) ConnLessProtocol.CreateCLServerFullMes ( RecHostAddr ); } -void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ) +void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ) { + if ( bUseTcpClient ) + { + qWarning() << "Server send cannot use TCP client"; + return; + } + // the protocol queries me to call the function to send the message // send it through the network if ( pTcpConnection ) diff --git a/src/server.h b/src/server.h index 8f7fbe8544..56bf21743c 100644 --- a/src/server.h +++ b/src/server.h @@ -344,7 +344,7 @@ public slots: void OnServerFull ( CHostAddress RecHostAddr ); - void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection ); + void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ); void OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ); From 7133516bf670dd270c909e409e7aca426f8fe58a Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 19 Mar 2026 12:16:50 +0000 Subject: [PATCH 09/41] Delete QTcpServer object when done --- src/tcpserver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index 2a461b129d..085b53088a 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -46,6 +46,7 @@ CTcpServer::~CTcpServer() qInfo() << "- stopping Jamulus-TCP server"; pTcpServer->close(); } + pTcpServer->deleteLater(); } bool CTcpServer::Start() From fd406dc280293bb8f22199769557de8454ee7884 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 20 Mar 2026 18:31:08 +0000 Subject: [PATCH 10/41] Added some debug output --- src/tcpserver.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index 085b53088a..be27512433 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -98,10 +98,10 @@ void CTcpServer::OnNewConnection() } } - CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, peerAddress ); - qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.InetAddr.toString(); + CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, peerAddress ); + // allocate memory for network receive and send buffer in samples CVector vecbyRecBuf; vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); @@ -120,12 +120,16 @@ void CTcpServer::OnNewConnection() int iRecID; CVector vecbyMesBodyData; + qDebug() << "- readyRead(), bytesAvailable() =" << pTcpConnection->pTcpSocket->bytesAvailable(); + long iNumBytesRead = pTcpConnection->pTcpSocket->read ( (char*) &vecbyRecBuf[0], MESS_HEADER_LENGTH_BYTE ); if ( iNumBytesRead == -1 ) { return; } + qDebug() << "- iNumBytesRead =" << iNumBytesRead; + if ( iNumBytesRead < MESS_HEADER_LENGTH_BYTE ) { qDebug() << "-- short read: expected" << MESS_HEADER_LENGTH_BYTE << "bytes, got" << iNumBytesRead; @@ -140,6 +144,8 @@ void CTcpServer::OnNewConnection() return; } + qDebug() << "- iNumBytesRead2 =" << iNumBytesRead2; + if ( iNumBytesRead2 < iPayloadLength ) { qDebug() << "-- short read: expected" << iPayloadLength << "bytes, got" << iNumBytesRead2; @@ -170,6 +176,8 @@ void CTcpServer::OnNewConnection() //### TODO: END ###// } } + + qDebug() << "- end of readyRead(), bytesAvailable() =" << pTcpConnection->pTcpSocket->bytesAvailable(); } ); } From 017a463d1d7e90dcaeddbf5c760d442d7342ff5c Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 20 Mar 2026 22:30:43 +0000 Subject: [PATCH 11/41] Update copyright years --- src/tcpserver.cpp | 2 +- src/tcpserver.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index be27512433..7d2764e993 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -1,5 +1,5 @@ /******************************************************************************\ - * Copyright (c) 2024 + * Copyright (c) 2024-2026 * * Author(s): * Tony Mountifield diff --git a/src/tcpserver.h b/src/tcpserver.h index d052f6e2b6..ea00e50c8d 100644 --- a/src/tcpserver.h +++ b/src/tcpserver.h @@ -1,5 +1,5 @@ /******************************************************************************\ - * Copyright (c) 2024 + * Copyright (c) 2024-2026 * * Author(s): * Tony Mountifield From 21dc1f86e5a92c325fa7f9dd3e326dfa6ede43e6 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 21 Mar 2026 00:28:24 +0000 Subject: [PATCH 12/41] Separate CTcpConnection code from CTcpServer --- Jamulus.pro | 2 + src/protocol.h | 2 +- src/server.h | 1 + src/socket.h | 2 +- src/tcpconnection.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++ src/tcpconnection.h | 68 ++++++++++++++++++++ src/tcpserver.cpp | 90 +------------------------- src/tcpserver.h | 23 ++----- 8 files changed, 228 insertions(+), 105 deletions(-) create mode 100644 src/tcpconnection.cpp create mode 100644 src/tcpconnection.h diff --git a/Jamulus.pro b/Jamulus.pro index 9ce4fd04da..b38017ac96 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -400,6 +400,7 @@ HEADERS += src/plugins/audioreverb.h \ src/settings.h \ src/socket.h \ src/tcpserver.h \ + src/tcpconnection.h \ src/util.h \ src/recorder/jamrecorder.h \ src/recorder/creaperproject.h \ @@ -509,6 +510,7 @@ SOURCES += src/plugins/audioreverb.cpp \ src/signalhandler.cpp \ src/socket.cpp \ src/tcpserver.cpp \ + src/tcpconnection.cpp \ src/util.cpp \ src/recorder/jamrecorder.cpp \ src/recorder/creaperproject.cpp \ diff --git a/src/protocol.h b/src/protocol.h index 3fdf421753..9c9f85d933 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -31,7 +31,7 @@ #include #include "global.h" #include "util.h" -#include "tcpserver.h" +#include "tcpconnection.h" /* Definitions ****************************************************************/ // protocol message IDs diff --git a/src/server.h b/src/server.h index 56bf21743c..991e027119 100644 --- a/src/server.h +++ b/src/server.h @@ -43,6 +43,7 @@ #include "serverlogging.h" #include "serverlist.h" #include "tcpserver.h" +#include "tcpconnection.h" #include "recorder/jamcontroller.h" #include "threadpool.h" diff --git a/src/socket.h b/src/socket.h index a8919fcc01..2a9c6216a1 100644 --- a/src/socket.h +++ b/src/socket.h @@ -31,7 +31,7 @@ #include "global.h" #include "protocol.h" #include "util.h" -#include "tcpserver.h" +#include "tcpconnection.h" #ifndef _WIN32 # include # include diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp new file mode 100644 index 0000000000..8a960e6c4b --- /dev/null +++ b/src/tcpconnection.cpp @@ -0,0 +1,145 @@ +/******************************************************************************\ + * Copyright (c) 2024-2026 + * + * Author(s): + * Tony Mountifield + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + \******************************************************************************/ + +#include "tcpserver.h" + +#include "protocol.h" +#include "server.h" +#include "channel.h" + +CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer ) : + pTcpSocket ( pTcpSocket ), + tcpAddress ( tcpAddress ), + pServer ( pServer ) +{ + vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); + iPos = 0; + iPayloadRemain = 0; + + connect ( pTcpSocket, &QTcpSocket::disconnected, this, &CTcpConnection::OnDisconnected ); + connect ( pTcpSocket, &QTcpSocket::readyRead, this, &CTcpConnection::OnReadyRead ); + if ( pServer ) + { + connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); + } +} + +void CTcpConnection::OnDisconnected() +{ + qDebug() << "- Jamulus-TCP: disconnected from:" << tcpAddress.InetAddr.toString(); + pTcpSocket->deleteLater(); + delete this; +} + +void CTcpConnection::OnReadyRead() +{ + long iBytesAvail = pTcpSocket->bytesAvailable(); + + qDebug() << "- readyRead(), bytesAvailable() =" << iBytesAvail; + + while ( iBytesAvail > 0 ) + { + if ( iPos < MESS_HEADER_LENGTH_BYTE ) + { + // reading message header + long iNumBytesRead = pTcpSocket->read ( (char*) &vecbyRecBuf[iPos], MESS_HEADER_LENGTH_BYTE - iPos ); + if ( iNumBytesRead == -1 ) + { + return; + } + + qDebug() << "-- (hdr) iNumBytesRead =" << iNumBytesRead; + + iPos += iNumBytesRead; + iBytesAvail -= iNumBytesRead; + + if ( iPos >= MESS_HEADER_LENGTH_BYTE ) + { + // now have a complete header + iPayloadRemain = CProtocol::GetBodyLength ( vecbyRecBuf ); + + Q_ASSERT ( iPayloadRemain <= MAX_SIZE_BYTES_NETW_BUF - MESS_HEADER_LENGTH_BYTE ); + + iPayloadRemain -= iPos - MESS_HEADER_LENGTH_BYTE; + } + } + else + { + // reading message body + long iNumBytesRead = pTcpSocket->read ( (char*) &vecbyRecBuf[iPos], iPayloadRemain ); + if ( iNumBytesRead == -1 ) + { + return; + } + + qDebug() << "-- (body) iNumBytesRead =" << iNumBytesRead; + + iPos += iNumBytesRead; + iPayloadRemain -= iNumBytesRead; + iBytesAvail -= iNumBytesRead; + + Q_ASSERT ( iPayloadRemain >= 0 ); + + if ( iPayloadRemain == 0 ) + { + // have a complete payload + qDebug() << "- Jamulus-TCP: received protocol message of length" << iPos; + + // check if this is a protocol message + int iRecCounter; + int iRecID; + CVector vecbyMesBodyData; + + if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, iPos, vecbyMesBodyData, iRecCounter, iRecID ) ) + { + qDebug() << "- Jamulus-TCP: message parsed OK, ID =" << iRecID; + + // this is a protocol message, check the type of the message + if ( CProtocol::IsConnectionLessMessageID ( iRecID ) ) + { + //### TODO: BEGIN ###// + // a copy of the vector is used -> avoid malloc in real-time routine + emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, tcpAddress, this ); + //### TODO: END ###// + } + else + { + //### TODO: BEGIN ###// + // a copy of the vector is used -> avoid malloc in real-time routine + // emit ProtocolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, pTcpConnection->tcpAddress, pTcpConnection ); + //### TODO: END ###// + } + } + else + { + qDebug() << "- Jamulus-TCP: failed to parse frame"; + } + + iPos = 0; // ready for next message, if any + } + } + } + + qDebug() << "- end of readyRead(), bytesAvailable() =" << pTcpSocket->bytesAvailable(); +} diff --git a/src/tcpconnection.h b/src/tcpconnection.h new file mode 100644 index 0000000000..46527c15cb --- /dev/null +++ b/src/tcpconnection.h @@ -0,0 +1,68 @@ +/******************************************************************************\ + * Copyright (c) 2024-2026 + * + * Author(s): + * Tony Mountifield + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "global.h" +#include "util.h" + +// The header files channel.h and server.h require to include this header file +// so we get a cyclic dependency. To solve this issue, a prototype of the +// channel class and server class is defined here. +class CServer; // forward declaration of CServer +// class CChannel; // forward declaration of CChannel + +/* Classes ********************************************************************/ +class CTcpConnection : public QObject +{ + Q_OBJECT + +public: + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer = nullptr ); + ~CTcpConnection() {} + + QTcpSocket* pTcpSocket; + CHostAddress tcpAddress; + CHostAddress udpAddress; + +private: + CServer* pServer; + int iPos; + int iPayloadRemain; + CVector vecbyRecBuf; + +signals: + void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); + +protected slots: + void OnDisconnected(); + void OnReadyRead(); +}; diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index 7d2764e993..d659914680 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -23,10 +23,11 @@ \******************************************************************************/ #include "tcpserver.h" +//#include "tcpconnection.h" #include "protocol.h" #include "server.h" -#include "channel.h" +//#include "channel.h" CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ) : pServer ( pNServP ), @@ -35,7 +36,6 @@ CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int i bEnableIPv6 ( bEnableIPv6 ), pTcpServer ( new QTcpServer ( this ) ) { - connect ( this, &CTcpServer::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); connect ( pTcpServer, &QTcpServer::newConnection, this, &CTcpServer::OnNewConnection ); } @@ -100,89 +100,5 @@ void CTcpServer::OnNewConnection() qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.InetAddr.toString(); - CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, peerAddress ); - - // allocate memory for network receive and send buffer in samples - CVector vecbyRecBuf; - vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); - - connect ( pSocket, &QTcpSocket::disconnected, [this, pTcpConnection]() { - qDebug() << "- Jamulus-TCP: connection from:" << pTcpConnection->tcpAddress.InetAddr.toString() << "closed"; - pTcpConnection->pTcpSocket->deleteLater(); - delete pTcpConnection; - } ); - - connect ( pSocket, &QTcpSocket::readyRead, [this, pTcpConnection, vecbyRecBuf]() { - // handle received Jamulus protocol packet - - // check if this is a protocol message - int iRecCounter; - int iRecID; - CVector vecbyMesBodyData; - - qDebug() << "- readyRead(), bytesAvailable() =" << pTcpConnection->pTcpSocket->bytesAvailable(); - - long iNumBytesRead = pTcpConnection->pTcpSocket->read ( (char*) &vecbyRecBuf[0], MESS_HEADER_LENGTH_BYTE ); - if ( iNumBytesRead == -1 ) - { - return; - } - - qDebug() << "- iNumBytesRead =" << iNumBytesRead; - - if ( iNumBytesRead < MESS_HEADER_LENGTH_BYTE ) - { - qDebug() << "-- short read: expected" << MESS_HEADER_LENGTH_BYTE << "bytes, got" << iNumBytesRead; - return; - } - - long iPayloadLength = CProtocol::GetBodyLength ( vecbyRecBuf ); - - long iNumBytesRead2 = pTcpConnection->pTcpSocket->read ( (char*) &vecbyRecBuf[MESS_HEADER_LENGTH_BYTE], iPayloadLength ); - if ( iNumBytesRead2 == -1 ) - { - return; - } - - qDebug() << "- iNumBytesRead2 =" << iNumBytesRead2; - - if ( iNumBytesRead2 < iPayloadLength ) - { - qDebug() << "-- short read: expected" << iPayloadLength << "bytes, got" << iNumBytesRead2; - return; - } - - iNumBytesRead += iNumBytesRead2; - - qDebug() << "- Jamulus-TCP: received protocol message of length" << iNumBytesRead; - - if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, iNumBytesRead, vecbyMesBodyData, iRecCounter, iRecID ) ) - { - qDebug() << "- Jamulus-TCP: message parsed OK, ID =" << iRecID; - - // this is a protocol message, check the type of the message - if ( CProtocol::IsConnectionLessMessageID ( iRecID ) ) - { - //### TODO: BEGIN ###// - // a copy of the vector is used -> avoid malloc in real-time routine - emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, pTcpConnection->tcpAddress, pTcpConnection ); - //### TODO: END ###// - } - else - { - //### TODO: BEGIN ###// - // a copy of the vector is used -> avoid malloc in real-time routine - // emit ProtocolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, pTcpConnection->tcpAddress, pTcpConnection ); - //### TODO: END ###// - } - } - - qDebug() << "- end of readyRead(), bytesAvailable() =" << pTcpConnection->pTcpSocket->bytesAvailable(); - } ); -} - -#if 0 -void CTcpServer::Send ( QTcpSocket* pSocket ) { - // pSocket->write ( ); + new CTcpConnection ( pSocket, peerAddress, pServer ); // will auto-delete on disconnect } -#endif diff --git a/src/tcpserver.h b/src/tcpserver.h index ea00e50c8d..8e1da5acc2 100644 --- a/src/tcpserver.h +++ b/src/tcpserver.h @@ -31,15 +31,17 @@ #include #include +#include "tcpconnection.h" + #include "global.h" #include "util.h" // The header files channel.h and server.h require to include this header file // so we get a cyclic dependency. To solve this issue, a prototype of the // channel class and server class is defined here. -class CServer; // forward declaration of CServer -class CChannel; // forward declaration of CChannel -class CTcpConnection; // forward declaration of CTcpConnection +class CServer; // forward declaration of CServer +// class CChannel; // forward declaration of CChannel +// class CTcpConnection; // forward declaration of CTcpConnection /* Classes ********************************************************************/ class CTcpServer : public QObject @@ -59,20 +61,9 @@ class CTcpServer : public QObject const bool bEnableIPv6; QTcpServer* pTcpServer; -signals: - void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); + // signals: + // void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); protected slots: void OnNewConnection(); }; - -class CTcpConnection -{ -public: - CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ) {} - ~CTcpConnection() {} - - QTcpSocket* pTcpSocket; - CHostAddress tcpAddress; - CHostAddress udpAddress; -}; From 23a21ef98dca5dc4739b5b0085ea7af5838d19d7 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 22 Mar 2026 16:46:15 +0000 Subject: [PATCH 13/41] Make CTcpConnection members private --- src/server.cpp | 2 +- src/tcpconnection.cpp | 10 ++++++++++ src/tcpconnection.h | 4 +++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index 298cf8ac8f..e87dd6f44a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -496,7 +496,7 @@ void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM if ( pTcpConnection ) { // send to the connected socket directly - pTcpConnection->pTcpSocket->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); + pTcpConnection->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); } else { diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index 8a960e6c4b..c342c51cc8 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -143,3 +143,13 @@ void CTcpConnection::OnReadyRead() qDebug() << "- end of readyRead(), bytesAvailable() =" << pTcpSocket->bytesAvailable(); } + +qint64 CTcpConnection::write ( const char* data, qint64 maxSize ) +{ + if ( !pTcpSocket ) + { + return -1; + } + + return pTcpSocket->write ( data, maxSize ); +} diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 46527c15cb..834900c3d5 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -49,11 +49,13 @@ class CTcpConnection : public QObject CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer = nullptr ); ~CTcpConnection() {} + qint64 write ( const char* data, qint64 maxSize ); + +private: QTcpSocket* pTcpSocket; CHostAddress tcpAddress; CHostAddress udpAddress; -private: CServer* pServer; int iPos; int iPayloadRemain; From 668f7ccd210ec2bb1e6819d77b1a07463b539d05 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 23 Mar 2026 12:16:31 +0000 Subject: [PATCH 14/41] Add client-side TCP code --- src/client.cpp | 18 ++++++++++++++++++ src/tcpconnection.cpp | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 4b17cf8a78..0ad517b990 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -267,6 +267,24 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM if ( bUseTcpClient ) { // create a TCP client connection and send message + QTcpSocket* pSocket = new QTcpSocket ( this ); + + connect ( pSocket, &QTcpSocket::errorOccurred, this, [this, pSocket] ( QAbstractSocket::SocketError err ) { + Q_UNUSED ( err ); + + qWarning() << "- TCP connection error:" << pSocket->errorString(); + // may want to specifically handle ConnectionRefusedError? + pSocket->deleteLater(); + } ); + + connect ( pSocket, &QTcpSocket::connected, this, [this, pSocket, InetAddr, vecMessage]() { + // connection succeeded, give it to a CTcpConnection + CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, InetAddr, nullptr ); // client connection, will self-delete on disconnect + + pTcpConnection->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); + + // the CTcpConnection object will pass the reply back up to CProtocol + } ); } else { diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index c342c51cc8..f20f787388 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -122,6 +122,12 @@ void CTcpConnection::OnReadyRead() // a copy of the vector is used -> avoid malloc in real-time routine emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, tcpAddress, this ); //### TODO: END ###// + + // disconnect if we are a client + if ( !pServer ) + { + pTcpSocket->disconnectFromHost(); + } } else { From 0419be527ccde646d227ab2d587fd93d3572eb68 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 23 Mar 2026 18:11:20 +0000 Subject: [PATCH 15/41] Request server list via TCP if required --- src/client.cpp | 7 +++++-- src/connectdlg.cpp | 13 ++++++++++++- src/tcpconnection.cpp | 17 ++++++++++++----- src/tcpconnection.h | 16 +++++++++------- src/tcpserver.cpp | 7 ++----- src/tcpserver.h | 13 ++++--------- 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 0ad517b990..8852a76a52 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -279,12 +279,15 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM connect ( pSocket, &QTcpSocket::connected, this, [this, pSocket, InetAddr, vecMessage]() { // connection succeeded, give it to a CTcpConnection - CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, InetAddr, nullptr ); // client connection, will self-delete on disconnect + CTcpConnection* pTcpConnection = + new CTcpConnection ( pSocket, InetAddr, nullptr, &Channel ); // client connection, will self-delete on disconnect pTcpConnection->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); - // the CTcpConnection object will pass the reply back up to CProtocol + // the CTcpConnection object will pass the reply back up to CClient::Channel } ); + + pSocket->connectToHost ( InetAddr.InetAddr, InetAddr.iPort ); } else { diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 6c7e9c77fd..b44d07df0a 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -544,7 +544,18 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector& vecChanInfo ) { diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index f20f787388..04118a238d 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -28,10 +28,11 @@ #include "server.h" #include "channel.h" -CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer ) : +CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ), - pServer ( pServer ) + pServer ( pServer ), + pChannel ( pChannel ) { vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); iPos = 0; @@ -39,17 +40,23 @@ CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcp connect ( pTcpSocket, &QTcpSocket::disconnected, this, &CTcpConnection::OnDisconnected ); connect ( pTcpSocket, &QTcpSocket::readyRead, this, &CTcpConnection::OnReadyRead ); + if ( pServer ) { connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); } + + if ( pChannel ) + { + connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pChannel, &CChannel::OnProtocolCLMessageReceived ); + } } void CTcpConnection::OnDisconnected() { - qDebug() << "- Jamulus-TCP: disconnected from:" << tcpAddress.InetAddr.toString(); + qDebug() << "- Jamulus-TCP: disconnected from:" << tcpAddress.toString(); pTcpSocket->deleteLater(); - delete this; + deleteLater(); // delete this object in the next event loop } void CTcpConnection::OnReadyRead() @@ -124,7 +131,7 @@ void CTcpConnection::OnReadyRead() //### TODO: END ###// // disconnect if we are a client - if ( !pServer ) + if ( pChannel ) { pTcpSocket->disconnectFromHost(); } diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 834900c3d5..0930b4c419 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -35,10 +35,10 @@ #include "util.h" // The header files channel.h and server.h require to include this header file -// so we get a cyclic dependency. To solve this issue, a prototype of the -// channel class and server class is defined here. -class CServer; // forward declaration of CServer -// class CChannel; // forward declaration of CChannel +// so we get a cyclic dependency. To solve this issue, prototypes of the +// channel class and server class are defined here. +class CServer; // forward declaration of CServer +class CChannel; // forward declaration of CChannel /* Classes ********************************************************************/ class CTcpConnection : public QObject @@ -46,7 +46,7 @@ class CTcpConnection : public QObject Q_OBJECT public: - CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer = nullptr ); + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel ); ~CTcpConnection() {} qint64 write ( const char* data, qint64 maxSize ); @@ -56,7 +56,9 @@ class CTcpConnection : public QObject CHostAddress tcpAddress; CHostAddress udpAddress; - CServer* pServer; + CServer* pServer; + CChannel* pChannel; + int iPos; int iPayloadRemain; CVector vecbyRecBuf; @@ -64,7 +66,7 @@ class CTcpConnection : public QObject signals: void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); -protected slots: +private slots: void OnDisconnected(); void OnReadyRead(); }; diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index d659914680..be5251dc1c 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -23,11 +23,8 @@ \******************************************************************************/ #include "tcpserver.h" -//#include "tcpconnection.h" -#include "protocol.h" #include "server.h" -//#include "channel.h" CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ) : pServer ( pNServP ), @@ -98,7 +95,7 @@ void CTcpServer::OnNewConnection() } } - qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.InetAddr.toString(); + qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.toString(); - new CTcpConnection ( pSocket, peerAddress, pServer ); // will auto-delete on disconnect + new CTcpConnection ( pSocket, peerAddress, pServer, nullptr ); // will auto-delete on disconnect } diff --git a/src/tcpserver.h b/src/tcpserver.h index 8e1da5acc2..92329d691d 100644 --- a/src/tcpserver.h +++ b/src/tcpserver.h @@ -36,12 +36,10 @@ #include "global.h" #include "util.h" -// The header files channel.h and server.h require to include this header file +// The header file server.h requires to include this header file // so we get a cyclic dependency. To solve this issue, a prototype of the -// channel class and server class is defined here. +// server class is defined here. class CServer; // forward declaration of CServer -// class CChannel; // forward declaration of CChannel -// class CTcpConnection; // forward declaration of CTcpConnection /* Classes ********************************************************************/ class CTcpServer : public QObject @@ -50,7 +48,7 @@ class CTcpServer : public QObject public: CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ); - virtual ~CTcpServer(); + ~CTcpServer(); bool Start(); @@ -61,9 +59,6 @@ class CTcpServer : public QObject const bool bEnableIPv6; QTcpServer* pTcpServer; - // signals: - // void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); - -protected slots: +private slots: void OnNewConnection(); }; From 4dae70db79627c59fe39e01c9f01710e15644848 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 25 Mar 2026 20:35:24 +0000 Subject: [PATCH 16/41] Add message context parameter for CLM_TCP_SUPPORTED --- src/client.h | 2 +- src/clientdlg.h | 2 +- src/connectdlg.cpp | 25 ++++++++++++++++++------- src/connectdlg.h | 2 +- src/protocol.cpp | 35 +++++++++++++++++++++++++++++------ src/protocol.h | 6 +++--- src/server.cpp | 2 +- src/server.h | 2 +- src/serverlist.cpp | 2 +- 9 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/client.h b/src/client.h index 0192455ab6..d37d7a90f4 100644 --- a/src/client.h +++ b/src/client.h @@ -479,7 +479,7 @@ protected slots: void CLRedServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ); - void CLTcpSupported ( CHostAddress InetAddr ); + void CLTcpSupported ( CHostAddress InetAddr, int iID ); void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); diff --git a/src/clientdlg.h b/src/clientdlg.h index b36e297593..64ddb86106 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -223,7 +223,7 @@ public slots: ConnectDlg.SetServerList ( InetAddr, vecServerInfo, true ); } - void OnCLTcpSupported ( CHostAddress InetAddr ) { ConnectDlg.SetTcpSupported ( InetAddr ); } + void OnCLTcpSupported ( CHostAddress InetAddr, int iID ) { ConnectDlg.SetTcpSupported ( InetAddr, iID ); } void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ) { diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index b44d07df0a..1871171cfa 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -544,16 +544,27 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVectorerrorString(); From 406b1cc8555b2fa8b28eebad7abb33b903856091 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 26 Mar 2026 23:01:08 +0000 Subject: [PATCH 18/41] Fetch client list over TCP when necessary for a server --- src/connectdlg.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- src/connectdlg.h | 9 +++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 1871171cfa..956ac32c60 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -531,6 +531,9 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVectorsetData ( LVC_NAME, Qt::UserRole, CurHostAddress.toString() ); + enum EClientFetchMode eFetchMode = CFM_UDP_REQUEST; // start off in UDP mode + pNewListViewItem->setData ( LVC_CLIENTS, Qt::UserRole, eFetchMode ); // initialise fetch mode + // per default expand the list item (if not "show all servers") if ( bShowAllMusicians ) { @@ -561,6 +564,26 @@ void CConnectDlg::SetTcpSupported ( const CHostAddress& InetAddr, int iID ) break; case PROTMESSID_CLM_CONN_CLIENTS_LIST: + // find the server with the correct address + { + CMappedTreeWidgetItem* pCurListViewItem = FindListViewItem ( InetAddr ); + + if ( pCurListViewItem ) + { + // find the current fetch mode for the client list for this server + enum EClientFetchMode eFetchMode = + static_cast ( pCurListViewItem->data ( LVC_CLIENTS, Qt::UserRole ).toInt() ); + + if ( eFetchMode == CFM_UDP_REQUEST ) + { + // client list not yet received - switch to TCP mode + eFetchMode = CFM_TCP; + pCurListViewItem->setData ( LVC_CLIENTS, Qt::UserRole, eFetchMode ); // remember for future fetches + + emit CreateCLServerListReqConnClientsListMes ( InetAddr, true ); // TCP + } + } + } break; default: @@ -575,6 +598,16 @@ void CConnectDlg::SetConnClientsList ( const CHostAddress& InetAddr, const CVect if ( pCurListViewItem ) { + // find the current fetch mode for the client list for this server + enum EClientFetchMode eFetchMode = static_cast ( pCurListViewItem->data ( LVC_CLIENTS, Qt::UserRole ).toInt() ); + + if ( eFetchMode != CFM_TCP ) + { + // not switched to TCP mode - set to UDP for successful fetch + eFetchMode = CFM_UDP_RESULT; + pCurListViewItem->setData ( LVC_CLIENTS, Qt::UserRole, eFetchMode ); + } + // first remove any existing children DeleteAllListViewItemChilds ( pCurListViewItem ); @@ -1011,7 +1044,16 @@ void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, // connected clients, if not then request the client names if ( iNumClients != pCurListViewItem->childCount() ) { - emit CreateCLServerListReqConnClientsListMes ( InetAddr, false ); // UDP + // find the current fetch mode for the client list for this server + enum EClientFetchMode eFetchMode = static_cast ( pCurListViewItem->data ( LVC_CLIENTS, Qt::UserRole ).toInt() ); + + if ( eFetchMode != CFM_TCP ) + { + // not switched to TCP mode - reset for next UDP fetch + eFetchMode = CFM_UDP_REQUEST; + pCurListViewItem->setData ( LVC_CLIENTS, Qt::UserRole, eFetchMode ); + } + emit CreateCLServerListReqConnClientsListMes ( InetAddr, eFetchMode == CFM_TCP ); // UDP or TCP } // this is the first time a ping time was received, set item to visible diff --git a/src/connectdlg.h b/src/connectdlg.h index 53fdedad8e..b3d901dee7 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -95,6 +95,15 @@ class CConnectDlg : public CBaseDlg, private Ui_CConnectDlgBase }; protected: + // UDP/TCP mode for fetching client list - stored in data field for LVC_CLIENTS column + enum EClientFetchMode + { + CFM_UDP_REQUEST, // set when sending request by UDP + CFM_UDP_RESULT, // set when received a client list by UDP + CFM_TCP, // set when "TCP Supported" message arrives but client list has not arrived - + // re-request using TCP and remain in TCP mode + }; + virtual void showEvent ( QShowEvent* ); virtual void hideEvent ( QHideEvent* ); From ccddfde350d9f06cc4ec17a8faf38a57a0ce39ac Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 1 Apr 2026 17:32:19 +0100 Subject: [PATCH 19/41] Create CLM_CLIENT_ID and related methods --- src/protocol.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++---- src/protocol.h | 4 ++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/protocol.cpp b/src/protocol.cpp index b4447b52a2..af012c6354 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -365,6 +365,11 @@ CONNECTION LESS MESSAGES +--------------------+--------------+ +- PROTMESSID_CLM_EMPTY_MESSAGE: Empty message (No-op) + + note: does not have any data -> n = 0 + + - PROTMESSID_CLM_DISCONNECTION: Disconnect message note: does not have any data -> n = 0 @@ -441,14 +446,23 @@ CONNECTION LESS MESSAGES - PROTMESSID_CLM_TCP_SUPPORTED: TCP supported message - +----------------------------------------------------------+ - | 2 bytes ID of message to be potentially retried over TCP | - +----------------------------------------------------------+ + +-------------------------------------------------------+ + | 2 bytes ID of message to be potentially sent over TCP | + +-------------------------------------------------------+ - the ID indicates which type of message preceded it: + the ID indicates which type of message relates to it: - PROTMESSID_CLM_SERVER_LIST - PROTMESSID_CLM_CONN_CLIENTS_LIST - - 0 (sent on new incoming audio stream) + - PROTMESSID_CLM_CLIENT_ID + + +- PROTMESSID_CLM_CLIENT_ID: Sends the client's channel ID back to the server + + +---------------------------------+ + | 1 byte channel ID of the client | + +---------------------------------+ + + the ID informs the server with which channel to associate the TCP connection */ @@ -949,6 +963,10 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe case PROTMESSID_CLM_TCP_SUPPORTED: EvaluateCLTcpSupportedMes ( InetAddr, vecbyMesBodyData ); break; + + case PROTMESSID_CLM_CLIENT_ID: + EvaluateCLClientIDMes ( InetAddr, vecbyMesBodyData, pTcpConnection ); + break; } } @@ -2651,6 +2669,38 @@ bool CProtocol::EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr, const return false; // no error } +void CProtocol::CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iChanID, CTcpConnection* pTcpConnection ) +{ + int iPos = 0; // init position pointer + + // build data vector (1 byte long) + CVector vecData ( 1 ); + + // channel ID (1 byte) + PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); + + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_CLIENT_ID, vecData, InetAddr, pTcpConnection ); +} + +bool CProtocol::EvaluateCLClientIDMes ( const CHostAddress& InetAddr, const CVector& vecData, CTcpConnection* pTcpConnection ) +{ + int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 1 ) + { + return true; // return error code + } + + // channel ID + const int iCurID = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // invoke message action + emit CLClientIDReceived ( InetAddr, iCurID, pTcpConnection ); + + return false; // no error +} + /******************************************************************************\ * Message generation and parsing * \******************************************************************************/ diff --git a/src/protocol.h b/src/protocol.h index d7e0774751..ff24d5f92a 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -86,6 +86,7 @@ #define PROTMESSID_CLM_REGISTER_SERVER_EX 1017 // register server with extended information #define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list #define PROTMESSID_CLM_TCP_SUPPORTED 1019 // TCP is supported +#define PROTMESSID_CLM_CLIENT_ID 1020 // Client ID associated with TCP connection // special IDs #define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages @@ -158,6 +159,7 @@ class CProtocol : public QObject void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); void CreateCLTcpSupportedMes ( const CHostAddress& InetAddr, const int iID ); + void CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iChanID, CTcpConnection* pTcpConnection ); static int GetBodyLength ( const CVector& vecbyData ); @@ -295,6 +297,7 @@ class CProtocol : public QObject bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLClientIDMes ( const CHostAddress& InetAddr, const CVector& vecData, CTcpConnection* pTcpConnection ); int iOldRecID; int iOldRecCnt; @@ -363,4 +366,5 @@ public slots: void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); void CLTcpSupported ( CHostAddress InetAddr, int iID ); + void CLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpConnection* pTcpConnection ); }; From 8bbeca8a07494567714d1b2d48c73f71044e09d7 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 1 Apr 2026 21:15:53 +0100 Subject: [PATCH 20/41] Add OnClientIDReceived slot to CChannel --- src/channel.cpp | 8 +++++++- src/channel.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/channel.cpp b/src/channel.cpp index 47a492dcb9..0a812a014b 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -81,7 +81,7 @@ CChannel::CChannel ( const bool bNIsServer ) : QObject::connect ( &Protocol, &CProtocol::ChangeChanPan, this, &CChannel::OnChangeChanPan ); - QObject::connect ( &Protocol, &CProtocol::ClientIDReceived, this, &CChannel::ClientIDReceived ); + QObject::connect ( &Protocol, &CProtocol::ClientIDReceived, this, &CChannel::OnClientIDReceived ); QObject::connect ( &Protocol, &CProtocol::RawAudioSupported, this, &CChannel::RawAudioSupported ); @@ -714,3 +714,9 @@ void CChannel::UpdateSocketBufferSize() SetSockBufNumFrames ( SockBuf.GetAutoSetting(), true ); } } + +void CChannel::OnClientIDReceived ( int iChanID ) +{ + qDebug() << Q_FUNC_INFO << "iChanID =" << iChanID; + emit ClientIDReceived ( iChanID ); +} diff --git a/src/channel.h b/src/channel.h index e952909b93..0b41a12577 100644 --- a/src/channel.h +++ b/src/channel.h @@ -265,6 +265,7 @@ public slots: emit DetectedCLMessage ( vecbyMesBodyData, iRecID, RecHostAddr, pTcpConnection ); } + void OnClientIDReceived ( int iChanID ); void OnNewConnection() { emit NewConnection(); } signals: From 5d37602bd0cde7ee96e04952d36ddee93e75dfd6 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 1 Apr 2026 23:07:48 +0100 Subject: [PATCH 21/41] Skeleton support for connected mode TCP --- src/client.cpp | 32 +++++++++++++++++++++++++++++++- src/client.h | 5 +++++ src/server.cpp | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index a35833ab66..29bd5f94d5 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -148,7 +148,7 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &ConnLessProtocol, &CProtocol::CLRedServerListReceived, this, &CClient::CLRedServerListReceived ); - QObject::connect ( &ConnLessProtocol, &CProtocol::CLTcpSupported, this, &CClient::CLTcpSupported ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLTcpSupported, this, &CClient::OnCLTcpSupported ); QObject::connect ( &ConnLessProtocol, &CProtocol::CLConnClientsListMesReceived, this, &CClient::CLConnClientsListMesReceived ); @@ -1019,6 +1019,15 @@ void CClient::OnClientIDReceived ( int iServerChanID ) ClearClientChannels(); } + // if TCP Supported has been received, make TCP connection to server + iClientID = iServerChanID; // for sending back to server over TCP + + if ( bTcpSupported ) + { + // *** Make TCP connection + qDebug() << Q_FUNC_INFO << "need to make TCP connection for" << iClientID; + } + // allocate and map client-side channel 0 int iChanID = FindClientChannel ( iServerChanID, true ); // should always return channel 0 @@ -1054,11 +1063,32 @@ void CClient::OnRawAudioSupported() } } +void CClient::OnCLTcpSupported ( CHostAddress InetAddr, int iID ) +{ + if ( iID != PROTMESSID_CLM_CLIENT_ID ) + { + emit CLTcpSupported ( InetAddr, iID ); // pass to connect dialog + } + + // if client ID already received, make TCP connection to server + bTcpSupported = true; + + if ( iClientID != INVALID_INDEX ) + { + // *** Make TCP connection + qDebug() << Q_FUNC_INFO << "need to make TCP connection for" << iClientID; + } +} + void CClient::Start() { // init object Init(); + // clear TCP info + iClientID = INVALID_INDEX; + bTcpSupported = false; + // initialise client channels ClearClientChannels(); diff --git a/src/client.h b/src/client.h index d37d7a90f4..0f4d543ac0 100644 --- a/src/client.h +++ b/src/client.h @@ -429,6 +429,10 @@ class CClient : public QObject int maxGainOrPanId; int iCurPingTime; + // for TCP protocol support + bool bTcpSupported; + int iClientID; + protected slots: void OnHandledSignal ( int sigNum ); void OnSendProtMessage ( CVector vecMessage ); @@ -448,6 +452,7 @@ protected slots: } } void OnCLPingReceived ( CHostAddress InetAddr, int iMs ); + void OnCLTcpSupported ( CHostAddress InetAddr, int iID ); void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ); diff --git a/src/server.cpp b/src/server.cpp index 3e23c93cae..87aec29eea 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -392,7 +392,7 @@ void CServer::OnNewConnection ( int iChID, int iTotChans, CHostAddress RecHostAd // if TCP is enabled, we need to announce this first, before sending Client ID if ( bEnableTcp ) { - ConnLessProtocol.CreateCLTcpSupportedMes ( vecChannels[iChID].GetAddress(), 0 ); + ConnLessProtocol.CreateCLTcpSupportedMes ( vecChannels[iChID].GetAddress(), PROTMESSID_CLM_CLIENT_ID ); } // inform the client about its own ID at the server (note that this From 8dae6dcb570d8a05af42bdb1c3a2df1791de8501 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 2 Apr 2026 12:43:53 +0100 Subject: [PATCH 22/41] Move TCP debug message from connectdlg to client --- src/client.cpp | 2 ++ src/connectdlg.cpp | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 29bd5f94d5..1c2b905346 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1065,6 +1065,8 @@ void CClient::OnRawAudioSupported() void CClient::OnCLTcpSupported ( CHostAddress InetAddr, int iID ) { + qDebug() << "- TCP supported at server" << InetAddr.toString() << "for ID =" << iID; + if ( iID != PROTMESSID_CLM_CLIENT_ID ) { emit CLTcpSupported ( InetAddr, iID ); // pass to connect dialog diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 956ac32c60..b9a9c093c9 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -549,8 +549,6 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector Date: Sat, 4 Apr 2026 22:10:14 +0100 Subject: [PATCH 25/41] Send client list via TCP when connection available --- src/channel.cpp | 17 +++++++++++++++++ src/channel.h | 2 +- src/server.cpp | 6 +++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/channel.cpp b/src/channel.cpp index 0a812a014b..d3dd5fb87d 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -26,6 +26,7 @@ // CChannel implementation ***************************************************** CChannel::CChannel ( const bool bNIsServer ) : + pTcpConnection ( nullptr ), vecfGains ( MAX_NUM_CHANNELS, 1.0f ), vecfPannings ( MAX_NUM_CHANNELS, 0.5f ), iCurSockBufNumFrames ( INVALID_INDEX ), @@ -720,3 +721,19 @@ void CChannel::OnClientIDReceived ( int iChanID ) qDebug() << Q_FUNC_INFO << "iChanID =" << iChanID; emit ClientIDReceived ( iChanID ); } + +void CChannel::CreateConClientListMes ( const CVector& vecChanInfo, CProtocol& ConnLessProtocol ) +{ + if ( pTcpConnection ) + { + qDebug() << "- sending client list via TCP"; + + ConnLessProtocol.CreateCLConnClientsListMes ( InetAddr, vecChanInfo, pTcpConnection ); + } + else + { + qDebug() << "- sending client list via UDP"; + + Protocol.CreateConClientListMes ( vecChanInfo ); + } +} diff --git a/src/channel.h b/src/channel.h index 0b41a12577..4cc1309a7e 100644 --- a/src/channel.h +++ b/src/channel.h @@ -165,7 +165,7 @@ class CChannel : public QObject void CreateReqChannelLevelListMes() { Protocol.CreateReqChannelLevelListMes(); } //### TODO: END ###// - void CreateConClientListMes ( const CVector& vecChanInfo ) { Protocol.CreateConClientListMes ( vecChanInfo ); } + void CreateConClientListMes ( const CVector& vecChanInfo, CProtocol& ConnLessProtocol ); void CreateRecorderStateMes ( const ERecorderState eRecorderState ) { Protocol.CreateRecorderStateMes ( eRecorderState ); } diff --git a/src/server.cpp b/src/server.cpp index 87aec29eea..15d9ba9ff2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -408,7 +408,7 @@ void CServer::OnNewConnection ( int iChID, int iTotChans, CHostAddress RecHostAd // Send an empty channel list in order to force clients to reset their // audio mixer state. This is required to trigger clients to re-send their // gain levels upon reconnecting after server restarts. - vecChannels[iChID].CreateConClientListMes ( CVector ( 0 ) ); + vecChannels[iChID].CreateConClientListMes ( CVector ( 0 ), ConnLessProtocol ); // query support for split messages in the client vecChannels[iChID].CreateReqSplitMessSupportMes(); @@ -1287,7 +1287,7 @@ void CServer::CreateAndSendChanListForAllConChannels() if ( vecChannels[i].IsConnected() ) { // send message - vecChannels[i].CreateConClientListMes ( vecChanInfo ); + vecChannels[i].CreateConClientListMes ( vecChanInfo, ConnLessProtocol ); } } @@ -1304,7 +1304,7 @@ void CServer::CreateAndSendChanListForThisChan ( const int iCurChanID ) CVector vecChanInfo ( CreateChannelList() ); // now send connected channels list to the channel with the ID "iCurChanID" - vecChannels[iCurChanID].CreateConClientListMes ( vecChanInfo ); + vecChannels[iCurChanID].CreateConClientListMes ( vecChanInfo, ConnLessProtocol ); } void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID, const QString& strChatText ) From 5fb464ea0ddced7418e6a9b89108be3fcaa4da51 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 4 Apr 2026 22:42:08 +0100 Subject: [PATCH 26/41] Add skeleton handler for CLClientID to CServer --- src/server.cpp | 7 +++++++ src/server.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/server.cpp b/src/server.cpp index 15d9ba9ff2..75407269ab 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -284,6 +284,8 @@ CServer::CServer ( const int iNewMaxNumChan, QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqConnClientsList, this, &CServer::OnCLReqConnClientsList ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLClientIDReceived, this, &CServer::OnCLClientIDReceived ); + QObject::connect ( &ServerListManager, &CServerListManager::SvrRegStatusChanged, this, &CServer::SvrRegStatusChanged ); QObject::connect ( &JamController, &recorder::CJamController::RestartRecorder, this, &CServer::RestartRecorder ); @@ -504,6 +506,11 @@ void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM } } +void CServer::OnCLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpConnection* pTcpConnection ) +{ + qDebug() << "- client ID" << iChanID << "received from" << InetAddr.toString() << "with TCP connection" << pTcpConnection; +} + void CServer::OnCLDisconnection ( CHostAddress InetAddr ) { // check if the given address is actually a client which is connected to diff --git a/src/server.h b/src/server.h index 60c8d87501..61fcc3855f 100644 --- a/src/server.h +++ b/src/server.h @@ -402,6 +402,8 @@ public slots: void OnCLDisconnection ( CHostAddress InetAddr ); + void OnCLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpConnection* pTcpConnection ); + void OnAboutToQuit(); void OnHandledSignal ( int sigNum ); From 5486ddc0fcdd4f54315931a25cb0d778187e3f33 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 14:57:21 +0100 Subject: [PATCH 27/41] Add disconnecFromHost method to CTcpConnection --- src/tcpconnection.cpp | 8 ++++++++ src/tcpconnection.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index 490639e0c1..e7e13fb955 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -164,3 +164,11 @@ qint64 CTcpConnection::write ( const char* data, qint64 maxSize ) return pTcpSocket->write ( data, maxSize ); } + +void CTcpConnection::disconnectFromHost() +{ + if ( pTcpSocket ) + { + pTcpSocket->disconnectFromHost(); + } +} diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 0930b4c419..a836030e76 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -50,6 +50,7 @@ class CTcpConnection : public QObject ~CTcpConnection() {} qint64 write ( const char* data, qint64 maxSize ); + void disconnectFromHost(); private: QTcpSocket* pTcpSocket; From 997b368e05ab9f3f917dad548e006f0551a31484 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 17:46:16 +0100 Subject: [PATCH 28/41] Mods to CTcpConnection for future use --- src/client.cpp | 3 ++- src/tcpconnection.cpp | 13 +++++++++---- src/tcpconnection.h | 7 ++++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 28801824eb..cf8ef168f2 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -285,7 +285,8 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM connect ( pSocket, &QTcpSocket::connected, this, [this, pSocket, InetAddr, vecMessage]() { // connection succeeded, give it to a CTcpConnection CTcpConnection* pTcpConnection = - new CTcpConnection ( pSocket, InetAddr, nullptr, &Channel ); // client connection, will self-delete on disconnect + new CTcpConnection ( pSocket, InetAddr, nullptr, &Channel, true ); // client connection, auto-disconn, will self-delete on disconnect + // TODO: do not set bDisconAfterRecv when sending CLM_CLIENT_ID for long-term connection pTcpConnection->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index e7e13fb955..bc8726ee11 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -26,11 +26,16 @@ #include "server.h" #include "channel.h" -CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel ) : +CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, + const CHostAddress& tcpAddress, + CServer* pServer, + CChannel* pChannel, + bool bDisconAfterRecv ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ), pServer ( pServer ), - pChannel ( pChannel ) + pChannel ( pChannel ), + bDisconAfterRecv ( bDisconAfterRecv ) { vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); iPos = 0; @@ -128,8 +133,8 @@ void CTcpConnection::OnReadyRead() emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, tcpAddress, this ); //### TODO: END ###// - // disconnect if we are a client - if ( pChannel ) + // disconnect if it's not a persistent connection + if ( bDisconAfterRecv ) { pTcpSocket->disconnectFromHost(); } diff --git a/src/tcpconnection.h b/src/tcpconnection.h index a836030e76..2d5231356a 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -46,9 +46,12 @@ class CTcpConnection : public QObject Q_OBJECT public: - CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel ); + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bDisconAfterRecv = false ); ~CTcpConnection() {} + void SetChannel ( CChannel* pChan ) { pChannel = pChan; } + CChannel* GetChannel() { return pChannel; } + qint64 write ( const char* data, qint64 maxSize ); void disconnectFromHost(); @@ -60,6 +63,8 @@ class CTcpConnection : public QObject CServer* pServer; CChannel* pChannel; + const bool bDisconAfterRecv; + int iPos; int iPayloadRemain; CVector vecbyRecBuf; From 1573a8cd0d574a3e7e9ddf4a818e8a4354957931 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 17:47:14 +0100 Subject: [PATCH 29/41] In server, link TCP channel to UDP channel by client ID --- src/server.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/server.cpp b/src/server.cpp index 75407269ab..bf24982996 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -509,6 +509,31 @@ void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM void CServer::OnCLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpConnection* pTcpConnection ) { qDebug() << "- client ID" << iChanID << "received from" << InetAddr.toString() << "with TCP connection" << pTcpConnection; + + if ( iChanID < 0 || iChanID >= iMaxNumChannels || !vecChannels[iChanID].IsConnected() ) + { + // ID out of range or channel not connected - reject connection + pTcpConnection->disconnectFromHost(); + qDebug() << "- rejected invalid client ID"; + return; + } + + CChannel* pChannel = &vecChannels[iChanID]; + + qDebug() << "- request to link TCP connection with UDP client at" << pChannel->GetAddress().toString(); + + // compare IP addresses, but not port numbers + if ( InetAddr.InetAddr != pChannel->GetAddress().InetAddr ) + { + // IP address mismatch - reject connection + pTcpConnection->disconnectFromHost(); + qDebug() << "- rejected mismatched IP address"; + return; + } + + // link TCP connection with UDP channel + pTcpConnection->SetChannel ( pChannel ); + pChannel->SetTcpConnection ( pTcpConnection ); } void CServer::OnCLDisconnection ( CHostAddress InetAddr ) From 2d3b54d56fe18e60e21845480a8eed8a42ce1efd Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 22:52:55 +0100 Subject: [PATCH 30/41] Replace boolean TCP flag with multimode enum To provide three modes: UDP, TCP once or TCP long connection. --- src/client.cpp | 14 ++++++++------ src/client.h | 10 +++++----- src/clientdlg.h | 6 +++--- src/clientrpc.cpp | 2 +- src/connectdlg.cpp | 10 +++++----- src/connectdlg.h | 4 ++-- src/protocol.cpp | 12 ++++++------ src/protocol.h | 16 ++++++++++++---- src/server.cpp | 4 ++-- src/server.h | 2 +- src/testbench.h | 4 ++-- 11 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index cf8ef168f2..24b36b830f 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -254,7 +254,7 @@ void CClient::OnSendProtMessage ( CVector vecMessage ) Socket.SendPacket ( vecMessage, Channel.GetAddress() ); } -void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ) +void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, enum EProtoMode eProtoMode ) { if ( pTcpConnection ) { @@ -264,7 +264,7 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM // the protocol queries me to call the function to send the message // send it through the network - if ( bUseTcpClient ) + if ( eProtoMode != PROTO_UDP ) { // create a TCP client connection and send message QTcpSocket* pSocket = new QTcpSocket ( this ); @@ -282,11 +282,13 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM pSocket->deleteLater(); } ); - connect ( pSocket, &QTcpSocket::connected, this, [this, pSocket, InetAddr, vecMessage]() { + connect ( pSocket, &QTcpSocket::connected, this, [this, pSocket, InetAddr, vecMessage, eProtoMode]() { // connection succeeded, give it to a CTcpConnection - CTcpConnection* pTcpConnection = - new CTcpConnection ( pSocket, InetAddr, nullptr, &Channel, true ); // client connection, auto-disconn, will self-delete on disconnect - // TODO: do not set bDisconAfterRecv when sending CLM_CLIENT_ID for long-term connection + CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, + InetAddr, + nullptr, + &Channel, + eProtoMode == PROTO_TCP_ONCE ); // client connection, will self-delete on disconnect pTcpConnection->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); diff --git a/src/client.h b/src/client.h index 0f4d543ac0..36abaa3fea 100644 --- a/src/client.h +++ b/src/client.h @@ -278,14 +278,14 @@ class CClient : public QObject void CreateCLServerListReqVerAndOSMes ( const CHostAddress& InetAddr ) { ConnLessProtocol.CreateCLReqVersionAndOSMes ( InetAddr ); } - void CreateCLServerListReqConnClientsListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) + void CreateCLServerListReqConnClientsListMes ( const CHostAddress& InetAddr, enum EProtoMode eProtoMode ) { - ConnLessProtocol.CreateCLReqConnClientsListMes ( InetAddr, bUseTcpClient ); + ConnLessProtocol.CreateCLReqConnClientsListMes ( InetAddr, eProtoMode ); } - void CreateCLReqServerListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) + void CreateCLReqServerListMes ( const CHostAddress& InetAddr, enum EProtoMode eProtoMode ) { - ConnLessProtocol.CreateCLReqServerListMes ( InetAddr, bUseTcpClient ); + ConnLessProtocol.CreateCLReqServerListMes ( InetAddr, eProtoMode ); } int EstimatedOverallDelay ( const int iPingTimeMs ); @@ -454,7 +454,7 @@ protected slots: void OnCLPingReceived ( CHostAddress InetAddr, int iMs ); void OnCLTcpSupported ( CHostAddress InetAddr, int iID ); - void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ); + void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, enum EProtoMode eProtoMode ); void OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, int iMs, int iNumClients ); diff --git a/src/clientdlg.h b/src/clientdlg.h index 64ddb86106..d4a2a7d100 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -202,15 +202,15 @@ public slots: void OnNewLocalInputText ( QString strChatText ) { pClient->CreateChatTextMes ( strChatText ); } - void OnReqServerListQuery ( CHostAddress InetAddr, bool bUseTcpClient ) { pClient->CreateCLReqServerListMes ( InetAddr, bUseTcpClient ); } + void OnReqServerListQuery ( CHostAddress InetAddr, enum EProtoMode eProtoMode ) { pClient->CreateCLReqServerListMes ( InetAddr, eProtoMode ); } void OnCreateCLServerListPingMes ( CHostAddress InetAddr ) { pClient->CreateCLServerListPingMes ( InetAddr ); } void OnCreateCLServerListReqVerAndOSMes ( CHostAddress InetAddr ) { pClient->CreateCLServerListReqVerAndOSMes ( InetAddr ); } - void OnCreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr, bool bUseTcpClient ) + void OnCreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr, enum EProtoMode eProtoMode ) { - pClient->CreateCLServerListReqConnClientsListMes ( InetAddr, bUseTcpClient ); + pClient->CreateCLServerListReqConnClientsListMes ( InetAddr, eProtoMode ); } void OnCLServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ) diff --git a/src/clientrpc.cpp b/src/clientrpc.cpp index 367f2e3669..09fae6a9a4 100644 --- a/src/clientrpc.cpp +++ b/src/clientrpc.cpp @@ -171,7 +171,7 @@ CClientRpc::CClientRpc ( CClient* pClient, CClientSettings* pSettings, CRpcServe if ( NetworkUtil::ParseNetworkAddress ( jsonDirectoryIp.toString(), haDirectoryAddress, false ) ) { // send the request for the server list - pClient->CreateCLReqServerListMes ( haDirectoryAddress, false ); // UDP + pClient->CreateCLReqServerListMes ( haDirectoryAddress, PROTO_UDP ); response["result"] = "ok"; } else diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index b9a9c093c9..107d86419c 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -336,7 +336,7 @@ void CConnectDlg::RequestServerList() false ) ) { // send the request for the server list - emit ReqServerListQuery ( haDirectoryAddress, false ); // UDP + emit ReqServerListQuery ( haDirectoryAddress, PROTO_UDP ); // start timer, if this message did not get any respond to retransmit // the server list request message @@ -379,7 +379,7 @@ void CConnectDlg::OnTimerReRequestServList() { // note that this is a connection less message which may get lost // and therefore it makes sense to re-transmit it - emit ReqServerListQuery ( haDirectoryAddress, false ); // UDP + emit ReqServerListQuery ( haDirectoryAddress, PROTO_UDP ); } } @@ -557,7 +557,7 @@ void CConnectDlg::SetTcpSupported ( const CHostAddress& InetAddr, int iID ) if ( !bServerListReceived ) { // send the request for the server list - emit ReqServerListQuery ( InetAddr, true ); // TCP + emit ReqServerListQuery ( InetAddr, PROTO_TCP_ONCE ); // Close TCP connection after receiving reply } break; @@ -578,7 +578,7 @@ void CConnectDlg::SetTcpSupported ( const CHostAddress& InetAddr, int iID ) eFetchMode = CFM_TCP; pCurListViewItem->setData ( LVC_CLIENTS, Qt::UserRole, eFetchMode ); // remember for future fetches - emit CreateCLServerListReqConnClientsListMes ( InetAddr, true ); // TCP + emit CreateCLServerListReqConnClientsListMes ( InetAddr, PROTO_TCP_ONCE ); // Close TCP connection after receiving reply } } } @@ -1051,7 +1051,7 @@ void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, eFetchMode = CFM_UDP_REQUEST; pCurListViewItem->setData ( LVC_CLIENTS, Qt::UserRole, eFetchMode ); } - emit CreateCLServerListReqConnClientsListMes ( InetAddr, eFetchMode == CFM_TCP ); // UDP or TCP + emit CreateCLServerListReqConnClientsListMes ( InetAddr, eFetchMode == CFM_TCP ? PROTO_TCP_ONCE : PROTO_UDP ); } // this is the first time a ping time was received, set item to visible diff --git a/src/connectdlg.h b/src/connectdlg.h index b3d901dee7..7ffcdcd5bd 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -146,8 +146,8 @@ public slots: void OnCurrentServerItemChanged ( QTreeWidgetItem* current, QTreeWidgetItem* previous ); signals: - void ReqServerListQuery ( CHostAddress InetAddr, bool bUseTcpClient ); + void ReqServerListQuery ( CHostAddress InetAddr, enum EProtoMode eProtoMode ); void CreateCLServerListPingMes ( CHostAddress InetAddr ); void CreateCLServerListReqVerAndOSMes ( CHostAddress InetAddr ); - void CreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr, bool bUseTcpClient ); + void CreateCLServerListReqConnClientsListMes ( CHostAddress InetAddr, enum EProtoMode eProtoMode ); }; diff --git a/src/protocol.cpp b/src/protocol.cpp index af012c6354..bdd4576b02 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -641,7 +641,7 @@ void CProtocol::CreateAndImmSendConLessMessage ( const int iID, const CVector& vecData, const CHostAddress& InetAddr, CTcpConnection* pTcpConnection, - bool bUseTcpClient ) + enum EProtoMode eProtoMode ) { CVector vecNewMessage; @@ -650,7 +650,7 @@ void CProtocol::CreateAndImmSendConLessMessage ( const int iID, GenMessageFrame ( vecNewMessage, 0, iID, vecData ); // immediately send message - emit CLMessReadyForSending ( InetAddr, vecNewMessage, pTcpConnection, bUseTcpClient ); + emit CLMessReadyForSending ( InetAddr, vecNewMessage, pTcpConnection, eProtoMode ); } void CProtocol::ParseMessageBody ( const CVector& vecbyMesBodyData, const int iRecCounter, const int iRecID ) @@ -2281,9 +2281,9 @@ bool CProtocol::EvaluateCLRedServerListMes ( const CHostAddress& InetAddr, const return false; // no error } -void CProtocol::CreateCLReqServerListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) +void CProtocol::CreateCLReqServerListMes ( const CHostAddress& InetAddr, enum EProtoMode eProtoMode ) { - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_SERVER_LIST, CVector ( 0 ), InetAddr, nullptr, bUseTcpClient ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_SERVER_LIST, CVector ( 0 ), InetAddr, nullptr, eProtoMode ); } bool CProtocol::EvaluateCLReqServerListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) @@ -2534,9 +2534,9 @@ bool CProtocol::EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, con return false; // no error } -void CProtocol::CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ) +void CProtocol::CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr, enum EProtoMode eProtoMode ) { - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST, CVector ( 0 ), InetAddr, nullptr, bUseTcpClient ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST, CVector ( 0 ), InetAddr, nullptr, eProtoMode ); } bool CProtocol::EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) diff --git a/src/protocol.h b/src/protocol.h index ff24d5f92a..d32d6634e7 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -102,6 +102,14 @@ #define MESS_SPLIT_PART_SIZE_BYTES 550 #define MAX_NUM_MESS_SPLIT_PARTS ( MAX_SIZE_BYTES_NETW_BUF / MESS_SPLIT_PART_SIZE_BYTES ) +/* Enum for protocol mode *****************************************************/ +enum EProtoMode +{ + PROTO_UDP, + PROTO_TCP_ONCE, + PROTO_TCP_LONG, +}; + /* Classes ********************************************************************/ class CProtocol : public QObject { @@ -148,14 +156,14 @@ class CProtocol : public QObject void CreateCLUnregisterServerMes ( const CHostAddress& InetAddr ); void CreateCLServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo, CTcpConnection* pTcpConnection ); void CreateCLRedServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo ); - void CreateCLReqServerListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ); + void CreateCLReqServerListMes ( const CHostAddress& InetAddr, enum EProtoMode eProtoMode ); void CreateCLSendEmptyMesMes ( const CHostAddress& InetAddr, const CHostAddress& TargetInetAddr ); void CreateCLEmptyMes ( const CHostAddress& InetAddr ); void CreateCLDisconnection ( const CHostAddress& InetAddr ); void CreateCLVersionAndOSMes ( const CHostAddress& InetAddr ); void CreateCLReqVersionAndOSMes ( const CHostAddress& InetAddr ); void CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecChanInfo, CTcpConnection* pTcpConnection ); - void CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr, bool bUseTcpClient ); + void CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr, enum EProtoMode eProtoMode ); void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); void CreateCLTcpSupportedMes ( const CHostAddress& InetAddr, const int iID ); @@ -257,7 +265,7 @@ class CProtocol : public QObject const CVector& vecData, const CHostAddress& InetAddr, CTcpConnection* pTcpConnection = nullptr, - bool bUseTcpClient = false ); + enum EProtoMode eProtoMode = PROTO_UDP ); bool EvaluateJitBufMes ( const CVector& vecData ); bool EvaluateReqJitBufMes(); @@ -320,7 +328,7 @@ public slots: signals: // transmitting void MessReadyForSending ( CVector vecMessage ); - void CLMessReadyForSending ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ); + void CLMessReadyForSending ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, enum EProtoMode eProtoMode ); // receiving void ChangeJittBufSize ( int iNewJitBufSize ); diff --git a/src/server.cpp b/src/server.cpp index bf24982996..e6701717f5 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -485,9 +485,9 @@ void CServer::OnServerFull ( CHostAddress RecHostAddr ) ConnLessProtocol.CreateCLServerFullMes ( RecHostAddr ); } -void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ) +void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, enum EProtoMode eProtoMode ) { - if ( bUseTcpClient ) + if ( eProtoMode != PROTO_UDP ) { qWarning() << "Server send cannot use TCP client"; return; diff --git a/src/server.h b/src/server.h index 61fcc3855f..51608c449c 100644 --- a/src/server.h +++ b/src/server.h @@ -345,7 +345,7 @@ public slots: void OnServerFull ( CHostAddress RecHostAddr ); - void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, bool bUseTcpClient ); + void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, enum EProtoMode eProtoMode ); void OnProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr, CTcpConnection* pTcpConnection ); diff --git a/src/testbench.h b/src/testbench.h index 6191a8659e..c991db07be 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -222,7 +222,7 @@ public slots: break; case 20: // PROTMESSID_CLM_REQ_SERVER_LIST - Protocol.CreateCLReqServerListMes ( CurHostAddress, false ); + Protocol.CreateCLReqServerListMes ( CurHostAddress, PROTO_UDP ); break; case 21: // PROTMESSID_CLM_SEND_EMPTY_MESSAGE @@ -265,7 +265,7 @@ public slots: break; case 29: // PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST - Protocol.CreateCLReqConnClientsListMes ( CurHostAddress, false ); + Protocol.CreateCLReqConnClientsListMes ( CurHostAddress, PROTO_UDP ); break; case 30: // PROTMESSID_CLM_CHANNEL_LEVEL_LIST From 01530e07b29fbf8696511c96ecfe1cb0398c8e9d Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 6 Apr 2026 11:02:40 +0100 Subject: [PATCH 31/41] Add creation of session-long TCP connection --- src/client.cpp | 3 +++ src/protocol.cpp | 4 ++-- src/protocol.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 24b36b830f..c59da5a2bc 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1029,6 +1029,7 @@ void CClient::OnClientIDReceived ( int iServerChanID ) { // *** Make TCP connection qDebug() << Q_FUNC_INFO << "need to make TCP connection for" << iClientID; + ConnLessProtocol.CreateCLClientIDMes ( Channel.GetAddress(), iClientID, PROTO_TCP_LONG ); // create persistent TCP connection } // allocate and map client-side channel 0 @@ -1083,6 +1084,8 @@ void CClient::OnCLTcpSupported ( CHostAddress InetAddr, int iID ) { // *** Make TCP connection qDebug() << Q_FUNC_INFO << "need to make TCP connection for" << iClientID; + Q_ASSERT ( InetAddr == Channel.GetAddress() ); + ConnLessProtocol.CreateCLClientIDMes ( InetAddr, iClientID, PROTO_TCP_LONG ); // create persistent TCP connection } } diff --git a/src/protocol.cpp b/src/protocol.cpp index bdd4576b02..4e67e72181 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2669,7 +2669,7 @@ bool CProtocol::EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr, const return false; // no error } -void CProtocol::CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iChanID, CTcpConnection* pTcpConnection ) +void CProtocol::CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iChanID, enum EProtoMode eProtoMode ) { int iPos = 0; // init position pointer @@ -2679,7 +2679,7 @@ void CProtocol::CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iC // channel ID (1 byte) PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_CLIENT_ID, vecData, InetAddr, pTcpConnection ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_CLIENT_ID, vecData, InetAddr, nullptr, eProtoMode ); } bool CProtocol::EvaluateCLClientIDMes ( const CHostAddress& InetAddr, const CVector& vecData, CTcpConnection* pTcpConnection ) diff --git a/src/protocol.h b/src/protocol.h index d32d6634e7..083e4e5ee3 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -167,7 +167,7 @@ class CProtocol : public QObject void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecLevelList, const int iNumClients ); void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); void CreateCLTcpSupportedMes ( const CHostAddress& InetAddr, const int iID ); - void CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iChanID, CTcpConnection* pTcpConnection ); + void CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iChanID, enum EProtoMode eProtoMode ); static int GetBodyLength ( const CVector& vecbyData ); From 245fb40b9ce7171b21c4a7762177452a4ad6434f Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 6 Apr 2026 22:48:50 +0100 Subject: [PATCH 32/41] Route CLConnClientList depending on whether connected --- src/client.cpp | 16 +++++++++++++++- src/client.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index c59da5a2bc..6abc776d12 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -150,7 +150,7 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &ConnLessProtocol, &CProtocol::CLTcpSupported, this, &CClient::OnCLTcpSupported ); - QObject::connect ( &ConnLessProtocol, &CProtocol::CLConnClientsListMesReceived, this, &CClient::CLConnClientsListMesReceived ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLConnClientsListMesReceived, this, &CClient::OnCLConnClientsListMesReceived ); QObject::connect ( &ConnLessProtocol, &CProtocol::CLPingReceived, this, &CClient::OnCLPingReceived ); @@ -1089,6 +1089,20 @@ void CClient::OnCLTcpSupported ( CHostAddress InetAddr, int iID ) } } +void CClient::OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ) +{ + // test if we are receiving for the connect dialog or a connected session + qDebug() << Q_FUNC_INFO << "Channel.IsConnected() =" << Channel.IsConnected(); + if ( Channel.IsConnected() ) + { + OnConClientListMesReceived ( vecChanInfo ); // connected session + } + else + { + emit CLConnClientsListMesReceived ( InetAddr, vecChanInfo ); // connect dialog + } +} + void CClient::Start() { // init object diff --git a/src/client.h b/src/client.h index 36abaa3fea..2ab7108cf2 100644 --- a/src/client.h +++ b/src/client.h @@ -469,6 +469,7 @@ protected slots: void OnMuteStateHasChangedReceived ( int iServerChanID, bool bIsMuted ); void OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void OnConClientListMesReceived ( CVector vecChanInfo ); + void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); signals: void ConClientListMesReceived ( CVector vecChanInfo ); From f6eb414d4880a620f5a992ebe729f72f99991655 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 7 Apr 2026 10:36:41 +0100 Subject: [PATCH 33/41] Be specific about bDisconAfterRecv for TCP --- src/tcpconnection.h | 2 +- src/tcpserver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 2d5231356a..bdb7f4dd0f 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -46,7 +46,7 @@ class CTcpConnection : public QObject Q_OBJECT public: - CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bDisconAfterRecv = false ); + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bDisconAfterRecv ); ~CTcpConnection() {} void SetChannel ( CChannel* pChan ) { pChannel = pChan; } diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index be5251dc1c..3c84e4894b 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -97,5 +97,5 @@ void CTcpServer::OnNewConnection() qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.toString(); - new CTcpConnection ( pSocket, peerAddress, pServer, nullptr ); // will auto-delete on disconnect + new CTcpConnection ( pSocket, peerAddress, pServer, nullptr, false ); // will auto-delete on disconnect } From 468b7d0b13db2e8aee23f4f7badc3369b5146f6b Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 7 Apr 2026 17:58:59 +0100 Subject: [PATCH 34/41] Rework TCP session-mode connection --- src/client.cpp | 22 ++++++++++++++++++---- src/client.h | 2 +- src/protocol.cpp | 6 +++--- src/protocol.h | 4 ++-- src/tcpconnection.cpp | 16 ++++++++-------- src/tcpconnection.h | 6 ++++-- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 6abc776d12..e27b97fc75 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -288,7 +288,12 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM InetAddr, nullptr, &Channel, - eProtoMode == PROTO_TCP_ONCE ); // client connection, will self-delete on disconnect + eProtoMode == PROTO_TCP_LONG ); // client connection, will self-delete on disconnect + + if ( eProtoMode == PROTO_TCP_LONG ) + { + Channel.SetTcpConnection ( pTcpConnection ); // link session connection with channel + } pTcpConnection->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); @@ -1089,16 +1094,17 @@ void CClient::OnCLTcpSupported ( CHostAddress InetAddr, int iID ) } } -void CClient::OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ) +void CClient::OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo, CTcpConnection* pTcpConnection ) { // test if we are receiving for the connect dialog or a connected session - qDebug() << Q_FUNC_INFO << "Channel.IsConnected() =" << Channel.IsConnected(); - if ( Channel.IsConnected() ) + if ( pTcpConnection && pTcpConnection->IsSession() ) { + qDebug() << "- sending client list to client dialog"; OnConClientListMesReceived ( vecChanInfo ); // connected session } else { + qDebug() << "- sending client list to connect dialog"; emit CLConnClientsListMesReceived ( InetAddr, vecChanInfo ); // connect dialog } } @@ -1132,6 +1138,14 @@ void CClient::Stop() // stop audio interface Sound.Stop(); + // close any session TCP connection + CTcpConnection* pTcpConnection = Channel.GetTcpConnection(); + if ( pTcpConnection ) + { + Channel.SetTcpConnection ( nullptr ); + pTcpConnection->disconnectFromHost(); + } + // disable channel Channel.SetEnable ( false ); diff --git a/src/client.h b/src/client.h index 2ab7108cf2..6299fd24f9 100644 --- a/src/client.h +++ b/src/client.h @@ -469,7 +469,7 @@ protected slots: void OnMuteStateHasChangedReceived ( int iServerChanID, bool bIsMuted ); void OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void OnConClientListMesReceived ( CVector vecChanInfo ); - void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); + void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo, CTcpConnection* pTcpConnection ); signals: void ConClientListMesReceived ( CVector vecChanInfo ); diff --git a/src/protocol.cpp b/src/protocol.cpp index 4e67e72181..5593de8507 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -945,7 +945,7 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe break; case PROTMESSID_CLM_CONN_CLIENTS_LIST: - EvaluateCLConnClientsListMes ( InetAddr, vecbyMesBodyData ); + EvaluateCLConnClientsListMes ( InetAddr, vecbyMesBodyData, pTcpConnection ); break; case PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST: @@ -2475,7 +2475,7 @@ void CProtocol::CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const CreateAndImmSendConLessMessage ( PROTMESSID_CLM_CONN_CLIENTS_LIST, vecData, InetAddr, pTcpConnection ); } -bool CProtocol::EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData ) +bool CProtocol::EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData, CTcpConnection* pTcpConnection ) { int iPos = 0; // init position pointer const int iDataLen = vecData.Size(); @@ -2529,7 +2529,7 @@ bool CProtocol::EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, con } // invoke message action - emit CLConnClientsListMesReceived ( InetAddr, vecChanInfo ); + emit CLConnClientsListMesReceived ( InetAddr, vecChanInfo, pTcpConnection ); return false; // no error } diff --git a/src/protocol.h b/src/protocol.h index 083e4e5ee3..8e005ef1e0 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -300,7 +300,7 @@ class CProtocol : public QObject bool EvaluateCLDisconnectionMes ( const CHostAddress& InetAddr ); bool EvaluateCLVersionAndOSMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLReqVersionAndOSMes ( const CHostAddress& InetAddr ); - bool EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, const CVector& vecData, CTcpConnection* pTcpConnection ); bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ); bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, const CVector& vecData ); @@ -369,7 +369,7 @@ public slots: void CLDisconnection ( CHostAddress InetAddr ); void CLVersionAndOSReceived ( CHostAddress InetAddr, COSUtil::EOpSystemType eOSType, QString strVersion ); void CLReqVersionAndOS ( CHostAddress InetAddr ); - void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); + void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo, CTcpConnection* pTcpConnection ); void CLReqConnClientsList ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ); void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index bc8726ee11..4223557a60 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -26,16 +26,12 @@ #include "server.h" #include "channel.h" -CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, - const CHostAddress& tcpAddress, - CServer* pServer, - CChannel* pChannel, - bool bDisconAfterRecv ) : +CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bIsSession ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ), pServer ( pServer ), pChannel ( pChannel ), - bDisconAfterRecv ( bDisconAfterRecv ) + bIsSession ( bIsSession ) { vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); iPos = 0; @@ -59,6 +55,10 @@ void CTcpConnection::OnDisconnected() { qDebug() << "- Jamulus-TCP: disconnected from:" << tcpAddress.toString(); pTcpSocket->deleteLater(); + if ( pChannel && pChannel->GetTcpConnection() == this ) + { + pChannel->SetTcpConnection ( nullptr ); // unlink from channel + } deleteLater(); // delete this object in the next event loop } @@ -133,8 +133,8 @@ void CTcpConnection::OnReadyRead() emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, tcpAddress, this ); //### TODO: END ###// - // disconnect if it's not a persistent connection - if ( bDisconAfterRecv ) + // disconnect if it's not a client session connection + if ( !pServer && !bIsSession ) { pTcpSocket->disconnectFromHost(); } diff --git a/src/tcpconnection.h b/src/tcpconnection.h index bdb7f4dd0f..3537c2550e 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -46,7 +46,7 @@ class CTcpConnection : public QObject Q_OBJECT public: - CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bDisconAfterRecv ); + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bIsSession ); ~CTcpConnection() {} void SetChannel ( CChannel* pChan ) { pChannel = pChan; } @@ -55,6 +55,8 @@ class CTcpConnection : public QObject qint64 write ( const char* data, qint64 maxSize ); void disconnectFromHost(); + bool IsSession() { return bIsSession; } + private: QTcpSocket* pTcpSocket; CHostAddress tcpAddress; @@ -63,7 +65,7 @@ class CTcpConnection : public QObject CServer* pServer; CChannel* pChannel; - const bool bDisconAfterRecv; + const bool bIsSession; int iPos; int iPayloadRemain; From ac977ae6f05bfb8f825e851186559fb475ea1c1e Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 7 Apr 2026 18:06:52 +0100 Subject: [PATCH 35/41] Minor comment updates --- src/connectdlg.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 107d86419c..a01a35d132 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -552,12 +552,12 @@ void CConnectDlg::SetTcpSupported ( const CHostAddress& InetAddr, int iID ) switch ( iID ) { case PROTMESSID_CLM_SERVER_LIST: - // if we haven't received the serverlist, it must have got lost due to fragmentation + // if we haven't received the serverlist, it might have got lost due to fragmentation // retry using TCP instead if ( !bServerListReceived ) { // send the request for the server list - emit ReqServerListQuery ( InetAddr, PROTO_TCP_ONCE ); // Close TCP connection after receiving reply + emit ReqServerListQuery ( InetAddr, PROTO_TCP_ONCE ); // close TCP connection after receiving reply } break; @@ -578,7 +578,7 @@ void CConnectDlg::SetTcpSupported ( const CHostAddress& InetAddr, int iID ) eFetchMode = CFM_TCP; pCurListViewItem->setData ( LVC_CLIENTS, Qt::UserRole, eFetchMode ); // remember for future fetches - emit CreateCLServerListReqConnClientsListMes ( InetAddr, PROTO_TCP_ONCE ); // Close TCP connection after receiving reply + emit CreateCLServerListReqConnClientsListMes ( InetAddr, PROTO_TCP_ONCE ); // close TCP connection after receiving reply } } } From 19fe1d0c93865a8612853a30e92a44585d0fac6e Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 8 Apr 2026 20:44:21 +0100 Subject: [PATCH 36/41] Add support for sending Empty Message over TCP --- src/protocol.cpp | 4 ++-- src/protocol.h | 2 +- src/server.h | 2 +- src/serverlist.cpp | 4 ++-- src/testbench.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/protocol.cpp b/src/protocol.cpp index 5593de8507..eef7ef6a7e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2332,11 +2332,11 @@ bool CProtocol::EvaluateCLSendEmptyMesMes ( const CVector& vecData ) return false; // no error } -void CProtocol::CreateCLEmptyMes ( const CHostAddress& InetAddr ) +void CProtocol::CreateCLEmptyMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ) { // special message: for this message there exist no Evaluate // function - CreateAndImmSendConLessMessage ( PROTMESSID_CLM_EMPTY_MESSAGE, CVector ( 0 ), InetAddr ); + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_EMPTY_MESSAGE, CVector ( 0 ), InetAddr, pTcpConnection ); } void CProtocol::CreateCLDisconnection ( const CHostAddress& InetAddr ) diff --git a/src/protocol.h b/src/protocol.h index 8e005ef1e0..526593539e 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -158,7 +158,7 @@ class CProtocol : public QObject void CreateCLRedServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo ); void CreateCLReqServerListMes ( const CHostAddress& InetAddr, enum EProtoMode eProtoMode ); void CreateCLSendEmptyMesMes ( const CHostAddress& InetAddr, const CHostAddress& TargetInetAddr ); - void CreateCLEmptyMes ( const CHostAddress& InetAddr ); + void CreateCLEmptyMes ( const CHostAddress& InetAddr, CTcpConnection* pTcpConnection ); void CreateCLDisconnection ( const CHostAddress& InetAddr ); void CreateCLVersionAndOSMes ( const CHostAddress& InetAddr ); void CreateCLReqVersionAndOSMes ( const CHostAddress& InetAddr ); diff --git a/src/server.h b/src/server.h index 51608c449c..c93ca5cfa3 100644 --- a/src/server.h +++ b/src/server.h @@ -363,7 +363,7 @@ public slots: // only send empty message if not a directory if ( !ServerListManager.IsDirectory() ) { - ConnLessProtocol.CreateCLEmptyMes ( TargetInetAddr ); + ConnLessProtocol.CreateCLEmptyMes ( TargetInetAddr, nullptr ); } } diff --git a/src/serverlist.cpp b/src/serverlist.cpp index bf6dd73192..334d003210 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -523,7 +523,7 @@ void CServerListManager::OnTimerPingServerInList() for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ ) { // send empty message to keep NAT port open at registered server - pConnLessProtocol->CreateCLEmptyMes ( ServerList[iIdx].HostAddr ); + pConnLessProtocol->CreateCLEmptyMes ( ServerList[iIdx].HostAddr, nullptr ); } } @@ -925,7 +925,7 @@ void CServerListManager::OnTimerPingServers() { // send empty message to directory to keep NAT port open -> we do // not require any answer from the directory - pConnLessProtocol->CreateCLEmptyMes ( DirectoryAddress ); + pConnLessProtocol->CreateCLEmptyMes ( DirectoryAddress, nullptr ); } } diff --git a/src/testbench.h b/src/testbench.h index c991db07be..03d3dd2fd7 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -230,7 +230,7 @@ public slots: break; case 22: // PROTMESSID_CLM_EMPTY_MESSAGE - Protocol.CreateCLEmptyMes ( CurHostAddress ); + Protocol.CreateCLEmptyMes ( CurHostAddress, nullptr ); break; case 23: // PROTMESSID_CLM_DISCONNECTION From 774c0f3bb9909c0d302011ccd40f3d6639f3f436 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 11:45:40 +0100 Subject: [PATCH 37/41] Implement keepalive over session long TCP connection --- src/client.cpp | 4 +++- src/client.h | 6 ++++++ src/tcpconnection.cpp | 24 +++++++++++++++++++++++- src/tcpconnection.h | 14 +++++++++++++- src/tcpserver.cpp | 2 +- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index e27b97fc75..b2864011e6 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -258,7 +258,8 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM { if ( pTcpConnection ) { - qWarning() << "Client send cannot use TCP server"; + // already have TCP connection - just send and return + pTcpConnection->write ( (const char*) &( (CVector) vecMessage )[0], vecMessage.Size() ); return; } @@ -287,6 +288,7 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, InetAddr, nullptr, + this, &Channel, eProtoMode == PROTO_TCP_LONG ); // client connection, will self-delete on disconnect diff --git a/src/client.h b/src/client.h index 6299fd24f9..be463eada2 100644 --- a/src/client.h +++ b/src/client.h @@ -471,6 +471,12 @@ protected slots: void OnConClientListMesReceived ( CVector vecChanInfo ); void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo, CTcpConnection* pTcpConnection ); +public slots: + void OnCLSendEmptyMes ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ) + { + ConnLessProtocol.CreateCLEmptyMes ( InetAddr, pTcpConnection ); + } + signals: void ConClientListMesReceived ( CVector vecChanInfo ); void ChatTextReceived ( QString strChatText ); diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index 4223557a60..e87ae9c509 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -24,12 +24,19 @@ #include "protocol.h" #include "server.h" +#include "client.h" #include "channel.h" -CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bIsSession ) : +CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, + const CHostAddress& tcpAddress, + CServer* pServer, + CClient* pClient, + CChannel* pChannel, + bool bIsSession ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ), pServer ( pServer ), + pClient ( pClient ), pChannel ( pChannel ), bIsSession ( bIsSession ) { @@ -49,11 +56,20 @@ CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcp { connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pChannel, &CChannel::OnProtocolCLMessageReceived ); } + + if ( pClient && bIsSession ) + { + // set up keepalive CLM_EMPTY_MESSAGE over TCP session connection + connect ( this, &CTcpConnection::CLSendEmptyMes, pClient, &CClient::OnCLSendEmptyMes ); + connect ( &TimerKeepalive, &QTimer::timeout, this, &CTcpConnection::OnTimerKeepalive ); + TimerKeepalive.start ( TCP_KEEPALIVE_INTERVAL_MS ); + } } void CTcpConnection::OnDisconnected() { qDebug() << "- Jamulus-TCP: disconnected from:" << tcpAddress.toString(); + TimerKeepalive.stop(); pTcpSocket->deleteLater(); if ( pChannel && pChannel->GetTcpConnection() == this ) { @@ -160,6 +176,12 @@ void CTcpConnection::OnReadyRead() qDebug() << "- end of readyRead(), bytesAvailable() =" << pTcpSocket->bytesAvailable(); } +void CTcpConnection::OnTimerKeepalive() +{ + // qDebug() << "- Keepalive timer" << this << "to TCP" << tcpAddress.toString(); + emit CLSendEmptyMes ( tcpAddress, this ); +} + qint64 CTcpConnection::write ( const char* data, qint64 maxSize ) { if ( !pTcpSocket ) diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 3537c2550e..4c8bda3ee9 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -40,13 +40,20 @@ class CServer; // forward declaration of CServer class CChannel; // forward declaration of CChannel +#define TCP_KEEPALIVE_INTERVAL_MS 15000 + /* Classes ********************************************************************/ class CTcpConnection : public QObject { Q_OBJECT public: - CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer, CChannel* pChannel, bool bIsSession ); + CTcpConnection ( QTcpSocket* pTcpSocket, + const CHostAddress& tcpAddress, + CServer* pServer, + CClient* pClient, + CChannel* pChannel, + bool bIsSession ); ~CTcpConnection() {} void SetChannel ( CChannel* pChan ) { pChannel = pChan; } @@ -63,6 +70,7 @@ class CTcpConnection : public QObject CHostAddress udpAddress; CServer* pServer; + CClient* pClient; CChannel* pChannel; const bool bIsSession; @@ -71,10 +79,14 @@ class CTcpConnection : public QObject int iPayloadRemain; CVector vecbyRecBuf; + QTimer TimerKeepalive; + signals: void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); + void CLSendEmptyMes ( CHostAddress InetAddr, CTcpConnection* pTcpConnection ); private slots: void OnDisconnected(); void OnReadyRead(); + void OnTimerKeepalive(); }; diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index 3c84e4894b..7e178531b0 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -97,5 +97,5 @@ void CTcpServer::OnNewConnection() qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.toString(); - new CTcpConnection ( pSocket, peerAddress, pServer, nullptr, false ); // will auto-delete on disconnect + new CTcpConnection ( pSocket, peerAddress, pServer, nullptr, nullptr, false ); // will auto-delete on disconnect } From a6777488cf6560447166ade2fd134a3936a46a61 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 14:49:30 +0100 Subject: [PATCH 38/41] Clarify comment --- src/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index b2864011e6..240b6443e3 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1029,7 +1029,7 @@ void CClient::OnClientIDReceived ( int iServerChanID ) ClearClientChannels(); } - // if TCP Supported has been received, make TCP connection to server + // if TCP Supported has already been received, make TCP connection to server iClientID = iServerChanID; // for sending back to server over TCP if ( bTcpSupported ) From 5414b03a04b9a60f1b74f01f5c65e25d1359e945 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 17:36:11 +0100 Subject: [PATCH 39/41] Make CTcpConnection work in serveronly mode. Constructor for CTcpConnection made polymorphic for client and server. --- src/client.cpp | 1 - src/tcpconnection.cpp | 47 ++++++++++++++++++++++++++----------------- src/tcpconnection.h | 12 ++++------- src/tcpserver.cpp | 2 +- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 240b6443e3..19ebf3b93e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -287,7 +287,6 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM // connection succeeded, give it to a CTcpConnection CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, InetAddr, - nullptr, this, &Channel, eProtoMode == PROTO_TCP_LONG ); // client connection, will self-delete on disconnect diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index e87ae9c509..2663d96038 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -24,19 +24,17 @@ #include "protocol.h" #include "server.h" -#include "client.h" +#ifndef SERVER_ONLY +# include "client.h" +#endif #include "channel.h" -CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, - const CHostAddress& tcpAddress, - CServer* pServer, - CClient* pClient, - CChannel* pChannel, - bool bIsSession ) : +#ifndef SERVER_ONLY +// TCP connection used by client +CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CClient* pClient, CChannel* pChannel, bool bIsSession ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ), - pServer ( pServer ), - pClient ( pClient ), + pServer ( nullptr ), pChannel ( pChannel ), bIsSession ( bIsSession ) { @@ -47,17 +45,9 @@ CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, connect ( pTcpSocket, &QTcpSocket::disconnected, this, &CTcpConnection::OnDisconnected ); connect ( pTcpSocket, &QTcpSocket::readyRead, this, &CTcpConnection::OnReadyRead ); - if ( pServer ) - { - connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); - } - - if ( pChannel ) - { - connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pChannel, &CChannel::OnProtocolCLMessageReceived ); - } + connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pChannel, &CChannel::OnProtocolCLMessageReceived ); - if ( pClient && bIsSession ) + if ( bIsSession ) { // set up keepalive CLM_EMPTY_MESSAGE over TCP session connection connect ( this, &CTcpConnection::CLSendEmptyMes, pClient, &CClient::OnCLSendEmptyMes ); @@ -65,6 +55,25 @@ CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, TimerKeepalive.start ( TCP_KEEPALIVE_INTERVAL_MS ); } } +#endif + +// TCP connection used by server +CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer ) : + pTcpSocket ( pTcpSocket ), + tcpAddress ( tcpAddress ), + pServer ( pServer ), + pChannel ( nullptr ), + bIsSession ( false ) +{ + vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); + iPos = 0; + iPayloadRemain = 0; + + connect ( pTcpSocket, &QTcpSocket::disconnected, this, &CTcpConnection::OnDisconnected ); + connect ( pTcpSocket, &QTcpSocket::readyRead, this, &CTcpConnection::OnReadyRead ); + + connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); +} void CTcpConnection::OnDisconnected() { diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 4c8bda3ee9..4732c16536 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -48,12 +48,10 @@ class CTcpConnection : public QObject Q_OBJECT public: - CTcpConnection ( QTcpSocket* pTcpSocket, - const CHostAddress& tcpAddress, - CServer* pServer, - CClient* pClient, - CChannel* pChannel, - bool bIsSession ); +#ifndef SERVER_ONLY + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CClient* pClient, CChannel* pChannel, bool bIsSession ); +#endif + CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress, CServer* pServer ); ~CTcpConnection() {} void SetChannel ( CChannel* pChan ) { pChannel = pChan; } @@ -67,10 +65,8 @@ class CTcpConnection : public QObject private: QTcpSocket* pTcpSocket; CHostAddress tcpAddress; - CHostAddress udpAddress; CServer* pServer; - CClient* pClient; CChannel* pChannel; const bool bIsSession; diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index 7e178531b0..9669337385 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -97,5 +97,5 @@ void CTcpServer::OnNewConnection() qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.toString(); - new CTcpConnection ( pSocket, peerAddress, pServer, nullptr, nullptr, false ); // will auto-delete on disconnect + new CTcpConnection ( pSocket, peerAddress, pServer ); // will auto-delete on disconnect } From 738bb11bca055eb9159459aad97f0cc7df835302 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 22:56:38 +0100 Subject: [PATCH 40/41] Add timeout for TCP connection --- src/client.cpp | 25 +++++++++++++++++++++++-- src/tcpconnection.h | 1 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 19ebf3b93e..d6772a1ad2 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -270,20 +270,40 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM // create a TCP client connection and send message QTcpSocket* pSocket = new QTcpSocket ( this ); + // timer for TCP connect timeout shorter than Qt default 30 seconds + QTimer* pTimer = new QTimer ( this ); + pTimer->setSingleShot ( true ); + + connect ( pTimer, &QTimer::timeout, this, [this, pSocket, pTimer]() { + if ( pSocket->state() != QAbstractSocket::ConnectedState ) + { + pSocket->abort(); + pSocket->deleteLater(); + qDebug() << "- TCP connect timeout"; + } + pTimer->deleteLater(); + } ); + #if QT_VERSION >= QT_VERSION_CHECK( 5, 15, 0 ) # define ERRORSIGNAL &QTcpSocket::errorOccurred #else # define ERRORSIGNAL QOverload::of ( &QAbstractSocket::error ) #endif - connect ( pSocket, ERRORSIGNAL, this, [this, pSocket] ( QAbstractSocket::SocketError err ) { + connect ( pSocket, ERRORSIGNAL, this, [this, pSocket, pTimer] ( QAbstractSocket::SocketError err ) { Q_UNUSED ( err ); + pTimer->stop(); + pTimer->deleteLater(); + qWarning() << "- TCP connection error:" << pSocket->errorString(); // may want to specifically handle ConnectionRefusedError? pSocket->deleteLater(); } ); - connect ( pSocket, &QTcpSocket::connected, this, [this, pSocket, InetAddr, vecMessage, eProtoMode]() { + connect ( pSocket, &QTcpSocket::connected, this, [this, pSocket, pTimer, InetAddr, vecMessage, eProtoMode]() { + pTimer->stop(); + pTimer->deleteLater(); + // connection succeeded, give it to a CTcpConnection CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, InetAddr, @@ -302,6 +322,7 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecM } ); pSocket->connectToHost ( InetAddr.InetAddr, InetAddr.iPort ); + pTimer->start ( TCP_CONNECT_TIMEOUT_MS ); } else { diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 4732c16536..a606e2233e 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -40,6 +40,7 @@ class CServer; // forward declaration of CServer class CChannel; // forward declaration of CChannel +#define TCP_CONNECT_TIMEOUT_MS 3000 #define TCP_KEEPALIVE_INTERVAL_MS 15000 /* Classes ********************************************************************/ From 2c09cb1aa6308b5aa181f40c06ab7be8cd8f0a9e Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 21 May 2026 10:40:31 +0100 Subject: [PATCH 41/41] Add an idle timeout on the server side --- src/tcpconnection.cpp | 18 ++++++++++++++++++ src/tcpconnection.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index 2663d96038..5fc958f7bc 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -73,12 +73,18 @@ CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcp connect ( pTcpSocket, &QTcpSocket::readyRead, this, &CTcpConnection::OnReadyRead ); connect ( this, &CTcpConnection::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived ); + + // setup an idle timer on the server side only + connect ( &TimerIdleTimeout, &QTimer::timeout, this, &CTcpConnection::OnTimerIdleTimeout ); + TimerIdleTimeout.setSingleShot ( true ); + TimerIdleTimeout.start ( TCP_IDLE_TIMEOUT_MS ); } void CTcpConnection::OnDisconnected() { qDebug() << "- Jamulus-TCP: disconnected from:" << tcpAddress.toString(); TimerKeepalive.stop(); + TimerIdleTimeout.stop(); pTcpSocket->deleteLater(); if ( pChannel && pChannel->GetTcpConnection() == this ) { @@ -183,6 +189,12 @@ void CTcpConnection::OnReadyRead() } qDebug() << "- end of readyRead(), bytesAvailable() =" << pTcpSocket->bytesAvailable(); + + if ( pServer ) + { + // restart server idle timer allowing for keepalive interval + TimerIdleTimeout.start ( TCP_KEEPALIVE_INTERVAL_MS + TCP_IDLE_TIMEOUT_MS ); + } } void CTcpConnection::OnTimerKeepalive() @@ -191,6 +203,12 @@ void CTcpConnection::OnTimerKeepalive() emit CLSendEmptyMes ( tcpAddress, this ); } +void CTcpConnection::OnTimerIdleTimeout() +{ + // qDebug() << "- ConnTimeout timer" << this << "from TCP" << tcpAddress.toString(); + disconnectFromHost(); +} + qint64 CTcpConnection::write ( const char* data, qint64 maxSize ) { if ( !pTcpSocket ) diff --git a/src/tcpconnection.h b/src/tcpconnection.h index a606e2233e..1c51e2e5b4 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -41,6 +41,7 @@ class CServer; // forward declaration of CServer class CChannel; // forward declaration of CChannel #define TCP_CONNECT_TIMEOUT_MS 3000 +#define TCP_IDLE_TIMEOUT_MS 5000 #define TCP_KEEPALIVE_INTERVAL_MS 15000 /* Classes ********************************************************************/ @@ -77,6 +78,7 @@ class CTcpConnection : public QObject CVector vecbyRecBuf; QTimer TimerKeepalive; + QTimer TimerIdleTimeout; signals: void ProtocolCLMessageReceived ( int iRecID, CVector vecbyMesBodyData, CHostAddress HostAdr, CTcpConnection* pTcpConnection ); @@ -86,4 +88,5 @@ private slots: void OnDisconnected(); void OnReadyRead(); void OnTimerKeepalive(); + void OnTimerIdleTimeout(); };