From bfa05ea9455a44e58b0865c39882ef8fe643c51c Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 21 Jan 2025 15:00:05 +0000 Subject: [PATCH 01/50] 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 d2bf7a1140..2f0ae8efa0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -119,6 +119,7 @@ int main ( int argc, char** argv ) bool bUseTranslation = true; bool bCustomPortNumberGiven = false; bool bDisableIPv6 = false; + bool bEnableTcp = false; int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS; quint16 iPortNumber = DEFAULT_PORT_NUMBER; int iJsonRpcPortNumber = INVALID_PORT; @@ -281,6 +282,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" ) ) { @@ -1023,6 +1034,7 @@ int main ( int argc, char** argv ) bDisableRecording, bDelayPan, bDisableIPv6, + bEnableTcp, eLicenceType ); #ifndef NO_JSON_RPC @@ -1149,6 +1161,7 @@ QString UsageArguments ( char** argv ) " -s, --server start Server\n" " --serverbindip IPv4 address the Server will bind to (rather than all)\n" " (only works if IPv6 is unavailable or disabled with --noipv6)\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 90af6578e8..bcebe79b2b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -67,6 +67,7 @@ CServer::CServer ( const int iNewMaxNumChan, const bool bDisableRecording, const bool bNDelayPan, const bool bNDisableIPv6, + const bool bNEnableTcp, const ELicenceType eNLicenceType ) : bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ), bUseMultithreading ( bNUseMultithreading ), @@ -91,6 +92,7 @@ CServer::CServer ( const int iNewMaxNumChan, bDisableRecording ( bDisableRecording ), bAutoRunMinimized ( false ), bDelayPan ( bNDelayPan ), + bEnableTcp ( bNEnableTcp ), eLicenceType ( eNLicenceType ), bDisconnectAllClientsOnQuit ( bNDisconnectAllClientsOnQuit ), pSignalHandler ( CSignalHandler::getSingletonP() ) diff --git a/src/server.h b/src/server.h index dae256a982..f37ee55a37 100644 --- a/src/server.h +++ b/src/server.h @@ -126,6 +126,7 @@ class CServer : public QObject, public CServerSlots const bool bDisableRecording, const bool bNDelayPan, const bool bNDisableIPv6, + const bool bNEnableTcp, const ELicenceType eNLicenceType ); virtual ~CServer(); @@ -318,6 +319,9 @@ class CServer : public QObject, public CServerSlots // for delay panning bool bDelayPan; + // enable TCP Server + bool bEnableTcp; + // messaging QString strWelcomeMessage; ELicenceType eLicenceType; From c0090663babb6ad4c7ae202a43af9f0d5092e5d9 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 22 Mar 2024 23:52:50 +0000 Subject: [PATCH 02/50] 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 f4a761110d..082c1476df 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 335856ad93..f24cdde520 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2703,6 +2703,16 @@ void CProtocol::CreateCLWelcomeMessageMes ( const CHostAddress& InetAddr, const /******************************************************************************\ * 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 8d4125a9ab..d2c5ae4b04 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -184,6 +184,8 @@ class CProtocol : public QObject void CreateCLServerFeaturesMes ( const CHostAddress& InetAddr, const uint32_t iResult ); void CreateCLWelcomeMessageMes ( const CHostAddress& InetAddr, const QString strWelcomeMessage ); + 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 bcebe79b2b..edb9476c3e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -76,6 +76,7 @@ CServer::CServer ( const int iNewMaxNumChan, bDisableRaw ( bNDisableRaw ), bIPv6Available ( false ), Socket ( this, iPortNumber, iQosNumber, strServerBindIP, bNDisableIPv6, bIPv6Available ), + TcpServer ( this, strServerBindIP, iPortNumber, bNEnableIPv6 ), Logging(), iFrameCount ( 0 ), HighPrecisionTimer ( bNUseDoubleSystemFrameSize ), @@ -324,6 +325,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 f37ee55a37..5653aa1227 100644 --- a/src/server.h +++ b/src/server.h @@ -64,6 +64,7 @@ #include "util.h" #include "serverlogging.h" #include "serverlist.h" +#include "tcpserver.h" #include "recorder/jamcontroller.h" #include "threadpool.h" @@ -297,6 +298,7 @@ class CServer : public QObject, public CServerSlots // actual working objects bool bIPv6Available; // must be before Socket - passed by reference to Socket 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 451a98c1e03adf63e8884ff64edf45da4e63a176 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 25 Mar 2024 17:16:21 +0000 Subject: [PATCH 03/50] 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 eea8b0c8e2..8f42144eb3 100644 --- a/src/channel.h +++ b/src/channel.h @@ -108,6 +108,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; @@ -209,7 +212,8 @@ class CChannel : public QObject } // connection parameters - CHostAddress InetAddr; + CHostAddress InetAddr; + CTcpConnection* pTcpConnection; // channel info CChannelCoreInfo ChannelInfo; @@ -278,9 +282,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(); } @@ -306,7 +310,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 6c7464b976..a62e66a611 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -266,11 +266,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 ) @@ -285,10 +293,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 06847dc6d4..7cf77a11a9 100644 --- a/src/client.h +++ b/src/client.h @@ -453,7 +453,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 ); @@ -468,7 +468,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 f24cdde520..a7517d4b87 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -657,7 +657,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; @@ -666,7 +669,7 @@ void CProtocol::CreateAndImmSendConLessMessage ( const int iID, const CVector& vecbyMesBodyData, const int iRecCounter, const int iRecID ) @@ -894,7 +897,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) @@ -926,7 +932,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: @@ -962,7 +968,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: @@ -2079,7 +2085,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(); @@ -2134,7 +2140,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 ) @@ -2299,10 +2305,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 } @@ -2437,7 +2443,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(); @@ -2485,7 +2491,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 ) @@ -2552,10 +2558,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 d2c5ae4b04..4e5194b6bd 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -53,6 +53,7 @@ #include #include "global.h" #include "util.h" +#include "tcpserver.h" /* Definitions ****************************************************************/ // protocol message IDs @@ -169,7 +170,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 ); @@ -177,7 +178,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 ); @@ -194,7 +195,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 ); } @@ -273,7 +277,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(); @@ -303,13 +310,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 ); bool EvaluateCLReqServerFeaturesMes ( const CHostAddress& InetAddr ); @@ -336,7 +343,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 ); @@ -372,13 +379,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 ); void CLReqServerFeatures ( CHostAddress InetAddr ); diff --git a/src/server.cpp b/src/server.cpp index edb9476c3e..c054e1b998 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -552,11 +552,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 ) @@ -1561,12 +1569,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 5653aa1227..b02cb83914 100644 --- a/src/server.h +++ b/src/server.h @@ -359,9 +359,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 ); @@ -381,11 +381,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 0dafa73863..6d038b99ed 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -685,7 +685,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 ); @@ -744,8 +744,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 f18c40678d..e31eac1cc3 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -193,7 +193,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 b81ea4d69d..26cab9e7eb 100644 --- a/src/socket.h +++ b/src/socket.h @@ -53,6 +53,7 @@ #include "global.h" #include "protocol.h" #include "util.h" +#include "tcpserver.h" #ifndef _WIN32 # include # include @@ -138,7 +139,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 233acd6351..e165144703 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -240,7 +240,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 @@ -283,7 +283,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 a5ea5e4f4e021899d3927dd0e3ae3d29b293a5f7 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 21 Jan 2025 17:52:46 +0000 Subject: [PATCH 04/50] 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 a7517d4b87..081a0aa307 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -484,6 +484,12 @@ CONNECTION LESS MESSAGES note: does not have any data -> n = 0 + + +- PROTMESSID_CLM_TCP_SUPPORTED: TCP supported message + + note: does not have any data -> n = 0 + */ #include "protocol.h" @@ -986,6 +992,10 @@ void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMe case PROTMESSID_CLM_REQ_WELCOME_MESSAGE: EvaluateCLReqWelcomeMessageMes ( InetAddr ); break; + + case PROTMESSID_CLM_TCP_SUPPORTED: + EvaluateCLTcpSupportedMes ( InetAddr ); + break; } } @@ -2706,6 +2716,19 @@ void CProtocol::CreateCLWelcomeMessageMes ( const CHostAddress& InetAddr, const CreateAndImmSendConLessMessage ( PROTMESSID_CLM_WELCOME_MESSAGE, vecData, InetAddr ); } +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 4e5194b6bd..fae2d21553 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -111,6 +111,7 @@ #define PROTMESSID_CLM_REQ_SERVER_FEATURES 1020 // request server features #define PROTMESSID_CLM_WELCOME_MESSAGE 1021 // server welcome message #define PROTMESSID_CLM_REQ_WELCOME_MESSAGE 1022 // request server welcome message +#define PROTMESSID_CLM_TCP_SUPPORTED 1019 // TCP is supported // special IDs #define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages @@ -184,6 +185,7 @@ class CProtocol : public QObject void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult ); void CreateCLServerFeaturesMes ( const CHostAddress& InetAddr, const uint32_t iResult ); void CreateCLWelcomeMessageMes ( const CHostAddress& InetAddr, const QString strWelcomeMessage ); + void CreateCLTcpSupportedMes ( const CHostAddress& InetAddr ); static int GetBodyLength ( const CVector& vecbyData ); @@ -321,6 +323,7 @@ class CProtocol : public QObject bool EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, const CVector& vecData ); bool EvaluateCLReqServerFeaturesMes ( const CHostAddress& InetAddr ); bool EvaluateCLReqWelcomeMessageMes ( const CHostAddress& InetAddr ); + bool EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr ); int iOldRecID; int iOldRecCnt; @@ -390,4 +393,5 @@ public slots: void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); void CLReqServerFeatures ( CHostAddress InetAddr ); void CLReqWelcomeMessage ( CHostAddress InetAddr ); + void CLTcpSupported ( CHostAddress InetAddr ); }; From 592da7f387098766e2106877ad73823d76b17092 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 9 Mar 2026 16:00:33 +0000 Subject: [PATCH 05/50] Add CLM_TCP_SUPPORTED message generation when TCP enabled --- src/server.cpp | 7 +++++++ src/server.h | 6 ++++++ src/serverlist.cpp | 12 +++++++++++- src/serverlist.h | 3 +++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index c054e1b998..7e9b02b340 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -88,6 +88,7 @@ CServer::CServer ( const int iNewMaxNumChan, strServerPublicIP, strServerListFilter, iNewMaxNumChan, + bNEnableTcp, &ConnLessProtocol ), JamController ( this ), bDisableRecording ( bDisableRecording ), @@ -404,6 +405,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 b02cb83914..2a0b712481 100644 --- a/src/server.h +++ b/src/server.h @@ -388,6 +388,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 6d038b99ed..503f1bd232 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -150,9 +150,11 @@ CServerListManager::CServerListManager ( CServer* pServer, const QString& strServerListFilter, const QString& strServerPublicIP, const int iNumChannels, + const bool bNEnableTcp, CProtocol* pNConLProt ) : pServer ( pServer ), DirectoryType ( AT_NONE ), + bEnableTcp ( bNEnableTcp ), ServerListFileName ( strServerListFileName ), strDirectoryAddress ( "" ), bIsDirectory ( false ), @@ -731,7 +733,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) @@ -750,6 +754,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 e31eac1cc3..857d8aec4f 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -166,6 +166,7 @@ class CServerListManager : public QObject const QString& strServerListFilter, const QString& strServerPublicIP, const int iNumChannels, + const bool bNEnableTcp, CProtocol* pNConLProt ); void SetServerName ( const QString& strNewName ); @@ -218,6 +219,8 @@ class CServerListManager : public QObject CHostAddress DirectoryAddress; EDirectoryType DirectoryType; + bool bEnableTcp; + CHostAddress ServerPublicIP; CHostAddress ServerPublicIP6; From f3f022e8ecb044f8f5f5cd1dcec7d200df0d2c9c Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 16 Mar 2026 11:28:54 +0000 Subject: [PATCH 06/50] 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 7cf77a11a9..ce8a99bc5a 100644 --- a/src/client.h +++ b/src/client.h @@ -302,9 +302,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 6a34cc66f0..de32165dab 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -221,13 +221,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 02324121e3..9269f8dd9c 100644 --- a/src/clientrpc.cpp +++ b/src/clientrpc.cpp @@ -193,7 +193,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 05127ded37..645a133da8 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -358,7 +358,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 @@ -401,7 +401,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 } } @@ -1011,7 +1011,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 2d82a8c454..5a7b683be8 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -157,8 +157,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 081a0aa307..9690a1c6a0 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -666,7 +666,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; @@ -2310,9 +2311,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 ) @@ -2563,9 +2564,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 fae2d21553..487f451ad3 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -173,14 +173,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 CreateCLServerFeaturesMes ( const CHostAddress& InetAddr, const uint32_t iResult ); @@ -282,7 +282,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 e165144703..bb08a657eb 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -244,7 +244,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 @@ -287,7 +287,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 e610545cfb3ea9ab37aaa76a96980f28e75d60e3 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 16 Mar 2026 14:22:38 +0000 Subject: [PATCH 07/50] 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 a62e66a611..fdf5c089f6 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -169,6 +169,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 ce8a99bc5a..efb2f02217 100644 --- a/src/client.h +++ b/src/client.h @@ -504,6 +504,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 b102c34724..302f8c4b43 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -529,6 +529,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 de32165dab..c7855add59 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -242,6 +242,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 645a133da8..16a25001ca 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -566,6 +566,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 5a7b683be8..d6661d28d8 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -91,6 +91,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 adb80773a1862bc3ab8bf10515781c8769f39af5 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 16 Mar 2026 15:02:49 +0000 Subject: [PATCH 08/50] 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 fdf5c089f6..c692bd7c60 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -268,14 +268,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 efb2f02217..c458404ad5 100644 --- a/src/client.h +++ b/src/client.h @@ -474,7 +474,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 c7855add59..0b0ebf0de5 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -242,10 +242,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 16a25001ca..360181b59d 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -566,10 +566,7 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector& vecChanInfo ) { diff --git a/src/protocol.cpp b/src/protocol.cpp index 9690a1c6a0..2cbff3fbb1 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -676,7 +676,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 487f451ad3..ca571f4c3a 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -347,7 +347,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 7e9b02b340..9437b20ca1 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -559,8 +559,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 2a0b712481..81c9bbe892 100644 --- a/src/server.h +++ b/src/server.h @@ -359,7 +359,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 99a1855a0b1dc70b64ccc5ce7235bfb728552f87 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 19 Mar 2026 12:16:50 +0000 Subject: [PATCH 09/50] 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 d843b9b5e9511c2d75eed93344891089e62812ea Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 20 Mar 2026 18:31:08 +0000 Subject: [PATCH 10/50] 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 0d60d8769080609a6145b24b734468d85321bb63 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 20 Mar 2026 22:30:43 +0000 Subject: [PATCH 11/50] 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 701560874aee8cdbb2e2435002662ff0d22f2d5e Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 21 Mar 2026 00:28:24 +0000 Subject: [PATCH 12/50] 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 082c1476df..6bb87219f1 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 ca571f4c3a..f84c32be26 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -53,7 +53,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 81c9bbe892..18c3f259bc 100644 --- a/src/server.h +++ b/src/server.h @@ -65,6 +65,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 26cab9e7eb..4c95fdfd97 100644 --- a/src/socket.h +++ b/src/socket.h @@ -53,7 +53,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 24d95752604d071e65f611851ad9a4cc67ceb583 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 22 Mar 2026 16:46:15 +0000 Subject: [PATCH 13/50] 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 9437b20ca1..0a4851cbb0 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -572,7 +572,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 f86bcd013cbd8af639f1d756dd6b452a79802473 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 23 Mar 2026 12:16:31 +0000 Subject: [PATCH 14/50] 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 c692bd7c60..0eabdb42df 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -281,6 +281,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 35a9fcefe1db1b287f840a20bf371f69353db53b Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 23 Mar 2026 18:11:20 +0000 Subject: [PATCH 15/50] 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 0eabdb42df..6966bfe400 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -293,12 +293,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 360181b59d..9210668b2d 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -566,7 +566,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 d2b00b69704dba4d098d5595c892b8e3206071c6 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 25 Mar 2026 20:35:24 +0000 Subject: [PATCH 16/50] 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 c458404ad5..ce7085667a 100644 --- a/src/client.h +++ b/src/client.h @@ -504,7 +504,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 0b0ebf0de5..1d403d79b0 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -242,7 +242,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 9210668b2d..72e6a67711 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -566,16 +566,27 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVectorerrorString(); From b25d358915480719f5e1ec89f6a3a9a6b40db720 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 26 Mar 2026 23:01:08 +0000 Subject: [PATCH 18/50] 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 72e6a67711..07567a16b3 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -553,6 +553,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 ) { @@ -583,6 +586,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: @@ -597,6 +620,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 ); @@ -1035,7 +1068,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 c8dd6e95d8..e893c9308c 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -117,6 +117,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 529f7fef0b7639858eb3210519c735bc7daac409 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 1 Apr 2026 17:32:19 +0100 Subject: [PATCH 19/50] 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 9a3ada0435..c4b7d6bf23 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -387,6 +387,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 @@ -488,14 +493,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 */ @@ -1004,6 +1018,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; } } @@ -2753,6 +2771,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 9f27d6b8fb..858b4c2147 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -112,6 +112,7 @@ #define PROTMESSID_CLM_WELCOME_MESSAGE 1021 // server welcome message #define PROTMESSID_CLM_REQ_WELCOME_MESSAGE 1022 // request server welcome message #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 @@ -186,6 +187,7 @@ class CProtocol : public QObject void CreateCLServerFeaturesMes ( const CHostAddress& InetAddr, const uint32_t iResult ); void CreateCLWelcomeMessageMes ( const CHostAddress& InetAddr, const QString strWelcomeMessage ); void CreateCLTcpSupportedMes ( const CHostAddress& InetAddr, const int iID ); + void CreateCLClientIDMes ( const CHostAddress& InetAddr, const int iChanID, CTcpConnection* pTcpConnection ); static int GetBodyLength ( const CVector& vecbyData ); @@ -325,6 +327,7 @@ class CProtocol : public QObject bool EvaluateCLReqServerFeaturesMes ( const CHostAddress& InetAddr ); bool EvaluateCLReqWelcomeMessageMes ( const CHostAddress& InetAddr ); bool EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLClientIDMes ( const CHostAddress& InetAddr, const CVector& vecData, CTcpConnection* pTcpConnection ); int iOldRecID; int iOldRecCnt; @@ -395,4 +398,5 @@ public slots: void CLReqServerFeatures ( CHostAddress InetAddr ); void CLReqWelcomeMessage ( CHostAddress InetAddr ); void CLTcpSupported ( CHostAddress InetAddr, int iID ); + void CLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpConnection* pTcpConnection ); }; From 63d0123944be700be0d6a1e818812aedd8d24d37 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 1 Apr 2026 21:15:53 +0100 Subject: [PATCH 20/50] 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 7755b7ec92..52b3dd6837 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -103,7 +103,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 ); @@ -736,3 +736,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 8f42144eb3..cb236fab02 100644 --- a/src/channel.h +++ b/src/channel.h @@ -287,6 +287,7 @@ public slots: emit DetectedCLMessage ( vecbyMesBodyData, iRecID, RecHostAddr, pTcpConnection ); } + void OnClientIDReceived ( int iChanID ); void OnNewConnection() { emit NewConnection(); } signals: From 524936d7c3ce268d635041f425682b7a1fd3bebb Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 1 Apr 2026 23:07:48 +0100 Subject: [PATCH 21/50] 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 734eeb4d88..d6105234b5 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -169,7 +169,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 ); @@ -1033,6 +1033,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 @@ -1068,11 +1077,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 ce7085667a..5df9814702 100644 --- a/src/client.h +++ b/src/client.h @@ -454,6 +454,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 ); @@ -473,6 +477,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 1f989abfe5..083fe8ebe4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -408,7 +408,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 f627d0e8e62a638123ca058a5b92587a349e6ced Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 2 Apr 2026 12:43:53 +0100 Subject: [PATCH 22/50] 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 d6105234b5..234fe70925 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1079,6 +1079,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 07567a16b3..5d33bb3d8f 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -571,8 +571,6 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector Date: Sat, 4 Apr 2026 22:10:14 +0100 Subject: [PATCH 25/50] 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 52b3dd6837..7c50bf2c2a 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -48,6 +48,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 ), @@ -742,3 +743,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 cb236fab02..9b71d0e6ba 100644 --- a/src/channel.h +++ b/src/channel.h @@ -187,7 +187,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 083fe8ebe4..b0ac312d6d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -424,7 +424,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(); @@ -1363,7 +1363,7 @@ void CServer::CreateAndSendChanListForAllConChannels() if ( vecChannels[i].IsConnected() ) { // send message - vecChannels[i].CreateConClientListMes ( vecChanInfo ); + vecChannels[i].CreateConClientListMes ( vecChanInfo, ConnLessProtocol ); } } } @@ -1374,7 +1374,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 13f06afc774b354302dd3f5d576f3b3be3b78b05 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 4 Apr 2026 22:42:08 +0100 Subject: [PATCH 26/50] 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 b0ac312d6d..991648f79a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -300,6 +300,8 @@ CServer::CServer ( const int iNewMaxNumChan, QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqWelcomeMessage, this, &CServer::OnCLReqWelcomeMessage ); + 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 ); @@ -580,6 +582,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 7845a2553f..a965e489ba 100644 --- a/src/server.h +++ b/src/server.h @@ -421,6 +421,8 @@ public slots: void OnCLDisconnection ( CHostAddress InetAddr ); + void OnCLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpConnection* pTcpConnection ); + void OnAboutToQuit(); void OnHandledSignal ( int sigNum ); From 6ae56e6e3b6d50f61f49ab109c9a4e367f40ae1f Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 14:57:21 +0100 Subject: [PATCH 27/50] 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 44cfa8b7185671af083cf3a4c47776a06afa9266 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 17:46:16 +0100 Subject: [PATCH 28/50] 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 d04b6f160d..03384aedcf 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -299,7 +299,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 328ea5a466614b6bf24add8d080f9cf1b9290f1f Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 17:47:14 +0100 Subject: [PATCH 29/50] 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 991648f79a..02ab94b036 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -585,6 +585,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 c73c7fbe3cfb9c02c7f0433aa3d0d6239b24f66a Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 5 Apr 2026 22:52:55 +0100 Subject: [PATCH 30/50] 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 03384aedcf..ce0a900633 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -268,7 +268,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 ) { @@ -278,7 +278,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 ); @@ -296,11 +296,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 5df9814702..e80b8ef31e 100644 --- a/src/client.h +++ b/src/client.h @@ -302,14 +302,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 ); @@ -479,7 +479,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 1d403d79b0..b33f8a5bac 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -221,15 +221,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 9269f8dd9c..e6fc5d36ee 100644 --- a/src/clientrpc.cpp +++ b/src/clientrpc.cpp @@ -193,7 +193,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 5d33bb3d8f..d63cc7d8f0 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -358,7 +358,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 @@ -401,7 +401,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 ); } } @@ -579,7 +579,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; @@ -600,7 +600,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 } } } @@ -1075,7 +1075,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 e893c9308c..3bc6f63b57 100644 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -168,8 +168,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 c4b7d6bf23..417790f41b 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -688,7 +688,7 @@ void CProtocol::CreateAndImmSendConLessMessage ( const int iID, const CVector& vecData, const CHostAddress& InetAddr, CTcpConnection* pTcpConnection, - bool bUseTcpClient ) + enum EProtoMode eProtoMode ) { CVector vecNewMessage; @@ -697,7 +697,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 ) @@ -2336,9 +2336,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 ) @@ -2589,9 +2589,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 858b4c2147..276c68670e 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -128,6 +128,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 { @@ -174,14 +182,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 CreateCLServerFeaturesMes ( const CHostAddress& InetAddr, const uint32_t iResult ); @@ -285,7 +293,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(); @@ -350,7 +358,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 02ab94b036..d99d106ee6 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -561,9 +561,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 a965e489ba..54061fdd96 100644 --- a/src/server.h +++ b/src/server.h @@ -360,7 +360,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 bb08a657eb..18d13e1b2a 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -244,7 +244,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 @@ -287,7 +287,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 34e7fb22c716d58b72d915cd74dc96a66d691753 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 6 Apr 2026 11:02:40 +0100 Subject: [PATCH 31/50] 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 ce0a900633..283d6c9275 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1043,6 +1043,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 @@ -1097,6 +1098,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 417790f41b..bf63419639 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2771,7 +2771,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 @@ -2781,7 +2781,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 276c68670e..6b9245b2da 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -195,7 +195,7 @@ class CProtocol : public QObject void CreateCLServerFeaturesMes ( const CHostAddress& InetAddr, const uint32_t iResult ); void CreateCLWelcomeMessageMes ( const CHostAddress& InetAddr, const QString strWelcomeMessage ); 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 93505716402d873f7d68be6d4b1678821ca1af10 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 6 Apr 2026 22:48:50 +0100 Subject: [PATCH 32/50] 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 283d6c9275..d4d4aecc82 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -171,7 +171,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 ); @@ -1103,6 +1103,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 e80b8ef31e..18175f6d08 100644 --- a/src/client.h +++ b/src/client.h @@ -494,6 +494,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 15763795199afd90dc0a6bad5bc38a75528d0150 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 7 Apr 2026 10:36:41 +0100 Subject: [PATCH 33/50] 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 1f9a5f94f037da5227c82e68216091ae5916826a Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 7 Apr 2026 17:58:59 +0100 Subject: [PATCH 34/50] 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 d4d4aecc82..3b0fede1df 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -302,7 +302,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() ); @@ -1103,16 +1108,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 } } @@ -1146,6 +1152,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 18175f6d08..978f5bdaab 100644 --- a/src/client.h +++ b/src/client.h @@ -494,7 +494,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 bf63419639..36fbf97d21 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -992,7 +992,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: @@ -2530,7 +2530,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(); @@ -2584,7 +2584,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 6b9245b2da..12ab89bd24 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -328,7 +328,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 ); @@ -399,7 +399,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 d7e165f285d090d14f6de152ce0d114e695a422d Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 7 Apr 2026 18:06:52 +0100 Subject: [PATCH 35/50] 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 d63cc7d8f0..61bcf4fcda 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -574,12 +574,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; @@ -600,7 +600,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 49f3925c8cd2572318ff7f5aebd953513428726c Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 8 Apr 2026 20:44:21 +0100 Subject: [PATCH 36/50] 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 36fbf97d21..f496a750ff 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2387,11 +2387,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 12ab89bd24..f01a7d3706 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -184,7 +184,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 54061fdd96..d41b121a26 100644 --- a/src/server.h +++ b/src/server.h @@ -378,7 +378,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 08152e4576..74b3ec35a6 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -545,7 +545,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 ); } } @@ -953,7 +953,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 18d13e1b2a..6554317b5d 100644 --- a/src/testbench.h +++ b/src/testbench.h @@ -252,7 +252,7 @@ public slots: break; case 22: // PROTMESSID_CLM_EMPTY_MESSAGE - Protocol.CreateCLEmptyMes ( CurHostAddress ); + Protocol.CreateCLEmptyMes ( CurHostAddress, nullptr ); break; case 23: // PROTMESSID_CLM_DISCONNECTION From 8ac674f5898bdcc0afd2dd86bd43b4c1c84c1970 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 11:45:40 +0100 Subject: [PATCH 37/50] 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 3b0fede1df..138eeca33d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -272,7 +272,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; } @@ -301,6 +302,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 978f5bdaab..1218ceaf9a 100644 --- a/src/client.h +++ b/src/client.h @@ -496,6 +496,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 50fc273ad5893bbf227fbb78c22b82cdb1a9d2ee Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 14:49:30 +0100 Subject: [PATCH 38/50] Clarify comment --- src/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index 138eeca33d..7330a43424 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1043,7 +1043,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 10139ef1fa11d7f5a779f3bd5fdf8a7730b32586 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 17:36:11 +0100 Subject: [PATCH 39/50] 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 7330a43424..af281af10f 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -301,7 +301,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 7b99efc89dea78d091664f6dbd67cfb8a6932b2d Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 9 Apr 2026 22:56:38 +0100 Subject: [PATCH 40/50] 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 af281af10f..62764cf68c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -284,20 +284,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, @@ -316,6 +336,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 482c24766f9a65db10312d00b9815305ef67e997 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 21 May 2026 10:40:31 +0100 Subject: [PATCH 41/50] 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(); }; From 9c3ea6bfa41284b8db0ab3217b261c2519b12331 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 28 May 2026 12:23:11 +0100 Subject: [PATCH 42/50] Add document describing TCP operation --- docs/TCP.md | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 docs/TCP.md diff --git a/docs/TCP.md b/docs/TCP.md new file mode 100644 index 0000000000..ef79e50e44 --- /dev/null +++ b/docs/TCP.md @@ -0,0 +1,215 @@ +# TCP FALLBACK FOR THE JAMULUS PROTOCOL + +## THE PROBLEM BEING SOLVED + +All Jamulus protocol (non-audio) messages are currently delivered over the same UDP channel as the audio. For most protocol messages, this is fine, but those that send a list of servers from a directory, or a list of clients from a server, can generate a UDP datagram that is too large to fit into a single physical packet. Physical packets are constrained by the MTU of the Ethernet interface (normally 1500 bytes or less), and further by any limitations in links between hops on the internet. Neither the client nor the server has any control over these limitation. It's also possible a large welcome message could require fragmentation. + +The UDP protocol itself allows datagrams up to be up to nearly 65535 bytes in size, minus any protocol overhead. IPv4 will allow nearly all of this size to be used, in theory. If the IPv4 datagram being sent by a node (host or router) is too large to fit into a single packet on the outgoing interface, the IP protocol will fragment the packet into pieces that do fit, with IP headers that contain the information needed to order and reassemble the fragments into a single datagram at the receiving end. Normally intermediate hops do not perform any reassembly, but will further fragment an IP packet if it will not fit the MTU of the outgoing interface. + +The receiving end's IP stack needs to store all the received fragments as they arrive and can only reassemble them into the original datagram once all fragments have been received. The loss of even one fragment renders the whole datagram lost, and the remaining received fragments consume resources until they time out and are discarded. There are also possibilities for a denial of service attack if an attacker deliberately sends lots of fragments with one or more missing. + +If a directory has more than around 35 servers registered (depending on the length of the name, city, etc.), the list of servers sent to a client when requested is certain to be fragmented. Similarly, if a powerful server has a lot of clients connected, e.g. a big band or large choir, the list of clients sent to each connected client could be large enough to get fragmented. In either of these cases, a client that is unable to receive fragmented IP packets will show an empty list or an empty mixer panel. + +There are several reasons that fragmented IP datagrams can fail to make it from server to client: + +* The configuration of a user's router, either accidentally or deliberately. Sometimes a user can be helped by a knowledgeable friend to check and fix this, but often not. +* The configuration of an intermediate router along the path from server to client. This is fairly rare, but could be a carrier's deliberate choice to avoid the kind of DoS attack mentioned above. For whatever reason, it is outside the control of the user or server operator. +* The IPv6 protocol deliberately has no provision for fragmentation of datagrams at the IP layer of intermediate hops. So this is a complete show-stopper for the use of IPv6 in directories, as there is therefore no support at all for large UDP messages. + +The IPv6 limitation means that resolving this issue is a prerequisite to implementing IPv6 support in directories. + +## CONNECTIONLESS MODE - CLIENT CONNECT DIALOG + +The basic summary is that TCP need only be used as a fallback when it is determined that a UDP message from a directory or server failed to reach the client, probably due to fragmentation, _and_ that the directory or server explicitly supports TCP. + +### Current operation when client opens Connect dialog + +1. Client sends `CLM_REQ_SERVER_LIST` to the selected directory server to ask for a list of registered servers. It then starts a 2.5 sec re-request timer. + +2. Directory server fetches its internal list of registered servers, and sends a `CLM_SEND_EMPTY_MESSAGE` to each listed server, with the IP and UDP port of the requesting client as parameters. + +3. Directory server sends `CLM_RED_SERVER_LIST` (reduced server list) to client. + + a. If/when client receives `CLM_RED_SERVER_LIST`, it populates its list of servers with the reduced info, and sets an internal flag to say it has done so. It checks this flag to avoid processing a repeat reduced server list. + + b. If the list is large and fragmented, and the path does not correctly pass fragments, the client will not receive the list. + +4. Directory server sends `CLM_SERVER_LIST` to client. It does this immediately after sending the reduced list above. + + a. If/when the client receives `CLM_SERVER_LIST`, it populates its list of servers with the full info, replacing any existing list that contained reduced info. It then stops the 2.5 sec re-request timer mentioned above, so that the server list is not requested again. + + b. If the list is large and fragmented, and the path does not correctly pass fragments, the client will not receive the list, and the request timer will be left running to retry. + +5. While client is displaying the server list, it periodically pings each server with `CLM_PING_MS_WITHNUMCLIENTS` including a timestamp in the message. + +6. Each pinged server, when it receives the ping, will create a `CLM_PING_MS_WITHNUMCLIENTS` in reply, containing a copy of the received timestamp, and the number of clients currently connected to that server. + +7. When the client receives the reply, it can calculate the round-trip time from the received timestamp and the current time. + +8. If the number of connected clients returned is different from the previously received number for that server, the client sends a `CLM_REQ_CONN_CLIENTS_LIST` to the server. + + a. The server responds with a list of clients in a `CLM_CONN_CLIENTS_LIST`. Most servers only have a small number of clients connected, and this message is not large enough to need IP fragmentation. + + b. If the server is a large one with many clients connected (e.g. for a choir, big band or WorldJam green room), the client list may be large enough to be fragmented by the IP layer. In that case, it might not be received by the requesting client, although in most cases it will. + +9. If/when the client receives the client list from the server, it can display the list of connected clients under the relevant server, if this is enabled in the GUI. + +10. The five steps above (5-9) continue until the user clicks Connect or closes the dialog. + +### Enhancement for TCP support + +1. A server (which may also be a directory) can be configured with the command-line option `--enabletcp` to enable TCP operation. + +2. If the directory server has TCP enabled, then *after* it has sent the `CLM_RED_SERVER_LIST` and `CLM_SERVER_LIST` by UDP, it will send a new message `CLM_TCP_SUPPORTED` to the client, with a data field of `CLM_SERVER_LIST`. This data field enables the client to know which request may need to be retried over TCP. + + a. An older version of client that does not support TCP will ignore this message and continue operating in the normal way just on UDP. + + b. A newer client that supports TCP should receive and process the `CLM_TCP_SUPPORTED` message *after* it has received and processed the UDP server list, unless fragmentation (or another cause) prevented the list from arriving. + + c. If such a client has already processed a full server list from `CLM_SERVER_LIST`, it will have no need to open a TCP connection to the directory, so this will be skipped. + + d. If the client receives `CLM_TCP_SUPPORTED` having *not* received and processed a `CLM_SERVER_LIST`, it will open a TCP connection to the directory server, and request the server list again over the TCP connection. + +3. If the directory server accepts a TCP connection and receives a `CLM_REQ_SERVER_LIST` over it, it will process the request in the same way as for a UDP request, with the following differences: + + a. There is no need for the directory to send `CLM_SEND_EMPTY_MESSAGE` to the servers in the list, since that was already done in response to the original UDP request. + + b. There is no need for the directory to send `CLM_RED_SERVER_LIST` to the client, since the TCP connection is reliable, so the directory server just sends the `CLM_SERVER_LIST` over the TCP connection. + +4. When the client has received the `CLM_SERVER_LIST` over TCP, it closes the TCP connection, populates its list of servers in the connect dialog in the normal way and stops the 2.5 sec re-request timer. + +5. The client starts pinging each listed server as normal, using UDP, and the server responds with a ping including the timestamp and number of clients, as described above. + +6. As above, if the number of connected clients has changed, the client sends a `CLM_REQ_CONN_CLIENTS_LIST` over UDP in the normal way. + +7. If a server in the list supports TCP, when it has sent a reply to the client list request with `CLM_CONN_CLIENTS_LIST`, it will follow it immediately with a `CLM_TCP_SUPPORTED`, with a data field of `CLM_CONN_CLIENTS_LIST`. + + a. An older version of client that does not support TCP will ignore the `CLM_TCP_SUPPORTED` message and continue operating in the normal way just on UDP. + + b. A newer client that supports TCP should received the `CLM_TCP_SUPPORTED` message *after* it has received and processed the UDP client list, unless fragmentation (or another cause) prevented the list from arriving. + + c. If such a client has already processed a client list from `CLM_CONN_CLIENTS_LIST`, it will have no need to open a TCP connection to the server, so this will be skipped. + + d. If the client receives `CLM_TCP_SUPPORTED` having *not* received and processed a `CLM_CONN_CLIENTS_LIST`, it will open a TCP connection to the server, and request the client list again over the TCP connection. + +8. If the server accepts a TCP connection and receives a `CLM_REQ_CONN_CLIENTS_LIST` over it, it will process the request in the same way as for a UDP request, but will send the reply over the TCP connection. + +9. When the client has received the `CLM_CONN_CLIENTS_LIST` over TCP, it closes the TCP connection and updates the list of clients for that server in the GUI. However, it will note for that server that TCP is needed, and if/when the number of connected clients next changes while the connect dialog is still open, it will immediately request the updated list via TCP instead of UDP. + + +### Summary + +By sending the `CLM_TCP_SUPPORTED` message immediately *after* sending a potentially large list of servers or connected clients, it allows a client easily to determine whether or not it needs to fall back to TCP without the necessity of timeouts or other delays. It will only need to use TCP if it has not already succeeded in receiving the message over UDP. + +## CONNECTED MODE + +### Existing operation when client clicks on Connect + +All these steps use UDP only. + +1. Client starts sending an audio stream to the server. This audio stream continues in parallel with the protocol exchange below. + + a. Server does not yet start sending an audio stream to the client. + +2. Server sees the audio stream and looks up the source IP:port in its channel table, finding no channel that matches. + +3. Server allocates a new channel in the channels array and stores the source IP:port in it. The channel index becomes the Channel ID of the connected client. + +4. Server sends a `CLIENT_ID` message to the client, containing the Channel ID mentioned above. + + a. Client replies with `ACKN (CLIENT_ID)`. *Note that all messages that do not begin `CLM_` need to be acked by the receiving side. For clarity, these `ACKN` messages will not be mentioned below.* + +5. Server sends `CONN_CLIENTS_LIST` to the client, containing a list of all current clients in the session. + +6. Server sends `REQ_SPLIT_MESS_SUPPORT` to ask the client if it supports split messages. + +7. Client sends back `SPLIT_MESS_SUPPORTED` immediately. + +7. Server sends `REQ_NETW_TRANSPORT_PROPS` to ask for the clients network transport parameters. + +8. Client sends `NETW_TRANSPORT_PROPS` containing the codec, packet size, number of channels, bitrate, etc. + +9. Server sends `REQ_JITT_BUF_SIZE` to ask for the client's required jitter buffer sizes. + +10. Client sends `JIT_BUF_SIZE`, containing the positions of the "server" jitter buffer slider in the Settings dialog. This is telling the server what size jitter buffer to use for receiving audio data from the client. (The position of the "client" jitter buffer slider is not needed by the server, as it is only used locally in the client). + +11. Server sends `REQ_CHANNEL_INFOS` to ask for the identity information for the channel. + +12. Client sends `CHANNEL_INFOS` containing the identity information from the user's profile settings in the client (country, instrument, skill level, name, city). + +13. Now that the server has received the `CHANNEL_INFOS` from the client, it starts to send the mixed audio stream to the client. + +14. Server sends `CHAT_TEXT` containing the server welcome message, if any. If there is none, this message is skipped. + +15. Server sends `VERSION_AND_OS` to tell the client the version of Jamulus on the server and the server platform. + +After this, messages are sent by either side when there is something to notify: + +* From server to client: + + - `CLM_CHANNEL_LEVEL_LIST` - list of audio levels for each channel. Sent every 250ms by a timer. + - `RECORDER_STATE` - current state of the server-based recording. Sent when the state changes? + - `JITT_BUF_SIZE` - the size of the receiving jitter buffer for this connection on the server. Sent in Auto mode when the value changes. + - `CLM_PING_MS` - sent in response to a `CLM_PING_MS` received from the client. For client-side ping time calculation. + - `CONN_CLIENTS_LIST` - list of connected clients. Sent when the list changes due to a client connecting or leaving. This message could be large on a server with many clients. + +* From client to server: + + - `CLM_PING_MS` - contains a timestamp and requests the server to send back the same timestamp, so that the round-trip time can be measured. Sent every 500ms by a timer. + - `NETW_TRANSPORT_PROPS` - specifies codec, packet size, bitrate, etc. Sent when the user changes Audio Channels, Audio Quality, Buffer Delay or Small Network Buffers. + - `CHANNEL_GAIN` - specifies the user's requested gain for a specific channel. Sent when the user moves a fader, but rate limited to avoid many changes in succession being sent. + - `JITT_BUF_SIZE` - specifies the requested size of the server's jitter buffer, or Auto. + - `CLM_DISCONNECTION` - sent when the user clicks on Disconnect, or connects to another server. + + +### TCP usage in Connected Mode + +The connected-mode protocol messages sent over UDP are all sequence numbered and acknowledged, in order to be robust against potential packet loss. Over TCP, such packet loss will not occur, as sequencing and acknowledgement all happen at the TCP network layer. + +Consequently, TCP will not be used for connected-mode protocol messages. + +The reason for using a TCP connection in an active session is just to provide a reliable path for delivering a list of connected clients that could be large and subject to fragmentation (if it is sent over UDP). So the established TCP connection is only used to deliver client lists, and not other protocol messages. + +Therefore, if the server has an active TCP connection from the client, it will use the connectionless `CLM_CONN_CLIENTS_LIST` message to deliver updates for the connected client list. If there is no active TCP connection, updates will be delivered using the connected-mode `CONN_CLIENTS_LIST` over UDP as at present. + +So the sequence is as follows: + +1. As soon as a TCP-enabled server sees audio from a new client and creates a channel for it, it will send `CLM_TCP_SUPPORTED` to the client, with a data field of `CLM_CLIENT_ID`. + +2. The server will then send the connected-mode `CLIENT_ID` message as normal, containing the channel ID that has been allocated. + +3. An older version of client that does not support TCP will ignore the `CLM_TCP_SUPPORTED` message and continue operating in the normal way just on UDP. + +4. A newer client that supports TCP will receive the `CLM_TCP_SUPPORTED` message, and will note that the server supports TCP. The client will open a long-lived TCP connection to the server (on the same port number as UDP). + +5. The server will accept the TCP connection, and will wait for the first message to arrive via that connection. + +6. When a newer client receives the `CLIENT_ID` message from a server it knows supports TCP, the client will send, as its first message over the connection, a `CLM_CLIENT_ID` message containing the channel ID that it received over UDP. (`CLM_CLIENT_ID` is a newly-defined connectionless message). + +7. The server will lookup the channel specified by the `CLM_CLIENT_ID` message, and *will check that the IP address of the channel matches the remote address of the TCP connection*. If it does not, it will close the connection. This prevents hijacking of a session by sending another client's ID. + +8. If the TCP connection matches the client channel, the socket descriptor will be stored in the channel, and the channel pointer will be stored in the TCP Connection instance. + +9. Any messages from the client that arrive over TCP will be handled in the same way as messages received over UDP. Responses will be send back over TCP too. At present, there are no such messages defined. Existing protocol messages will continue to use UDP. + +10. Updates to the Connected Clients List generated by the server will be sent over TCP as `CLM_CONN_CLIENTS_LIST`, if there is an active socket descriptor stored for the channel. If not, they will be sent over UDP as `CONN_CLIENTS_LIST` in the normal way. + +11. In order to keep the long-term TCP connection alive via firewalls, NAT routers, etc., the client will start a periodic timer (e.g. 15 sec) to send a `CLM_EMPTY_MESSAGE` over the TCP connection. This causes no action at the server, but keeps the TCP connection alive. + +12. The server will start an idle timeout, resetting it each time a message is received. If the idle timer times out, the server will close the TCP connection. + +13. Audio packets will continue to always use the UDP socket. + +14. To disconnect, a client will send `CLM_DISCONNECT` over UDP exactly as at present, and will also close the TCP connection. + +If the server receives a disconnection of the TCP socket, it will revert to UDP for connected client updates. It could send another `CLM_TCP_SUPPORTED` to invite the client to re-establish a TCP connection. + +## OTHER CONSIDERATIONS + +The server should only be configured to offer TCP by specifying `--enabletcp` if the server operator has also configured any firewall to allow the inbound TCP connections. + +If a server were to offer TCP to the client, but the server's firewall didn't allow the incoming TCP connection, the client request for TCP would wait until its request times out. + +This has to be the responsibility of the server/directory operator, and is why TCP operation must be controlled by a command-line option, rather than always enabled. The operator should only enable TCP in the Jamulus server if they know their environment has been configured to support it. + +Most operators of small servers of directories will not need to be concerned with TCP at all. _The only server operators who will need to enable TCP support are those running large directories (e.g. Volker, Peter) or those running a large server designed to support many simultaneous client connections._ From 8982a6338d55cce1b0063535725b3318d952202f Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 8 Jun 2026 23:40:50 +0100 Subject: [PATCH 43/50] Update copyright headers for new source files --- src/tcpconnection.cpp | 23 +++++++++++------------ src/tcpconnection.h | 21 ++++++++++----------- src/tcpserver.cpp | 23 +++++++++++------------ src/tcpserver.h | 21 ++++++++++----------- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index 5fc958f7bc..3c02c4ca20 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -6,21 +6,20 @@ * ****************************************************************************** * - * 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 free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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. + * 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 Affero 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 + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * - \******************************************************************************/ +\******************************************************************************/ #include "protocol.h" #include "server.h" diff --git a/src/tcpconnection.h b/src/tcpconnection.h index 1c51e2e5b4..f78c20827b 100644 --- a/src/tcpconnection.h +++ b/src/tcpconnection.h @@ -6,19 +6,18 @@ * ****************************************************************************** * - * 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 free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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. + * 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 Affero 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 + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * \******************************************************************************/ diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index 9669337385..59d1ec0e71 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -6,21 +6,20 @@ * ****************************************************************************** * - * 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 free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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. + * 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 Affero 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 + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * - \******************************************************************************/ +\******************************************************************************/ #include "tcpserver.h" diff --git a/src/tcpserver.h b/src/tcpserver.h index 92329d691d..6e32c66c50 100644 --- a/src/tcpserver.h +++ b/src/tcpserver.h @@ -6,19 +6,18 @@ * ****************************************************************************** * - * 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 free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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. + * 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 Affero 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 + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . * \******************************************************************************/ From 8d8f772ae68e77e4756795b55f973f4a551974dd Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 8 Jun 2026 23:55:29 +0100 Subject: [PATCH 44/50] Use new way to discover IPv6 availability --- src/server.cpp | 2 +- src/tcpserver.cpp | 7 +++---- src/tcpserver.h | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index d99d106ee6..2b1d83cb97 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -76,7 +76,7 @@ CServer::CServer ( const int iNewMaxNumChan, bDisableRaw ( bNDisableRaw ), bIPv6Available ( false ), Socket ( this, iPortNumber, iQosNumber, strServerBindIP, bNDisableIPv6, bIPv6Available ), - TcpServer ( this, strServerBindIP, iPortNumber, bNEnableIPv6 ), + TcpServer ( this, strServerBindIP, iPortNumber ), Logging(), iFrameCount ( 0 ), HighPrecisionTimer ( bNUseDoubleSystemFrameSize ), diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index 59d1ec0e71..d22cfd4d48 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -25,11 +25,10 @@ #include "server.h" -CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ) : +CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort ) : pServer ( pNServP ), strServerBindIP ( strServerBindIP ), iPort ( iPort ), - bEnableIPv6 ( bEnableIPv6 ), pTcpServer ( new QTcpServer ( this ) ) { connect ( pTcpServer, &QTcpServer::newConnection, this, &CTcpServer::OnNewConnection ); @@ -53,9 +52,9 @@ bool CTcpServer::Start() } // default to any-address for either both IP protocols or just IPv4 - QHostAddress hostAddress = bEnableIPv6 ? QHostAddress::Any : QHostAddress::AnyIPv4; + QHostAddress hostAddress = pServer->IsIPv6Available() ? QHostAddress::Any : QHostAddress::AnyIPv4; - if ( !bEnableIPv6 ) + if ( !pServer->IsIPv6Available() ) { if ( !strServerBindIP.isEmpty() ) { diff --git a/src/tcpserver.h b/src/tcpserver.h index 6e32c66c50..a707ed7525 100644 --- a/src/tcpserver.h +++ b/src/tcpserver.h @@ -46,7 +46,7 @@ class CTcpServer : public QObject Q_OBJECT public: - CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ); + CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort ); ~CTcpServer(); bool Start(); @@ -55,7 +55,6 @@ class CTcpServer : public QObject CServer* pServer; // for server const QString strServerBindIP; const int iPort; - const bool bEnableIPv6; QTcpServer* pTcpServer; private slots: From d80a51276fb24edd254c8e24f99f4b69ac6e123e Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 9 Jun 2026 18:30:33 +0100 Subject: [PATCH 45/50] Only quote port number in TCP server start message. The displayed address of 0.0.0.0 was misleading for a dual-stack socket --- src/tcpserver.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index d22cfd4d48..ee3dc57a68 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -64,8 +64,7 @@ bool CTcpServer::Start() if ( pTcpServer->listen ( hostAddress, iPort ) ) { - qInfo() << qUtf8Printable ( - QString ( "- Jamulus-TCP: Server started on %1:%2" ).arg ( pTcpServer->serverAddress().toString() ).arg ( pTcpServer->serverPort() ) ); + qInfo() << qUtf8Printable ( QString ( "- Jamulus-TCP: Server started on port %1" ).arg ( pTcpServer->serverPort() ) ); return true; } qInfo() << "- Jamulus-TCP: Unable to start server:" << pTcpServer->errorString(); From 6646d907111663c9d44f20cd572f52b651597c22 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 9 Jun 2026 22:20:00 +0100 Subject: [PATCH 46/50] Small changes to address review comments --- src/client.cpp | 7 ++++--- src/server.cpp | 6 +++--- src/tcpconnection.cpp | 5 +++-- src/tcpserver.cpp | 5 +++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 62764cf68c..52bf655360 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -284,16 +284,17 @@ 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 + // timer for TCP connect timeout because Qt defaults to 30 seconds + // and we want it to be 3 seconds (TCP_CONNECT_TIMEOUT_MS) QTimer* pTimer = new QTimer ( this ); pTimer->setSingleShot ( true ); - connect ( pTimer, &QTimer::timeout, this, [this, pSocket, pTimer]() { + connect ( pTimer, &QTimer::timeout, this, [this, pSocket, pTimer, InetAddr]() { if ( pSocket->state() != QAbstractSocket::ConnectedState ) { pSocket->abort(); pSocket->deleteLater(); - qDebug() << "- TCP connect timeout"; + qWarning() << "- Jamulus-TCP: timeout connecting to" << InetAddr.toString(); } pTimer->deleteLater(); } ); diff --git a/src/server.cpp b/src/server.cpp index 2b1d83cb97..a34985686a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -590,20 +590,20 @@ void CServer::OnCLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpCon { // ID out of range or channel not connected - reject connection pTcpConnection->disconnectFromHost(); - qDebug() << "- rejected invalid client ID"; + qWarning() << "- Jamulus-TCP: rejected invalid client ID"; return; } CChannel* pChannel = &vecChannels[iChanID]; - qDebug() << "- request to link TCP connection with UDP client at" << pChannel->GetAddress().toString(); + qInfo() << "- Jamulus-TCP: 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"; + qWarning() << "- Jamulus-TCP: rejected mismatched IP address"; return; } diff --git a/src/tcpconnection.cpp b/src/tcpconnection.cpp index 3c02c4ca20..716b035cba 100644 --- a/src/tcpconnection.cpp +++ b/src/tcpconnection.cpp @@ -81,7 +81,7 @@ CTcpConnection::CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcp void CTcpConnection::OnDisconnected() { - qDebug() << "- Jamulus-TCP: disconnected from:" << tcpAddress.toString(); + qInfo() << "- Jamulus-TCP: disconnected from:" << tcpAddress.toString(); TimerKeepalive.stop(); TimerIdleTimeout.stop(); pTcpSocket->deleteLater(); @@ -179,7 +179,7 @@ void CTcpConnection::OnReadyRead() } else { - qDebug() << "- Jamulus-TCP: failed to parse frame"; + qWarning() << "- Jamulus-TCP: failed to parse frame"; } iPos = 0; // ready for next message, if any @@ -205,6 +205,7 @@ void CTcpConnection::OnTimerKeepalive() void CTcpConnection::OnTimerIdleTimeout() { // qDebug() << "- ConnTimeout timer" << this << "from TCP" << tcpAddress.toString(); + qWarning() << "- Jamulus-TCP: idle timeout - disconnecting"; disconnectFromHost(); } diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp index ee3dc57a68..22b5ebb8f4 100644 --- a/src/tcpserver.cpp +++ b/src/tcpserver.cpp @@ -64,10 +64,11 @@ bool CTcpServer::Start() if ( pTcpServer->listen ( hostAddress, iPort ) ) { - qInfo() << qUtf8Printable ( QString ( "- Jamulus-TCP: Server started on port %1" ).arg ( pTcpServer->serverPort() ) ); + qInfo() << qUtf8Printable ( QString ( "- Jamulus-TCP: server started on port %1" ).arg ( pTcpServer->serverPort() ) ); return true; } - qInfo() << "- Jamulus-TCP: Unable to start server:" << pTcpServer->errorString(); + qWarning() << qUtf8Printable ( + QString ( "- Jamulus-TCP: unable to start server on port %1: %2" ).arg ( pTcpServer->serverPort() ).arg ( pTcpServer->errorString() ) ); return false; } From 75f76fb84286bb4d9ef8cdd737e2f2950be038a5 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 11 Jun 2026 17:30:04 +0100 Subject: [PATCH 47/50] Improve naming of TCP signals and slots --- src/client.cpp | 6 +++--- src/client.h | 4 ++-- src/clientdlg.cpp | 2 +- src/clientdlg.h | 2 +- src/protocol.cpp | 2 +- src/protocol.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 52bf655360..286fd512d3 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -169,7 +169,7 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &ConnLessProtocol, &CProtocol::CLRedServerListReceived, this, &CClient::CLRedServerListReceived ); - QObject::connect ( &ConnLessProtocol, &CProtocol::CLTcpSupported, this, &CClient::OnCLTcpSupported ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLTcpSupportedReceived, this, &CClient::OnCLTcpSupportedReceived ); QObject::connect ( &ConnLessProtocol, &CProtocol::CLConnClientsListMesReceived, this, &CClient::OnCLConnClientsListMesReceived ); @@ -1109,13 +1109,13 @@ void CClient::OnRawAudioSupported() } } -void CClient::OnCLTcpSupported ( CHostAddress InetAddr, int iID ) +void CClient::OnCLTcpSupportedReceived ( 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 + emit CLTcpSupportedReceived ( InetAddr, iID ); // pass to connect dialog return; } diff --git a/src/client.h b/src/client.h index 1218ceaf9a..4aa13bbc0d 100644 --- a/src/client.h +++ b/src/client.h @@ -477,7 +477,7 @@ protected slots: } } void OnCLPingReceived ( CHostAddress InetAddr, int iMs ); - void OnCLTcpSupported ( CHostAddress InetAddr, int iID ); + void OnCLTcpSupportedReceived ( CHostAddress InetAddr, int iID ); void OnSendCLProtMessage ( CHostAddress InetAddr, CVector vecMessage, CTcpConnection* pTcpConnection, enum EProtoMode eProtoMode ); @@ -516,7 +516,7 @@ public slots: void CLRedServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ); - void CLTcpSupported ( CHostAddress InetAddr, int iID ); + void CLTcpSupportedReceived ( CHostAddress InetAddr, int iID ); void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 302f8c4b43..de5e13115a 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -529,7 +529,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QObject::connect ( pClient, &CClient::CLRedServerListReceived, this, &CClientDlg::OnCLRedServerListReceived ); - QObject::connect ( pClient, &CClient::CLTcpSupported, this, &CClientDlg::OnCLTcpSupported ); + QObject::connect ( pClient, &CClient::CLTcpSupportedReceived, this, &CClientDlg::OnCLTcpSupportedReceived ); QObject::connect ( pClient, &CClient::CLConnClientsListMesReceived, this, &CClientDlg::OnCLConnClientsListMesReceived ); diff --git a/src/clientdlg.h b/src/clientdlg.h index b33f8a5bac..eb1546d858 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -242,7 +242,7 @@ public slots: ConnectDlg.SetServerList ( InetAddr, vecServerInfo, true ); } - void OnCLTcpSupported ( CHostAddress InetAddr, int iID ) { ConnectDlg.SetTcpSupported ( InetAddr, iID ); } + void OnCLTcpSupportedReceived ( CHostAddress InetAddr, int iID ) { ConnectDlg.SetTcpSupported ( InetAddr, iID ); } void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ) { diff --git a/src/protocol.cpp b/src/protocol.cpp index f496a750ff..8bea781cfa 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -2766,7 +2766,7 @@ bool CProtocol::EvaluateCLTcpSupportedMes ( const CHostAddress& InetAddr, const } // invoke message action - emit CLTcpSupported ( InetAddr, static_cast ( GetValFromStream ( vecData, iPos, 2 ) ) ); + emit CLTcpSupportedReceived ( InetAddr, static_cast ( GetValFromStream ( vecData, iPos, 2 ) ) ); return false; // no error } diff --git a/src/protocol.h b/src/protocol.h index f01a7d3706..26cf129b3c 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -405,6 +405,6 @@ public slots: void CLRegisterServerResp ( CHostAddress InetAddr, ESvrRegResult eStatus ); void CLReqServerFeatures ( CHostAddress InetAddr ); void CLReqWelcomeMessage ( CHostAddress InetAddr ); - void CLTcpSupported ( CHostAddress InetAddr, int iID ); + void CLTcpSupportedReceived ( CHostAddress InetAddr, int iID ); void CLClientIDReceived ( CHostAddress InetAddr, int iChanID, CTcpConnection* pTcpConnection ); }; From 24409a9c7cf9728e46b3b521e5e59f62e82e1095 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 19 Jun 2026 16:52:19 +0100 Subject: [PATCH 48/50] Update protocol ID values --- src/protocol.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protocol.h b/src/protocol.h index 26cf129b3c..3effeea811 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -111,8 +111,8 @@ #define PROTMESSID_CLM_REQ_SERVER_FEATURES 1020 // request server features #define PROTMESSID_CLM_WELCOME_MESSAGE 1021 // server welcome message #define PROTMESSID_CLM_REQ_WELCOME_MESSAGE 1022 // request server welcome message -#define PROTMESSID_CLM_TCP_SUPPORTED 1019 // TCP is supported -#define PROTMESSID_CLM_CLIENT_ID 1020 // Client ID associated with TCP connection +#define PROTMESSID_CLM_TCP_SUPPORTED 1023 // TCP is supported +#define PROTMESSID_CLM_CLIENT_ID 1024 // Client ID associated with TCP connection // special IDs #define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages From ccaee22097a2175eabecf0cb5bcb4572e3fc9f28 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Fri, 19 Jun 2026 17:11:51 +0100 Subject: [PATCH 49/50] Add FS_TCP_ENABLED to server features --- src/server.cpp | 3 +++ src/util.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index a34985686a..4cebfd5960 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -534,6 +534,9 @@ void CServer::OnCLReqServerFeatures ( CHostAddress RecHostAddr ) // Licence agreement required? (argument -L) iFeatures |= ( ( eLicenceType != LT_NO_LICENCE ) << FS_HAS_LICENCE ); + // TCP enabled? (argument --enabletcp) + iFeatures |= ( bEnableTcp << FS_TCP_ENABLED ); + // TODO: // Running a GUI? (argument -n disables the GUI) // iFeatures |= ( << FS_HAS_GUI ); diff --git a/src/util.h b/src/util.h index 1185c56936..bc4ff11505 100644 --- a/src/util.h +++ b/src/util.h @@ -633,7 +633,8 @@ enum EFeatureSet FS_IS_LOGGING = 9, FS_HAS_LICENCE = 10, FS_HAS_GUI = 11, - FS_RPC_ENABLED = 12 + FS_RPC_ENABLED = 12, + FS_TCP_ENABLED = 13 }; inline QString DirectoryTypeToString ( EDirectoryType eAddrType ) From 1ac1a028a67314e6393528e0d23e9af709022ca4 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 22 Jun 2026 17:26:55 +0100 Subject: [PATCH 50/50] Support using a CHostAddress as a QHash key --- src/util.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/util.h b/src/util.h index bc4ff11505..a4a6d8f36d 100644 --- a/src/util.h +++ b/src/util.h @@ -827,6 +827,18 @@ class CHostAddress quint16 iPort; }; +// support using a CHostAddress as a QHash key +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) +using qhash_result_t = uint; +#else +using qhash_result_t = size_t; +#endif +inline qhash_result_t qHash ( const CHostAddress& addr, qhash_result_t seed = 0 ) +{ + seed = qHash ( addr.InetAddr, seed ); + return qHash ( addr.iPort, seed ); +} + // Instrument picture data base ------------------------------------------------ // this is a pure static class class CInstPictures