1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-20 07:49:42 +01:00

Introduce the Dpointer concept to ConnectionManager

This commit is contained in:
Uwe L. Korn 2013-06-07 16:35:29 +02:00
parent 26dbc9d1cc
commit 494e75bff1
3 changed files with 105 additions and 54 deletions

View File

@ -16,7 +16,8 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ConnectionManager.h"
#include "ConnectionManager_p.h"
#include "ControlConnection.h"
#include "QTcpSocketExtra.h"
#include "Servent.h"
@ -30,11 +31,16 @@
#include <qtconcurrentrun.h>
ConnectionManager::ConnectionManager( const QString &nodeid )
: m_nodeid( nodeid )
: d_ptr( new ConnectionManagerPrivate( this, nodeid ) )
{
// TODO sth?
}
ConnectionManager::~ConnectionManager()
{
delete d_ptr;
}
void
ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo )
{
@ -45,7 +51,7 @@ ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo )
void
ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo )
{
m_mutex.lock();
d_func()->mutex.lock();
// Respect different behaviour before 0.7.100
peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Trying to connect to client with version " << peerInfo->versionString().split(' ').last() << TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" );
if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.100" ) < 0)
@ -63,7 +69,7 @@ ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo
// We connected to the peer, so we are done here.
}
}
m_mutex.unlock();
d_func()->mutex.unlock();
return;
}
foreach ( SipInfo info, peerInfo->sipInfos() )
@ -78,7 +84,7 @@ ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo
return;
}
}
m_mutex.unlock();
d_func()->mutex.unlock();
}
void
@ -87,78 +93,78 @@ ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool l
// Lock, so that we will not attempt to do two parallell connects.
if (lock)
{
m_mutex.lock();
d_func()->mutex.lock();
}
// Check that we are not already connected to this peer
ControlConnection* cconn = Servent::instance()->lookupControlConnection( peerInfo->nodeId() );
if ( cconn != NULL || !m_controlConnection.isNull() )
if ( cconn != NULL || !d_func()->controlConnection.isNull() )
{
// We are already connected to this peer, so just add some more details.
peerInfoDebug( peerInfo ) << "Existing connection found, not connecting.";
cconn->addPeerInfo( peerInfo );
if ( cconn != NULL )
{
m_controlConnection = QPointer<ControlConnection>(cconn);
d_func()->controlConnection = QPointer<ControlConnection>(cconn);
}
m_mutex.unlock();
d_func()->mutex.unlock();
return;
// TODO: Keep the peerInfo in mind for reconnecting
// FIXME: Do we need this for reconnecting if the connection drops?
}
// If we are not connected, try to connect
m_currentPeerInfo = peerInfo;
d_func()->currentPeerInfo = peerInfo;
peerInfoDebug( peerInfo ) << "No existing connection found, trying to connect.";
m_sipCandidates.append( peerInfo->sipInfos() );
d_func()->sipCandidates.append( peerInfo->sipInfos() );
QVariantMap m;
m["conntype"] = "accept-offer";
m["key"] = peerInfo->key();
m["nodeid"] = Database::instance()->impl()->dbid();
m_controlConnection = QPointer<ControlConnection>( new ControlConnection( Servent::instance() ) );
m_controlConnection->setShutdownOnEmptyPeerInfos( false );
m_controlConnection->addPeerInfo( peerInfo );
m_controlConnection->setFirstMessage( m );
d_func()->controlConnection = QPointer<ControlConnection>( new ControlConnection( Servent::instance() ) );
d_func()->controlConnection->setShutdownOnEmptyPeerInfos( false );
d_func()->controlConnection->addPeerInfo( peerInfo );
d_func()->controlConnection->setFirstMessage( m );
if ( peerInfo->id().length() )
m_controlConnection->setName( peerInfo->contactId() );
d_func()->controlConnection->setName( peerInfo->contactId() );
if ( peerInfo->nodeId().length() )
m_controlConnection->setId( peerInfo->nodeId() );
d_func()->controlConnection->setId( peerInfo->nodeId() );
m_controlConnection->setProperty( "nodeid", peerInfo->nodeId() );
d_func()->controlConnection->setProperty( "nodeid", peerInfo->nodeId() );
Servent::instance()->registerControlConnection( m_controlConnection.data() );
Servent::instance()->registerControlConnection( d_func()->controlConnection.data() );
tryConnect();
}
void ConnectionManager::tryConnect()
{
// ATTENTION: mutex should be already locked by the calling function.
Q_ASSERT( !m_controlConnection.isNull() );
Q_ASSERT( !d_func()->controlConnection.isNull() );
if ( m_sipCandidates.isEmpty() )
if ( d_func()->sipCandidates.isEmpty() )
{
// No more possibilities to connect.
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << m_controlConnection->name() << " skipping.";
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << d_func()->controlConnection->name() << " skipping.";
// Clean up.
m_currentPeerInfo.clear();
delete m_controlConnection.data();
m_mutex.unlock();
d_func()->currentPeerInfo.clear();
delete d_func()->controlConnection.data();
d_func()->mutex.unlock();
return;
}
// Use first available SIP endpoint and remove it from the list
SipInfo info = m_sipCandidates.takeFirst();
SipInfo info = d_func()->sipCandidates.takeFirst();
if ( !info.isVisible() )
{
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one";
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one";
tryConnect();
return;
}
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << info.host() << ":" << info.port();
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << info.host() << ":" << info.port();
Q_ASSERT( info.port() > 0 );
// Check that we are not connecting to ourselves
@ -166,31 +172,31 @@ void ConnectionManager::tryConnect()
{
if ( info.host() == ha.toString() && info.port() == Servent::instance()->port() )
{
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves.";
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves.";
tryConnect();
return;
}
}
if ( info.host() == Servent::instance()->additionalAddress() && info.port() == Servent::instance()->additionalPort() )
{
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves.";
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves.";
tryConnect();
return;
}
// We should have already setup a first message in connectToPeer
Q_ASSERT( !m_controlConnection->firstMessage().isNull() );
Q_ASSERT( !d_func()->controlConnection->firstMessage().isNull() );
QTcpSocketExtra* sock = new QTcpSocketExtra();
sock->setConnectTimeout( CONNECT_TIMEOUT );
sock->_disowned = false;
sock->_conn = m_controlConnection.data();
sock->_conn = d_func()->controlConnection.data();
sock->_outbound = true;
connect( sock, SIGNAL( connected() ), this, SLOT( socketConnected() ) );
connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) );
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting socket to " << info.host() << ":" << info.port();
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connecting socket to " << info.host() << ":" << info.port();
sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite );
sock->moveToThread( thread() );
}
@ -199,10 +205,10 @@ void
ConnectionManager::socketError( QAbstractSocket::SocketError error )
{
Q_UNUSED( error );
Q_ASSERT( !m_controlConnection.isNull() );
Q_ASSERT( !d_func()->controlConnection.isNull() );
QTcpSocketExtra* sock = (QTcpSocketExtra*)sender();
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << sock->peerAddress().toString() << " failed: " << sock->errorString();
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << sock->peerAddress().toString() << " failed: " << sock->errorString();
sock->deleteLater();
// Try to connect with the next available SipInfo.
@ -215,35 +221,35 @@ ConnectionManager::socketConnected()
{
QTcpSocketExtra* sock = (QTcpSocketExtra*)sender();
peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connected to hostaddr: " << sock->peerAddress() << ", hostname:" << sock->peerName();
peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connected to hostaddr: " << sock->peerAddress() << ", hostname:" << sock->peerName();
Q_ASSERT( !sock->_conn.isNull() );
handoverSocket( sock );
m_currentPeerInfo.clear();
m_mutex.unlock();
d_func()->currentPeerInfo.clear();
d_func()->mutex.unlock();
}
void
ConnectionManager::handoverSocket( QTcpSocketExtra* sock )
{
Q_ASSERT( !m_controlConnection.isNull() );
Q_ASSERT( !d_func()->controlConnection.isNull() );
Q_ASSERT( sock );
Q_ASSERT( m_controlConnection->socket().isNull() );
Q_ASSERT( d_func()->controlConnection->socket().isNull() );
Q_ASSERT( sock->isValid() );
disconnect( sock, SIGNAL( disconnected() ), sock, SLOT( deleteLater() ) );
disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) );
sock->_disowned = true;
m_controlConnection->setOutbound( sock->_outbound );
m_controlConnection->setPeerPort( sock->peerPort() );
d_func()->controlConnection->setOutbound( sock->_outbound );
d_func()->controlConnection->setPeerPort( sock->peerPort() );
m_controlConnection->start( sock );
d_func()->controlConnection->start( sock );
// ControlConntection is now connected, now it can be destroyed if the PeerInfos disappear
m_controlConnection->setShutdownOnEmptyPeerInfos( true );
m_currentPeerInfo.clear();
m_mutex.unlock();
d_func()->controlConnection->setShutdownOnEmptyPeerInfos( true );
d_func()->currentPeerInfo.clear();
d_func()->mutex.unlock();
}

View File

@ -25,8 +25,8 @@
#include <QAbstractSocket>
#include <QObject>
#include <QMutex>
class ConnectionManagerPrivate;
class QTcpSocketExtra;
class DLLEXPORT ConnectionManager : public QObject
@ -36,6 +36,7 @@ class DLLEXPORT ConnectionManager : public QObject
public:
static QSharedPointer<ConnectionManager> getManagerForNodeId( const QString& nodeid );
ConnectionManager( const QString& nodeid );
~ConnectionManager();
void handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo );
@ -44,6 +45,9 @@ private slots:
void socketError( QAbstractSocket::SocketError error );
private:
Q_DECLARE_PRIVATE( ConnectionManager )
ConnectionManagerPrivate* d_ptr;
void connectToPeer(const Tomahawk::peerinfo_ptr& peerInfo , bool lock);
void handleSipInfoPrivate( const Tomahawk::peerinfo_ptr& peerInfo );
@ -56,13 +60,6 @@ private:
* Attempt to connect to the peer using the current stored information.
*/
void tryConnect();
// We just keep this for debug purposes and only during connection attempts.
Tomahawk::peerinfo_ptr m_currentPeerInfo;
QString m_nodeid;
QPointer<ControlConnection> m_controlConnection;
QList<SipInfo> m_sipCandidates;
QMutex m_mutex;
};
#endif // CONNECTIONMANAGER_H

View File

@ -0,0 +1,48 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONNECTIONMANAGER_P_H
#define CONNECTIONMANAGER_P_H
#include "ConnectionManager.h"
#include <QMutex>
class ConnectionManagerPrivate : public QObject
{
Q_OBJECT
public:
ConnectionManagerPrivate( ConnectionManager* q, const QString& _nodeid )
: q_ptr ( q )
, nodeid( _nodeid )
{
}
ConnectionManager* q_ptr;
Q_DECLARE_PUBLIC ( ConnectionManager )
private:
// We just keep this for debug purposes and only during connection attempts.
Tomahawk::peerinfo_ptr currentPeerInfo;
QString nodeid;
QPointer<ControlConnection> controlConnection;
QList<SipInfo> sipCandidates;
QMutex mutex;
};
#endif // CONNECTIONMANAGER_P_H