Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions src/channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,22 +400,6 @@ void CChannel::OnChangeChanPan ( int iChanID, float fNewPan ) { SetPan ( iChanID

void CChannel::OnChangeChanInfo ( CChannelCoreInfo ChanInfo ) { SetChanInfo ( ChanInfo ); }

bool CChannel::GetAddress ( CHostAddress& RetAddr )
{
QMutexLocker locker ( &Mutex );

if ( IsConnected() )
{
RetAddr = InetAddr;
return true;
}
else
{
RetAddr = CHostAddress();
return false;
}
}

void CChannel::OnNetTranspPropsReceived ( CNetworkTransportProps NetworkTransportProps )
{
// only the server shall act on network transport properties message
Expand Down
1 change: 0 additions & 1 deletion src/channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ class CChannel : public QObject
bool IsEnabled() { return bIsEnabled; }

void SetAddress ( const CHostAddress NAddr ) { InetAddr = NAddr; }
bool GetAddress ( CHostAddress& RetAddr );
const CHostAddress& GetAddress() const { return InetAddr; }

void ResetInfo()
Expand Down
196 changes: 126 additions & 70 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ CServer::CServer ( const int iNewMaxNumChan,
bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ),
bUseMultithreading ( bNUseMultithreading ),
iMaxNumChannels ( iNewMaxNumChan ),
iCurNumChannels ( 0 ),
Socket ( this, iPortNumber, iQosNumber, strServerBindIP, bNEnableIPv6 ),
Logging(),
iFrameCount ( 0 ),
Expand Down Expand Up @@ -410,6 +411,7 @@ CServer::CServer ( const int iNewMaxNumChan,
for ( i = 0; i < iMaxNumChannels; i++ )
{
vecChannels[i].SetEnable ( true );
vecChannelOrder[i] = i;
}

int iAvailableCores = QThread::idealThreadCount();
Expand Down Expand Up @@ -1054,10 +1056,15 @@ void CServer::DecodeReceiveData ( const int iChanCnt, const int iNumClients )
emit ClientDisconnected ( iCurChanID ); // TODO do this outside the mutex lock?
}

FreeChannel ( iCurChanID ); // note that the channel is now not in use
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be before telling the JamController the client left? (JamController has its own structures to worry about IIRC.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think so. I thought it would be better if the channel still existed at the point the jam controller was notified. But in fact the channel object continues to exist - FreeChannel() just removes it from the channel order list and the binary search.

But I admit I haven't yet tested this code with recording enabled. It would be good to do so.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I've run a test server with recording enabled, and some test clients. All appears to work ok. I've also looked at the code for CJamRecorder::OnDisconnected(), and there doesn't appear to be anything that would interact with the server.cpp code.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay if it makes no difference.


// note that no mutex is needed for this shared resource since it is not a
// read-modify-write operation but an atomic write and also each thread can
// only set it to true and never to false
bChannelIsNowDisconnected = true;

// since the channel is no longer in use, we should return
return;
Comment thread
pljones marked this conversation as resolved.
}

// get pointer to coded data
Expand Down Expand Up @@ -1461,59 +1468,145 @@ void CServer::CreateOtherMuteStateChanged ( const int iCurChanID, const int iOth
}
}

int CServer::GetFreeChan()
int CServer::GetNumberOfConnectedClients()
{
// look for a free channel
for ( int i = 0; i < iMaxNumChannels; i++ )
QMutexLocker locker ( &MutexChanOrder );

return iCurNumChannels;
}

// CServer::FindChannel() is called for every received audio packet or connected protocol
// packet, to find the channel ID associated with the source IP address and port.
// In order to search as efficiently as possible, a list of active channel IDs is stored
// in vecChannelOrder[], sorted by IP and port (according to CHostAddress::Compare()),
// and a binary search is used to find either the existing channel, or the position at
// which a new channel should be inserted.

int CServer::FindChannel ( const CHostAddress& CheckAddr, const bool bAllowNew )
{
int iNewChanID = INVALID_CHANNEL_ID;

QMutexLocker locker ( &MutexChanOrder );

int l = 0, r = iCurNumChannels, i;

// use binary search to find the channel
while ( r > l )
{
if ( !vecChannels[i].IsConnected() )
int t = ( r + l ) / 2;
int cmp = CheckAddr.Compare ( vecChannels[vecChannelOrder[t]].GetAddress() );

if ( cmp == 0 )
{
return i;
// address and port match
return vecChannelOrder[t];
}

if ( cmp < 0 )
{
l = t + 1;
}
else
{
r = t;
}
}

// existing channel not found - return if we cannot create a new channel
if ( !bAllowNew || iCurNumChannels >= iMaxNumChannels )
{
return INVALID_CHANNEL_ID;
}

// allocate a new channel
i = iCurNumChannels++; // save index of free channel and increment count
Comment thread
pljones marked this conversation as resolved.
iNewChanID = vecChannelOrder[i];
InitChannel ( iNewChanID, CheckAddr );

// now l == r == position in vecChannelOrder to insert iNewChanID
// move channel IDs up by one starting at the top and working down
while ( i > r )
{
int j = i--;
Comment thread
pljones marked this conversation as resolved.
vecChannelOrder[j] = vecChannelOrder[i];
}
// insert the new channel ID in the correct place
vecChannelOrder[i] = iNewChanID;

// DumpChannels ( __FUNCTION__ );

// no free channel found, return invalid ID
return INVALID_CHANNEL_ID;
return iNewChanID;
}

int CServer::GetNumberOfConnectedClients()
void CServer::InitChannel ( const int iNewChanID, const CHostAddress& InetAddr )
Comment thread
pljones marked this conversation as resolved.
{
int iNumConnClients = 0;
// initialize new channel by storing the calling host address
vecChannels[iNewChanID].SetAddress ( InetAddr );

// reset channel info
vecChannels[iNewChanID].ResetInfo();

// check all possible channels for connection status
// reset the channel gains/pans of current channel, at the same
// time reset gains/pans of this channel ID for all other channels
for ( int i = 0; i < iMaxNumChannels; i++ )
{
if ( vecChannels[i].IsConnected() )
vecChannels[iNewChanID].SetGain ( i, 1.0 );
vecChannels[iNewChanID].SetPan ( i, 0.5 );

// other channels (we do not distinguish the case if
// i == iCurChanID for simplicity)
vecChannels[i].SetGain ( iNewChanID, 1.0 );
vecChannels[i].SetPan ( iNewChanID, 0.5 );
}
}

// CServer::FreeChannel() is called to remove a channel from the list of active channels.
// The remaining ordered IDs are moved down by one space, and the freed ID is moved to the
// end, ready to be reused by the next new connection.

void CServer::FreeChannel ( const int iCurChanID )
{
QMutexLocker locker ( &MutexChanOrder );

for ( int i = 0; i < iCurNumChannels; i++ )
{
if ( vecChannelOrder[i] == iCurChanID )
{
// this channel is connected, increment counter
iNumConnClients++;
--iCurNumChannels;

// move channel IDs down by one starting at the bottom and working up
while ( i < iCurNumChannels )
{
int j = i++;
vecChannelOrder[j] = vecChannelOrder[i];
}
// put deleted channel at the end ready for re-use
vecChannelOrder[i] = iCurChanID;

// DumpChannels ( __FUNCTION__ );

return;
}
}

return iNumConnClients;
qWarning() << "FreeChannel() called with invalid channel ID";
}

int CServer::FindChannel ( const CHostAddress& CheckAddr )
void CServer::DumpChannels ( const QString& title )
{
CHostAddress InetAddr;
qDebug() << qUtf8Printable ( title );

// check for all possible channels if IP is already in use
for ( int i = 0; i < iMaxNumChannels; i++ )
{
// the "GetAddress" gives a valid address and returns true if the
// channel is connected
if ( vecChannels[i].GetAddress ( InetAddr ) )
int iChanID = vecChannelOrder[i];

if ( i == iCurNumChannels )
{
// IP found, return channel number
if ( InetAddr == CheckAddr )
{
return i;
}
qDebug() << "----------";
}
}

// IP not found, return invalid ID
return INVALID_CHANNEL_ID;
qDebug() << qUtf8Printable ( QString ( "%1: [%2] %3" ).arg ( i, 3 ).arg ( iChanID ).arg ( vecChannels[iChanID].GetAddress().toString() ) );
}
}

void CServer::OnProtcolCLMessageReceived ( int iRecID, CVector<uint8_t> vecbyMesBodyData, CHostAddress RecHostAddr )
Expand Down Expand Up @@ -1543,48 +1636,13 @@ bool CServer::PutAudioData ( const CVector<uint8_t>& vecbyRecBuf, const int iNum
QMutexLocker locker ( &Mutex );

bool bNewConnection = false; // init return value
bool bChanOK = true; // init with ok, might be overwritten

// Get channel ID ------------------------------------------------------
// check address
iCurChanID = FindChannel ( HostAdr );

if ( iCurChanID == INVALID_CHANNEL_ID )
{
// a new client is calling, look for free channel
iCurChanID = GetFreeChan();
iCurChanID = FindChannel ( HostAdr, true /* allow new */ );

if ( iCurChanID != INVALID_CHANNEL_ID )
{
// initialize current channel by storing the calling host
// address
vecChannels[iCurChanID].SetAddress ( HostAdr );

// reset channel info
vecChannels[iCurChanID].ResetInfo();

// reset the channel gains/pans of current channel, at the same
// time reset gains/pans of this channel ID for all other channels
for ( int i = 0; i < iMaxNumChannels; i++ )
{
vecChannels[iCurChanID].SetGain ( i, 1.0 );
vecChannels[iCurChanID].SetPan ( i, 0.5 );

// other channels (we do not distinguish the case if
// i == iCurChanID for simplicity)
vecChannels[i].SetGain ( iCurChanID, 1.0 );
vecChannels[i].SetPan ( iCurChanID, 0.5 );
}
}
else
{
// no free channel available
bChanOK = false;
}
}

// Put received audio data in jitter buffer ----------------------------
if ( bChanOK )
// If channel is valid or new, put received audio data in jitter buffer ----------------------------
if ( iCurChanID != INVALID_CHANNEL_ID )
{
// put packet in socket buffer
if ( vecChannels[iCurChanID].PutAudioData ( vecbyRecBuf, iNumBytesRead, HostAdr ) == PS_NEW_CONNECTION )
Expand All @@ -1603,8 +1661,6 @@ void CServer::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
CVector<int>& veciJitBufNumFrames,
CVector<int>& veciNetwFrameSizeFact )
{
CHostAddress InetAddr;

// init return values
vecHostAddresses.Init ( iMaxNumChannels );
vecsName.Init ( iMaxNumChannels );
Expand All @@ -1614,10 +1670,10 @@ void CServer::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
// check all possible channels
for ( int i = 0; i < iMaxNumChannels; i++ )
{
if ( vecChannels[i].GetAddress ( InetAddr ) )
if ( vecChannels[i].IsConnected() )
{
// get requested data
vecHostAddresses[i] = InetAddr;
vecHostAddresses[i] = vecChannels[i].GetAddress();
vecsName[i] = vecChannels[i].GetName();
veciJitBufNumFrames[i] = vecChannels[i].GetSockBufNumFrames();
veciNetwFrameSizeFact[i] = vecChannels[i].GetNetwFrameSizeFact();
Expand Down
15 changes: 11 additions & 4 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,10 @@ class CServer : public QObject, public CServerSlots<MAX_NUM_CHANNELS>
// access functions for actual channels
bool IsConnected ( const int iChanNum ) { return vecChannels[iChanNum].IsConnected(); }

int GetFreeChan();
int FindChannel ( const CHostAddress& CheckAddr );
int FindChannel ( const CHostAddress& CheckAddr, const bool bAllowNew = false );
void InitChannel ( const int iNewChanID, const CHostAddress& InetAddr );
void FreeChannel ( const int iCurChanID );
void DumpChannels ( const QString& title );
int GetNumberOfConnectedClients();
CVector<CChannelInfo> CreateChannelList();

Expand Down Expand Up @@ -305,8 +307,13 @@ class CServer : public QObject, public CServerSlots<MAX_NUM_CHANNELS>

// do not use the vector class since CChannel does not have appropriate
// copy constructor/operator
CChannel vecChannels[MAX_NUM_CHANNELS];
int iMaxNumChannels;
CChannel vecChannels[MAX_NUM_CHANNELS];
int iMaxNumChannels;

int iCurNumChannels;
int vecChannelOrder[MAX_NUM_CHANNELS];
QMutex MutexChanOrder;

CProtocol ConnLessProtocol;
QMutex Mutex;
QMutex MutexWelcomeMessage;
Expand Down
44 changes: 44 additions & 0 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,50 @@ bool NetworkUtil::IsPrivateNetworkIP ( const QHostAddress& qhAddr )
return false;
}

// CHostAddress methods
// Compare() - compare two CHostAddress objects, and return an ordering between them:
// 0 - they are equal
// <0 - this comes before other
// >0 - this comes after other
// The order is not important, so long as it is consistent, for use in a binary search.

int CHostAddress::Compare ( const CHostAddress& other ) const
{
// compare port first, as it is cheap, and clients will often use random ports

if ( iPort != other.iPort )
{
return (int) iPort - (int) other.iPort;
}

// compare protocols before addresses

QAbstractSocket::NetworkLayerProtocol thisProto = InetAddr.protocol();
QAbstractSocket::NetworkLayerProtocol otherProto = other.InetAddr.protocol();

if ( thisProto != otherProto )
{
return (int) thisProto - (int) otherProto;
}

// now we know both addresses are the same protocol

if ( thisProto == QAbstractSocket::IPv6Protocol )
{
// compare IPv6 addresses
Q_IPV6ADDR thisAddr = InetAddr.toIPv6Address();
Q_IPV6ADDR otherAddr = other.InetAddr.toIPv6Address();

return memcmp ( &thisAddr, &otherAddr, sizeof ( Q_IPV6ADDR ) );
}

// compare IPv4 addresses
quint32 thisAddr = InetAddr.toIPv4Address();
quint32 otherAddr = other.InetAddr.toIPv4Address();

return (int) thisAddr - (int) otherAddr;
}

// Instrument picture data base ------------------------------------------------
CVector<CInstPictures::CInstPictProps>& CInstPictures::GetTable ( const bool bReGenerateTable )
{
Expand Down
2 changes: 2 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,8 @@ class CHostAddress
// compare operator
bool operator== ( const CHostAddress& CompAddr ) const { return ( ( CompAddr.InetAddr == InetAddr ) && ( CompAddr.iPort == iPort ) ); }

int Compare ( const CHostAddress& other ) const;

QString toString ( const EStringMode eStringMode = SM_IP_PORT ) const
{
QString strReturn = InetAddr.toString();
Expand Down