Logo Search packages:      
Sourcecode: qnapi version File versions  Download package

qinterprocesschannel.cpp

Go to the documentation of this file.
/****************************************************************************
**
** Copyright (C) 2006-2008 fullmetalcoder <fullmetalcoder@hotmail.fr>
**
** This file is part of the Edyuk project <http://edyuk.org>
** 
** This file may be used under the terms of the GNU General Public License
** version 2 as published by the Free Software Foundation and appearing in the
** file GPL.txt included in the packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "qinterprocesschannel.h"

/*!
      \file qinterprocesschannel.cpp
      
      \brief Implementation of the QInterProcessChannel class.
*/

#include "qmanagedsocket.h"
#include "qmanagedrequest.h"

#include <QDir>
#include <QFile>
#include <QTimer>
#include <QSettings>
#include <QTcpSocket>
#include <QTcpServer>
#include <QApplication>

#define WAIT_TIMEOUT 5000

/*!
      \class QInterProcessChannel
      
      \brief A generic network-based communication channel for applications.
      
      QInterProcessChannel offers communication facilities between several instances of
      the same application. It initializes itself so that the first created instance acts
      as a "server" which recieve messages from the others instances. This is particularly
      handy when creating a single-instance application which, for example, uses file
      association through a command line interface.
      
      \see QSingleApplication
*/

/*!
      \brief Constructor
      
      Check for a valid server and create one if none found.
*/
00056 QInterProcessChannel::QInterProcessChannel(QObject *p)
 : QThread(p), pServer(0), pServerTimer(0)
{
      #ifdef Q_WS_WIN
      rcFile = QDir::tempPath() + QDir::separator() + QApplication::applicationName() + "rc";
      #else
      rcFile = QDir::tempPath() + QDir::separator() + QApplication::applicationName()
                  + "-" + getenv("USER") + "-" + getenv("DISPLAY") + "-"  + "rc";
      #endif

      init();
}

/*!
      \brief Destructor
*/
00072 QInterProcessChannel::~QInterProcessChannel()
{
      while ( isRunning() )
            quit();
      
      close();
}

/*!
      \return Wether the instance has a server role
*/
00083 bool QInterProcessChannel::isServer() const
{
      return pServer;
}

/*!
      \return The current buffered message
*/
00091 QString QInterProcessChannel::messageBuffer() const
{
      return sMsg;
}

/*!
      \brief Sets a buffered message
*/
00099 void QInterProcessChannel::setMessageBuffer(const QString& s)
{
      sMsg = s;
}

/*!
      \overload
      
      Sends the content of the current message buffer
*/
00109 void QInterProcessChannel::sendMessage()
{
      sendMessage(sMsg);
      sMsg.clear();
}

/*!
      \overload
*/
00118 void QInterProcessChannel::sendMessage(const QString& s)
{
      sendMessage(s.toLocal8Bit());
}
/*!
      \brief Send a message to server instance
*/
00125 void QInterProcessChannel::sendMessage(const QByteArray& msg)
{
      if ( !pServer && msg.count() )
      {
            //qDebug("Sending msg : %s", msg.constData());
            
            QTcpSocket *pSocket = new QTcpSocket(this);
            pSocket->connectToHost(m_addr, m_port);
            pSocket->waitForConnected(WAIT_TIMEOUT);
            pSocket->write(msg);
            pSocket->waitForBytesWritten(WAIT_TIMEOUT);
            
      } else {
            //qWarning("Empty messages are not carried out...");
      }
}

/*!
      \brief Closes the communication channel
      
      \see reconnect()
*/
00147 void QInterProcessChannel::close()
{
      if ( pServer )
      {
            pServer->close();
            delete pServer;
            pServer = 0;
            
            QFile::remove(rcFile);
      }
}

/*!
      \brief Attemps to reconnect
      
      When the server instance is closed (or crashes...) the connectionLost() signal
      is emitted. As an alternative to closing the client instances it is possible to
      try a reconnection so that one of theclient will become a server.
      
      \note When several clients are running side by side this function is very likely
      to cause a fork and create several (independant) clients whose only one will be
      reachable by newer clients...
      
      \see connectionLost()
*/
00172 void QInterProcessChannel::reconnect()
{
      //qDebug("attempting to reconnect");
      init();
}

/*!
      \internal
*/
00181 void QInterProcessChannel::run()
{
      /*
            There we check that the server lives...
            If connection fails notify it...
      */
      
      if ( pServer )
            return;
      
      //qDebug("timer setup...");
      
      pServerTimer = new QTimer;
      pServerTimer->setInterval(100);
      pServerTimer->setSingleShot(false);
      
      connect(pServerTimer, SIGNAL( timeout() ),
                  this        , SLOT  ( check() ) );
      
      pServerTimer->start();
      
      exec();
}

/*!
      \internal
*/
00208 void QInterProcessChannel::check()
{
      //qDebug("checking connection...");
      
      QTcpSocket *pSocket = new QTcpSocket(this);
      pSocket->connectToHost(m_addr, m_port);
      
      if ( pSocket->error() != -1 )
      {
            emit connectionLost();
            return;
      }
      
      pSocket->waitForConnected(WAIT_TIMEOUT);
      
      if ( pSocket->error() != -1 )
      {
            emit connectionLost();
            return;
      }
}

/*!
      \internal
*/
00233 void QInterProcessChannel::init()
{
      while ( isRunning() )
            quit();
      
      if ( pServerTimer )
      {
            pServerTimer->stop();
            delete pServerTimer;
            pServerTimer = 0;
      }
      
      bool ok = true;
      
      m_port = 0;
      m_addr = QHostAddress::LocalHost;
      
      pServer = new QTcpServer(this);
      pServer->listen(m_addr, m_port);
      
      connect(pServer   , SIGNAL( newConnection() ),
                  this  , SLOT  ( connection() ) );
      
      if ( QFile::exists(rcFile) )
      {
            /*
                  found a server config file, let us assume it is from a running server
            */
            //qDebug("checking old server...");
            
            QSettings conf(rcFile, QSettings::IniFormat);
            
            m_port = conf.value("port").toUInt();
            m_addr = QHostAddress(conf.value("address").toString());
            
            QTcpSocket *pSocket = new QTcpSocket(this);
            
            if ( !m_addr.isNull() && m_port )
            {
                  pSocket->connectToHost(m_addr, m_port);
                  ok = pSocket->waitForConnected(WAIT_TIMEOUT);
                  
                  if ( ok ) ok &= (bool)pSocket->write("--check");
                  if ( ok ) ok &= (bool)pSocket->waitForBytesWritten(WAIT_TIMEOUT);
                  if ( ok ) ok &= (bool)pSocket->waitForReadyRead(WAIT_TIMEOUT);
                  if ( ok ) ok &= (pSocket->readAll() == "[ALIVE]");
                  
            } else {
                  ok = false;
            }
            
            if ( !ok )
            {
                  QFile::remove(rcFile);
            }
            
            pSocket->disconnectFromHost();
            delete pSocket;
      }
      
      if ( !QFile::exists(rcFile) )
      {
            // no server found... Create one
            //qDebug("setting up new server...");
            
            m_port = pServer->serverPort();
            m_addr = pServer->serverAddress();
            
            QSettings conf(rcFile, QSettings::IniFormat);
            conf.setValue("port", m_port);
            conf.setValue("address", m_addr.toString());
            
            emit gotServerRole();
            emit serverRoleChanged(true);
      } else {
            // server found we'll hook on it later on...
            pServer->close();
            pServer = 0;
            
            emit serverRoleChanged(false);
      }
      
      start();
}

/*!
      \internal
*/
00321 void QInterProcessChannel::message(const QString& msg, QManagedSocket *s)
{
      if ( !pServer )
            return;
      
      //qDebug("message from Inter Process channel : %s", qPrintable(msg));
      
      QStringList argv = QManagedRequest::splitArguments(msg);
      const QString cmd = argv.at(0);
      //const int argc = argv.count();
      
      if ( cmd == "--check" )
      {
            s->send("[ALIVE]");
      } else if ( cmd == "--request" ) {
            argv.removeAt(0);
            emit request(argv);
      } else {
            emit message(msg);
      }
}

/*!
      \brief internal
*/
00346 void QInterProcessChannel::connection()
{
      if ( !pServer )
            return;
      
      //qDebug("incoming Inter Process connection");
      
      while ( pServer->hasPendingConnections() )
      {
            QManagedSocket *s = new QManagedSocket(pServer->nextPendingConnection(), this);
            
            connect(s   , SIGNAL( message(QString, QManagedSocket*) ),
                        this, SLOT  ( message(QString, QManagedSocket*) ) );
      }
}

Generated by  Doxygen 1.6.0   Back to index