Skip to content

Commit be8a775

Browse files
authored
Merge pull request #3805 from mcfnord/client-connection-state
Refactor Connect/Disconnect out to CClient with an explicit connection state
2 parents b3c9358 + 5fb6932 commit be8a775

5 files changed

Lines changed: 190 additions & 101 deletions

File tree

src/client.cpp

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ CClient::CClient ( const quint16 iPortNumber,
5959
strClientName ( strNClientName ),
6060
pSignalHandler ( CSignalHandler::getSingletonP() ),
6161
pSettings ( nullptr ),
62+
eConnectionState ( CS_DISCONNECTED ),
6263
Channel ( false ), /* we need a client channel -> "false" */
6364
CurOpusEncoder ( nullptr ),
6465
CurOpusDecoder ( nullptr ),
@@ -145,7 +146,7 @@ CClient::CClient ( const quint16 iPortNumber,
145146
// The first ConClientListMesReceived handler performs the necessary cleanup and has to run first:
146147
QObject::connect ( &Channel, &CChannel::ConClientListMesReceived, this, &CClient::OnConClientListMesReceived );
147148

148-
QObject::connect ( &Channel, &CChannel::Disconnected, this, &CClient::Disconnected );
149+
QObject::connect ( &Channel, &CChannel::Disconnected, this, &CClient::Stop );
149150

150151
QObject::connect ( &Channel, &CChannel::NewConnection, this, &CClient::OnNewConnection );
151152

@@ -626,6 +627,9 @@ bool CClient::SetServerAddr ( QString strNAddr )
626627
// apply address to the channel
627628
Channel.SetAddress ( HostAddress );
628629

630+
// By default, set server name to HostAddress. If using the Connect() method, this may be overwritten
631+
SetConnectedServerName ( HostAddress.toString() );
632+
629633
return true;
630634
}
631635
else
@@ -929,11 +933,9 @@ void CClient::OnHandledSignal ( int sigNum )
929933
{
930934
case SIGINT:
931935
case SIGTERM:
932-
// if connected, terminate connection (needed for headless mode)
933-
if ( IsRunning() )
934-
{
935-
Stop();
936-
}
936+
// tear down any pending or established connection first, so the server
937+
// is notified we are leaving (Disconnect() is a no-op if not connected)
938+
Disconnect();
937939

938940
// this should trigger OnAboutToQuit
939941
QCoreApplication::instance()->exit();
@@ -1027,6 +1029,9 @@ void CClient::OnClientIDReceived ( int iServerChanID )
10271029
SetRemoteChanGain ( iChanID, 0, false );
10281030
}
10291031

1032+
// the server has assigned us a channel ID, so the connection is established
1033+
SetConnectionState ( CS_CONNECTED );
1034+
10301035
emit ClientIDReceived ( iChanID );
10311036
}
10321037

@@ -1069,8 +1074,19 @@ void CClient::Start()
10691074
// Disable hibernation or display dimming if the app is running on Windows
10701075
SetThreadExecutionState ( ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED );
10711076
#endif
1077+
1078+
// the connection is requested now but not yet established: the transition
1079+
// to CS_CONNECTED happens when the server assigns our channel ID
1080+
// (see OnClientIDReceived)
1081+
SetConnectionState ( CS_CONNECTING );
1082+
1083+
emit Connecting ( GetConnectedServerName() );
10721084
}
10731085

1086+
/// @method
1087+
/// @brief Stops client and disconnects from server
1088+
/// @emit Disconnected
1089+
/// Use to set CClientDlg to show not being connected
10741090
void CClient::Stop()
10751091
{
10761092
// stop audio interface
@@ -1081,7 +1097,16 @@ void CClient::Stop()
10811097

10821098
// Fall back to opus in case raw was used
10831099
bRawAudioIsSupported = false;
1084-
Init();
1100+
try
1101+
{
1102+
Init();
1103+
}
1104+
catch ( const CGenErr& generr )
1105+
{
1106+
// a dead audio backend (e.g. JACK was shut down) must not prevent the
1107+
// disconnect message below from reaching the server
1108+
qWarning() << "Could not reinitialise the sound device while disconnecting:" << generr.GetErrorText();
1109+
}
10851110

10861111
// wait for approx. 100 ms to make sure no audio packet is still in the
10871112
// network queue causing the channel to be reconnected right after having
@@ -1112,6 +1137,74 @@ void CClient::Stop()
11121137
// Allow hibernation or display dimming if the app is running again (Windows)
11131138
SetThreadExecutionState ( ES_CONTINUOUS );
11141139
#endif
1140+
1141+
SetConnectionState ( CS_DISCONNECTED );
1142+
1143+
// emit Disconnected() to inform UI of disconnection
1144+
emit Disconnected();
1145+
}
1146+
1147+
/// @method
1148+
/// @brief Disconnects from the server if a connection is requested or established.
1149+
/// Idempotent: a no-op when already disconnected.
1150+
/// @emit Disconnected
1151+
void CClient::Disconnect()
1152+
{
1153+
// Key off the connection state, not IsRunning() (which tracks the audio
1154+
// device): the two diverge while connecting and in headless mode, and on
1155+
// SIGTERM we must still send the disconnect message to the server.
1156+
if ( GetConnectionState() != CS_DISCONNECTED )
1157+
{
1158+
Stop();
1159+
}
1160+
}
1161+
1162+
/// @method
1163+
/// @brief Connects to strServerAddress. If a connection is currently requested
1164+
/// or established, that connection is terminated first.
1165+
/// @emit Connecting (strServerName) if SetServerAddr was valid. emit happens through Start().
1166+
/// Use to set CClientDlg to show being connected
1167+
/// @emit ConnectingFailed (error) if an error occurred
1168+
/// Use to display error message in CClientDlg
1169+
/// @param strServerAddress - the server address to connect to
1170+
/// @param strServerName - the human readable server name passed to Connecting()
1171+
void CClient::Connect ( const QString& strServerAddress, const QString& strServerName )
1172+
{
1173+
try
1174+
{
1175+
// disconnect from any current server first so that connecting to a
1176+
// different server while connected behaves as a reconnect
1177+
Disconnect();
1178+
1179+
// Set server address and connect if valid address was supplied
1180+
if ( SetServerAddr ( strServerAddress ) )
1181+
{
1182+
SetConnectedServerName ( strServerName );
1183+
Start();
1184+
}
1185+
else
1186+
{
1187+
throw CGenErr ( tr ( "Received invalid server address. Please check for typos in the provided server address." ) );
1188+
}
1189+
}
1190+
catch ( const CGenErr& generr )
1191+
{
1192+
Stop();
1193+
emit ConnectingFailed ( generr.GetErrorText() );
1194+
}
1195+
}
1196+
1197+
/// @method
1198+
/// @brief Updates the connection state and, if it changed, notifies observers.
1199+
/// @emit ConnectionStateChanged (state) when the state actually changes
1200+
/// @param eNewConnectionState - the state to transition to
1201+
void CClient::SetConnectionState ( const EConnectionState eNewConnectionState )
1202+
{
1203+
if ( eConnectionState != eNewConnectionState )
1204+
{
1205+
eConnectionState = eNewConnectionState;
1206+
emit ConnectionStateChanged ( eConnectionState );
1207+
}
11151208
}
11161209

11171210
void CClient::Init()

src/client.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@
8989
#endif
9090

9191
/* Definitions ****************************************************************/
92+
// Client connection state -----------------------------------------------------
93+
enum EConnectionState
94+
{
95+
// a connection is "requested" as soon as the client starts the audio
96+
// stream towards the configured server and "established" once the server
97+
// has assigned a channel ID to the client
98+
CS_DISCONNECTED = 0, // no connection requested or established
99+
CS_CONNECTING = 1, // connection requested but not yet established
100+
CS_CONNECTED = 2 // connection established and current
101+
};
102+
92103
// audio in fader range
93104
#define AUD_FADER_IN_MIN 0
94105
#define AUD_FADER_IN_MAX 100
@@ -158,8 +169,15 @@ class CClient : public QObject
158169

159170
virtual ~CClient();
160171

161-
void Start();
162-
void Stop();
172+
void Disconnect();
173+
void Connect ( const QString& strServerAddress, const QString& strServerName );
174+
175+
// The ConnectedServerName is emitted by Connecting() to update the UI with a human readable server name
176+
void SetConnectedServerName ( const QString& strServerName ) { strConnectedServerName = strServerName; };
177+
QString GetConnectedServerName() const { return strConnectedServerName; };
178+
179+
EConnectionState GetConnectionState() const { return eConnectionState; }
180+
163181
bool IsRunning() { return Sound.IsRunning(); }
164182
bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); }
165183
bool SetServerAddr ( QString strNAddr );
@@ -342,6 +360,9 @@ class CClient : public QObject
342360
// callback function must be static, otherwise it does not work
343361
static void AudioCallback ( CVector<short>& psData, void* arg );
344362

363+
void Start();
364+
void Stop();
365+
345366
void Init();
346367
void ProcessSndCrdAudioData ( CVector<short>& vecsStereoSndCrd );
347368
void ProcessAudioDataIntern ( CVector<short>& vecsStereoSndCrd );
@@ -354,6 +375,13 @@ class CClient : public QObject
354375
void FreeClientChannel ( const int iServerChannelID );
355376
int FindClientChannel ( const int iServerChannelID, const bool bCreateIfNew ); // returns a client channel ID or INVALID_INDEX
356377
bool ReorderLevelList ( CVector<uint16_t>& vecLevelList ); // modifies vecLevelList, passed by reference
378+
// human-readable name of the current server, shown in the UI
379+
QString strConnectedServerName;
380+
381+
// current connection state; set only via SetConnectionState()
382+
EConnectionState eConnectionState;
383+
384+
void SetConnectionState ( const EConnectionState eNewConnectionState );
357385

358386
// only one channel is needed for client application
359387
CChannel Channel;
@@ -465,7 +493,8 @@ protected slots:
465493
{
466494
if ( InetAddr == Channel.GetAddress() )
467495
{
468-
emit Disconnected();
496+
// Stop client in case it received a Disconnection request
497+
Stop();
469498
}
470499
}
471500
void OnCLPingReceived ( CHostAddress InetAddr, int iMs );
@@ -508,7 +537,11 @@ protected slots:
508537

509538
void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector<uint16_t> vecLevelList );
510539

540+
void ConnectionStateChanged ( EConnectionState eConnectionState );
541+
void Connecting ( const QString& strServerName );
542+
void ConnectingFailed ( const QString& errorMessage );
511543
void Disconnected();
544+
512545
void SoundDeviceChanged ( QString strError );
513546
void ControllerInFaderLevel ( int iChannelIdx, int iValue );
514547
void ControllerInPanValue ( int iChannelIdx, int iValue );

0 commit comments

Comments
 (0)