Skip to content

Commit 3513a6e

Browse files
committed
Initial skeleton for TCP protocol server
1 parent 67d360f commit 3513a6e

7 files changed

Lines changed: 277 additions & 0 deletions

File tree

Jamulus.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ HEADERS += src/plugins/audioreverb.h \
399399
src/serverlogging.h \
400400
src/settings.h \
401401
src/socket.h \
402+
src/tcpserver.h \
402403
src/util.h \
403404
src/recorder/jamrecorder.h \
404405
src/recorder/creaperproject.h \
@@ -507,6 +508,7 @@ SOURCES += src/plugins/audioreverb.cpp \
507508
src/settings.cpp \
508509
src/signalhandler.cpp \
509510
src/socket.cpp \
511+
src/tcpserver.cpp \
510512
src/util.cpp \
511513
src/recorder/jamrecorder.cpp \
512514
src/recorder/creaperproject.cpp \

src/protocol.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,16 @@ bool CProtocol::EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, con
25822582
/******************************************************************************\
25832583
* Message generation and parsing *
25842584
\******************************************************************************/
2585+
int CProtocol::GetBodyLength ( const CVector<uint8_t>& vecbyData )
2586+
{
2587+
int iCurPos = 5; // position of length calculation
2588+
2589+
// 2 bytes length
2590+
const int iLenBy = static_cast<int> ( GetValFromStream ( vecbyData, iCurPos, 2 ) );
2591+
2592+
return iLenBy + 2; // remaining length to read, including CRC
2593+
}
2594+
25852595
bool CProtocol::ParseMessageFrame ( const CVector<uint8_t>& vecbyData,
25862596
const int iNumBytesIn,
25872597
CVector<uint8_t>& vecbyMesBodyData,

src/protocol.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ class CProtocol : public QObject
154154
void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr, const CVector<uint16_t>& vecLevelList, const int iNumClients );
155155
void CreateCLRegisterServerResp ( const CHostAddress& InetAddr, const ESvrRegResult eResult );
156156

157+
static int GetBodyLength ( const CVector<uint8_t>& vecbyData );
158+
157159
static bool ParseMessageFrame ( const CVector<uint8_t>& vecbyData,
158160
const int iNumBytesIn,
159161
CVector<uint8_t>& vecbyMesBodyData,

src/server.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ CServer::CServer ( const int iNewMaxNumChan,
5151
iMaxNumChannels ( iNewMaxNumChan ),
5252
iCurNumChannels ( 0 ),
5353
Socket ( this, iPortNumber, iQosNumber, strServerBindIP, bNEnableIPv6 ),
54+
TcpServer ( this, strServerBindIP, iPortNumber, bNEnableIPv6 ),
5455
Logging(),
5556
iFrameCount ( 0 ),
5657
bWriteStatusHTMLFile ( false ),
@@ -306,6 +307,10 @@ CServer::CServer ( const int iNewMaxNumChan,
306307
// start the socket (it is important to start the socket after all
307308
// initializations and connections)
308309
Socket.Start();
310+
if ( bEnableTcp )
311+
{
312+
TcpServer.Start();
313+
}
309314
}
310315

311316
template<unsigned int slotId>

src/server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "util.h"
4343
#include "serverlogging.h"
4444
#include "serverlist.h"
45+
#include "tcpserver.h"
4546
#include "recorder/jamcontroller.h"
4647

4748
#include "threadpool.h"
@@ -271,6 +272,7 @@ class CServer : public QObject, public CServerSlots<MAX_NUM_CHANNELS>
271272

272273
// actual working objects
273274
CHighPrioSocket Socket;
275+
CTcpServer TcpServer;
274276

275277
// logging
276278
CServerLogging Logging;

src/tcpserver.cpp

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/******************************************************************************\
2+
* Copyright (c) 2024
3+
*
4+
* Author(s):
5+
* Tony Mountifield
6+
*
7+
******************************************************************************
8+
*
9+
* This program is free software; you can redistribute it and/or modify it under
10+
* the terms of the GNU General Public License as published by the Free Software
11+
* Foundation; either version 2 of the License, or (at your option) any later
12+
* version.
13+
*
14+
* This program is distributed in the hope that it will be useful, but WITHOUT
15+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17+
* details.
18+
*
19+
* You should have received a copy of the GNU General Public License along with
20+
* this program; if not, write to the Free Software Foundation, Inc.,
21+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22+
*
23+
\******************************************************************************/
24+
25+
#include "tcpserver.h"
26+
27+
#include "server.h"
28+
#include "channel.h"
29+
30+
CTcpServer::CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 ) :
31+
pServer ( pNServP ),
32+
strServerBindIP ( strServerBindIP ),
33+
iPort ( iPort ),
34+
bEnableIPv6 ( bEnableIPv6 ),
35+
pTcpServer ( new QTcpServer ( this ) )
36+
{
37+
//// connect ( this, &CTcpServer::ProtocolCLMessageReceived, pServer, &CServer::OnProtocolCLMessageReceived );
38+
connect ( pTcpServer, &QTcpServer::newConnection, this, &CTcpServer::OnNewConnection );
39+
}
40+
41+
CTcpServer::~CTcpServer()
42+
{
43+
if ( pTcpServer->isListening() )
44+
{
45+
qInfo() << "- stopping Jamulus-TCP server";
46+
pTcpServer->close();
47+
}
48+
}
49+
50+
bool CTcpServer::Start()
51+
{
52+
if ( iPort < 0 )
53+
{
54+
return false;
55+
}
56+
57+
// default to any-address for either both IP protocols or just IPv4
58+
QHostAddress hostAddress = bEnableIPv6 ? QHostAddress::Any : QHostAddress::AnyIPv4;
59+
60+
if ( !bEnableIPv6 )
61+
{
62+
if ( !strServerBindIP.isEmpty() )
63+
{
64+
hostAddress = QHostAddress ( strServerBindIP );
65+
}
66+
}
67+
68+
if ( pTcpServer->listen ( hostAddress, iPort ) )
69+
{
70+
qInfo() << qUtf8Printable (
71+
QString ( "- Jamulus-TCP: Server started on %1:%2" ).arg ( pTcpServer->serverAddress().toString() ).arg ( pTcpServer->serverPort() ) );
72+
return true;
73+
}
74+
qInfo() << "- Jamulus-TCP: Unable to start server:" << pTcpServer->errorString();
75+
return false;
76+
}
77+
78+
void CTcpServer::OnNewConnection()
79+
{
80+
QTcpSocket* pSocket = pTcpServer->nextPendingConnection();
81+
if ( !pSocket )
82+
{
83+
return;
84+
}
85+
86+
// express IPv4 address as IPv4
87+
CHostAddress peerAddress ( pSocket->peerAddress(), pSocket->peerPort() );
88+
89+
if ( peerAddress.InetAddr.protocol() == QAbstractSocket::IPv6Protocol )
90+
{
91+
bool ok;
92+
quint32 ip4 = peerAddress.InetAddr.toIPv4Address ( &ok );
93+
if ( ok )
94+
{
95+
peerAddress.InetAddr.setAddress ( ip4 );
96+
}
97+
}
98+
99+
CTcpConnection* pTcpConnection = new CTcpConnection ( pSocket, peerAddress );
100+
101+
qDebug() << "- Jamulus-TCP: received connection from:" << peerAddress.InetAddr.toString();
102+
103+
// allocate memory for network receive and send buffer in samples
104+
CVector<uint8_t> vecbyRecBuf;
105+
vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF );
106+
107+
connect ( pSocket, &QTcpSocket::disconnected, [this, pTcpConnection]() {
108+
qDebug() << "- Jamulus-TCP: connection from:" << pTcpConnection->tcpAddress.InetAddr.toString() << "closed";
109+
pTcpConnection->pTcpSocket->deleteLater();
110+
delete pTcpConnection;
111+
} );
112+
113+
connect ( pSocket, &QTcpSocket::readyRead, [this, pTcpConnection, vecbyRecBuf]() {
114+
// handle received Jamulus protocol packet
115+
116+
// check if this is a protocol message
117+
int iRecCounter;
118+
int iRecID;
119+
CVector<uint8_t> vecbyMesBodyData;
120+
121+
long iNumBytesRead = pTcpConnection->pTcpSocket->read ( (char*) &vecbyRecBuf[0], MESS_HEADER_LENGTH_BYTE );
122+
if ( iNumBytesRead == -1 )
123+
{
124+
return;
125+
}
126+
127+
if ( iNumBytesRead < MESS_HEADER_LENGTH_BYTE )
128+
{
129+
qDebug() << "-- short read: expected" << MESS_HEADER_LENGTH_BYTE << "bytes, got" << iNumBytesRead;
130+
return;
131+
}
132+
133+
long iPayloadLength = CProtocol::GetBodyLength ( vecbyRecBuf );
134+
135+
long iNumBytesRead2 = pTcpConnection->pTcpSocket->read ( (char*) &vecbyRecBuf[MESS_HEADER_LENGTH_BYTE], iPayloadLength );
136+
if ( iNumBytesRead2 == -1 )
137+
{
138+
return;
139+
}
140+
141+
if ( iNumBytesRead2 < iPayloadLength )
142+
{
143+
qDebug() << "-- short read: expected" << iPayloadLength << "bytes, got" << iNumBytesRead2;
144+
return;
145+
}
146+
147+
iNumBytesRead += iNumBytesRead2;
148+
149+
qDebug() << "- Jamulus-TCP: received protocol message of length" << iNumBytesRead;
150+
151+
if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, iNumBytesRead, vecbyMesBodyData, iRecCounter, iRecID ) )
152+
{
153+
qDebug() << "- Jamulus-TCP: message parsed OK, ID =" << iRecID;
154+
155+
// this is a protocol message, check the type of the message
156+
if ( CProtocol::IsConnectionLessMessageID ( iRecID ) )
157+
{
158+
//### TODO: BEGIN ###//
159+
// a copy of the vector is used -> avoid malloc in real-time routine
160+
//// emit ProtocolCLMessageReceived ( iRecID, vecbyMesBodyData, peerAddress, pSocket );
161+
//### TODO: END ###//
162+
}
163+
else
164+
{
165+
//### TODO: BEGIN ###//
166+
// a copy of the vector is used -> avoid malloc in real-time routine
167+
// emit ProtocolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, peerAddress, pSocket );
168+
//### TODO: END ###//
169+
}
170+
}
171+
} );
172+
}
173+
174+
#if 0
175+
void CTcpServer::Send ( QTcpSocket* pSocket ) {
176+
// pSocket->write ( );
177+
}
178+
#endif

src/tcpserver.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/******************************************************************************\
2+
* Copyright (c) 2024
3+
*
4+
* Author(s):
5+
* Tony Mountifield
6+
*
7+
******************************************************************************
8+
*
9+
* This program is free software; you can redistribute it and/or modify it under
10+
* the terms of the GNU General Public License as published by the Free Software
11+
* Foundation; either version 2 of the License, or (at your option) any later
12+
* version.
13+
*
14+
* This program is distributed in the hope that it will be useful, but WITHOUT
15+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17+
* details.
18+
*
19+
* You should have received a copy of the GNU General Public License along with
20+
* this program; if not, write to the Free Software Foundation, Inc.,
21+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22+
*
23+
\******************************************************************************/
24+
25+
#pragma once
26+
27+
#include <QObject>
28+
#include <QTcpServer>
29+
#include <QTcpSocket>
30+
#include <QTimer>
31+
#include <QVector>
32+
#include <memory>
33+
34+
#include "global.h"
35+
#include "protocol.h"
36+
#include "util.h"
37+
38+
// The header files channel.h and server.h require to include this header file
39+
// so we get a cyclic dependency. To solve this issue, a prototype of the
40+
// channel class and server class is defined here.
41+
class CServer; // forward declaration of CServer
42+
class CChannel; // forward declaration of CChannel
43+
44+
/* Classes ********************************************************************/
45+
class CTcpServer : public QObject
46+
{
47+
Q_OBJECT
48+
49+
public:
50+
CTcpServer ( CServer* pNServP, const QString& strServerBindIP, int iPort, bool bEnableIPv6 );
51+
virtual ~CTcpServer();
52+
53+
bool Start();
54+
55+
private:
56+
CServer* pServer; // for server
57+
const QString strServerBindIP;
58+
const int iPort;
59+
const bool bEnableIPv6;
60+
QTcpServer* pTcpServer;
61+
62+
signals:
63+
void ProtocolCLMessageReceived ( int iRecID, CVector<uint8_t> vecbyMesBodyData, CHostAddress HostAdr, QTcpSocket* pTcpSocket );
64+
65+
protected slots:
66+
void OnNewConnection();
67+
};
68+
69+
class CTcpConnection
70+
{
71+
public:
72+
CTcpConnection ( QTcpSocket* pTcpSocket, const CHostAddress& tcpAddress ) : pTcpSocket ( pTcpSocket ), tcpAddress ( tcpAddress ) {}
73+
~CTcpConnection() {}
74+
75+
QTcpSocket* pTcpSocket;
76+
CHostAddress tcpAddress;
77+
CHostAddress udpAddress;
78+
};

0 commit comments

Comments
 (0)