QT使用QLocalServer、QLocalSocket进程通信实现程序单例

2022年3月26日 304点热度 0人点赞 0条评论

QLocalSocket 是Qt提供的一个进程与进程之间的通信的方法,在Windows系统上使用管道技术实现。

翻阅了一下微软的MSDN得知管道技术是通过共享内存实现的,创建管道的进程称为pipe server,连接的被称为pipe client,当一个进程往管道中写入信息后,另一个进程就可以在这个管道中读取信息。

扯远了,文章主要记录QLocalServer和QLocalSocket如何使用,然后在实现进程通信的基础上,实现一个程序保证在系统里只有一个实例在运行。

QLocalSocket QLocalServer

使用QLocalServer 时,先调用listen(const QString& name)启动监听服务,调用时需要传入服务端名称,localSocket连接服务端时也是通过这个名称查找到服务端。监听开启后,当有localSocket连接服务端时会发出newConnection()信号。

当已知有localSocket连接服务端时,可以调用nextPendingConnection() 获取请求连接并挂起的localSocket,然后通过socket对象就可以实现信息的读写操作了。

注意:QLocalServer对象在不使用时应该调用close()函数取消监听。

QLocalSocket类继承于QIODevice,所以有write() read() readLine() readAll() 函数可以使用。

使用时调用connectToServer(const QString& name)连接到服务端,可以使用waitForConnected()等待连接成功,默认的连接超时时间时30秒,也可以通过参数传入超时时间。

连接成功之后就可以调用read()和write()函数进行读写了,需要注意的时在读取前最好调用waitForReadyRead()等待信息被写入完成,再读取数据。同样,再写入之后也最后再调用waitForBytesWritten()保证数据被成功写入。这两个函数均可以传入一个超时时间,如果等待超时会返回false。

实现进程通信

了解以上信息之后,实现进程通信就非常简单了,只需要一个进程开启服务端监听,然后另一个进程再使用socket连接服务端,连接成功之后就可以写入/读取消息了。

.h

#include <QObject>
#include <QLocalServer>
#include <QLocalSocket>

class TestProcessCommunication:public QObject
{
    Q_OBJECT
public:
    TestProcessCommunication();
    ~TestProcessCommunication();
public slots:
    void newConnection();
private:
    QString serverName = "TestPrc";
    QLocalServer* server;
};

.cpp

#include "testprocesscommunication.h"
#include <iostream>
TestProcessCommunication::TestProcessCommunication()
{
    //判断是否已有服务端存在
    QLocalSocket socket;
    socket.connectToServer(serverName);
    if(socket.waitForConnected(2*1000))
    {
        //如果已存在则发送消息
        socket.write("hello world!");
        if(socket.waitForBytesWritten())
            return;
    }

    server = new QLocalServer();
    server->listen(serverName);
    connect(server,SIGNAL(newConnection()),this,SLOT(newConnection()));

    if(!server->isListening())
        std::cerr << "listen failed!" << std::endl;

}

TestProcessCommunication::~TestProcessCommunication()
{
    server->close();
    delete  server;
}

void TestProcessCommunication::newConnection()
{
    auto socket = server->nextPendingConnection();
    if(!socket)
        return;

    if(socket->waitForReadyRead())
    {
        auto msg = QString(socket->readAll());
        std::cerr << "receive msg: " << msg.toStdString() << std::endl;
    }
}

在两个进程中都创建一个TestProcessCommunication对象,第一个创建的对象就可以收到消息了。

注意:再同一个进程中同时创建两个对象,会因为消息等待的原因导致有时候获取不到信息,测试的时候最好是在两个进程中进行测试。(两个线程没有尝试)

程序实现单个实例运行

某些应用场景中,会希望程序只有一个实例在运行,但是一个普通的程序,在windows系统中每启动一次都会有一个实例在系统中运行。

这个时候可以在程序运行的时候尝试连接程序的localServer,如果连接失败,说明系统中还没有程序的实例,那么启动一个localServer监听,然后正常启动程序。如果连接成功则说明已经有实例在运行,那么直接退出程序。

如果当前启动的程序是带着任务启动的(如打开文件),但是系统中已存在程序实例,那么可以通过localSocket将任务信息发送给已经运行的实例,由它来完成任务,这样就既能保证系统中只有一个程序实例运行,也能保证不会忽略掉用户的一些操作。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论