Qt实现应用程序单实例运行-LocalServer方式

转载 2015年07月06日 17:37:18

Qt实现应用程序单实例运行--LocalServer方式

使Qt应用程序能够单实例运行的典型实现方法是使用共享内存实现。该方法实现简单,代码简洁。

但有一个致命缺陷:共享内存(QSharedMemory)实现的单程序运行,当运行环境是UNIX时,并且程序不幸崩溃,会导致共享内存无法释放,从而无法重新运行程序!

所以应该寻找其他的使Qt应用程序能够单实例运行的方案。于是找到LocalSocket和LocalServer通讯方案(据说Qt官方商业版的QSingleApplication的原理好像跟这个差不多)。

“要用到Qt的QLocalSocket,QLocalServer类,这两个类从接口上看和网络通信socket没有区别,但是它并不是真正的网络API,只是模仿了而已。这两个类在Unix/Linux系统上采用Unix域socket实现,而在Windows上则采用有名管道(named pipe)来实现。”

参见:

http://www.oschina.net/code/snippet_54100_629

http://blog.csdn.net/qq19831030qq/article/details/6199896

上面的方案实际操作过程中出现很多问题:

QString serverName = QCoreApplication::applicationName(); //获取到的serverName为空

为了解决上面的问题,最直接的测试方法是先手动指定一个serverName,

然后将QFile::remove(m_localServer->serverName());改成QFile::remove(serverName);

当手动指定serverName时,习惯上将serverName设为当前的应用程序名,在linux下这又导致下面的问题

QFile::remove(m_localServer->serverName()); //执行删除失败(实际测试发现失败原因是:尝试删除应用程序文件自身!)

 上面的两个问题导致Qt应用程序无法单实例运行。

后来终于在StackOverflow上面看到解决方案:使用QLocalServer::removeServer()删除LocalServer名

下面是实现代码:

头文件:

#ifndef SINGLEAPPLICATION_H
#define SINGLEAPPLICATION_H

#include <QObject>
#include <QApplication>
#include <QtNetwork/QLocalServer>
#include <QWidget>

class SingleApplication : public QApplication {
        Q_OBJECT
    public:
        SingleApplication(int &argc, char **argv);

        bool isRunning();                // 是否已經有实例在运行
        QWidget *w;                        // MainWindow指针

    private slots:
        // 有新连接时触发
        void _newLocalConnection();

    private:
        // 初始化本地连接
        void _initLocalConnection();
        // 创建服务端
        void _newLocalServer();
        // 激活窗口
        void _activateWindow();

        bool _isRunning;                // 是否已經有实例在运行
        QLocalServer *_localServer;     // 本地socket Server
        QString _serverName;            // 服务名称
};

#endif // SINGLEAPPLICATION_H
 CPP文件:
#include "SingleApplication.h"
#include <QtNetwork/QLocalSocket>
#include <QFileInfo>

#define TIME_OUT                (500)    // 500ms

SingleApplication::SingleApplication(int &argc, char **argv)
    : QApplication(argc, argv)
    , w(NULL)
    , _isRunning(false)
    , _localServer(NULL) {

    // 取应用程序名作为LocalServer的名字
    _serverName = QFileInfo(QCoreApplication::applicationFilePath()).fileName();

    _initLocalConnection();
}


////////////////////////////////////////////////////////////////////////////////
// 说明:
// 检查是否已經有一个实例在运行, true - 有实例运行, false - 没有实例运行
////////////////////////////////////////////////////////////////////////////////
bool SingleApplication::isRunning() {
    return _isRunning;
}

////////////////////////////////////////////////////////////////////////////////
// 说明:
// 通过socket通讯实现程序单实例运行,监听到新的连接时触发该函数
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_newLocalConnection() {
    QLocalSocket *socket = _localServer->nextPendingConnection();
    if(socket) {
        socket->waitForReadyRead(2*TIME_OUT);
        delete socket;

        // 其他处理,如:读取启动参数

        _activateWindow();
    }
}

////////////////////////////////////////////////////////////////////////////////
// 说明:
// 通过socket通讯实现程序单实例运行,
// 初始化本地连接,如果连接不上server,则创建,否则退出
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_initLocalConnection() {
    _isRunning = false;    

    QLocalSocket socket;
    socket.connectToServer(_serverName);
    if(socket.waitForConnected(TIME_OUT)) {
        fprintf(stderr, "%s already running.\n",
                _serverName.toLocal8Bit().constData());
        _isRunning = true;
        // 其他处理,如:将启动参数发送到服务端
        return;
    }

    //连接不上服务器,就创建一个
    _newLocalServer();
}

////////////////////////////////////////////////////////////////////////////////
// 说明:
// 创建LocalServer
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_newLocalServer() {
    _localServer = new QLocalServer(this);
    connect(_localServer, SIGNAL(newConnection()), this, SLOT(_newLocalConnection()));
    if(!_localServer->listen(_serverName)) {
        // 此时监听失败,可能是程序崩溃时,残留进程服务导致的,移除之
        if(_localServer->serverError() == QAbstractSocket::AddressInUseError) {
            QLocalServer::removeServer(_serverName); // <-- 重点
            _localServer->listen(_serverName); // 再次监听
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// 说明:
// 激活主窗口
////////////////////////////////////////////////////////////////////////////////
void SingleApplication::_activateWindow() {
    if(w) {
        w->show();
        w->raise();
        w->activateWindow(); // 激活窗口
    }
}

调用示例:

#include "MainWindow.h"
#include "SingleApplication.h"

int main(int argc, char *argv[]) {
    SingleApplication a(argc, argv);
    if(!a.isRunning()) {
        MainWindow w;
        a.w = &w;

        w.show();

        return a.exec();
    }
    return 0;
}

相关文章推荐

Qt IPC研究之QLocalServer和QLocalSocket

    在Qt中,提供了多种IPC方法,作者所用的是QLocalServer和QLocalSocket。看起来好像和Socket搭上点边,实则底层是windows的name pipe。这应该是支持双工...
  • fldx
  • fldx
  • 2011年03月16日 21:18
  • 7181

Qt之QLocalServer

简述QLocalServer提供了一个基于本地socket的server。QLocalServer可以接受来自本地socket的连接。通过调用listen(),让server监听来自特定key的连接。...

QLocalSocket和QLocalServer的简单使用

1、用QTime统计某函数执行消耗时间。 QTime t; t.start(); some_lengthy_task(); qDebug("Time elapsed: %d m...
  • sauphy
  • sauphy
  • 2015年05月28日 22:22
  • 1384

QLocalServer和QLocalSocket单进程和进程通信

描述: QLocalServer 继承自QObject。QLocalServer提供了一个基于本地套接字(socket)的服务端(server)。它可以接受来自本地socket的连接...

QLocalServer

QLocalServer 继承自QObject。 QLocalServer提供了一个基于本地套接字(socket)的服务端(server)。 QLocalServer可以接受来自本地sock...

LocalSocket/LocalServerSocket

前言   本章内容android.net.LocalSocket章节,版本为Android 4.0 r1,翻译来自:"水中影",欢迎访问他的博客:"http://www.cnblogs.com/go...
  • mxiaomi
  • mxiaomi
  • 2014年11月26日 15:38
  • 1782

IntelliJ IDEA导航特性Top20

在前面的文章里,我介绍了IntelliJ IDEA(以下称IntelliJ)中与代码补全及重构相关的特性。今天我将介绍另外一个利器——IntelliJ的导航(Navigation),只要运用得当,它将...

打印回形矩阵

打印一个回形矩阵如下 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7 #include //当n=4时,分别打印1-3, 4-6, 7-9, 10-12,...

QT中实现程序只运行一个实例--应用程序的单例化

起因 最近想实现一个应用程序单例化的程序, 目前使QT运行一个实例有如下几种方式 1.QSharedMemory 使用共享内存,当第二个进程启动时,判断内存区...
  • gatieme
  • gatieme
  • 2016年01月31日 13:58
  • 3078

Qt应用程序只运行一个实例

在实际应用中,我们经常需要让应用程序只有一个实例,再打开新的文档或者页面时,只是替换现在的窗口或者新打开一个标签,而不是重新启动一次应用程序。Qt中是否可以做到这样呢,答案是肯定的,因为Qt本身可以直...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Qt实现应用程序单实例运行-LocalServer方式
举报原因:
原因补充:

(最多只允许输入30个字)