进程通信(IPC)之QLocalSocket用法

        进程通信(IPC)的方法有很多,项目开发中,需要根据业务需求来选择适合的IPC方式。所谓LocalSocket,其实就是在socket的基础上衍生出来的一种IPC通信机制。其旨在解决同一台主机上不同进程间互相通信的问题,不能像网络通信使用的socket一样实现不同主机间通信。但正因为这一点,它不需要经过网络协议栈,不需要打包拆包、计算校验,所以执行效率要更高。

而Qt对LocalSocket进行了封装(QLocalSocket),使其用起来更方便,下面介绍QLocalSocket在本地进程间进行通信的简单用法:

服务端:

创建QLocalServer,connect发现新连接的槽函数。

    QLocalServer *m_server = new QLocalServer(this);
    //myserver为服务端名称,类似于IP+PORT,客户端连接需要与其保持一致
    if( m_server->listen("myserver") ) //监听  
    {
       connect(m_server,SIGNAL(newConnection()),this,SLOT(new_connection()));
       printf("listen success!!\n");
    }
    else
       printf("listen fail\n");
       //m_server->removeServer("myserver");   //如果SERVER已经存在,则需删除

发现新连接槽函数处理:

void Widget::new_connection()
{
    qDebug()<<"发现新连接!!";
    ui->textBrowser->append("发现新连接!!");
    QLocalSocket *newsocket = m_server->nextPendingConnection();  //获取连接上的客户端句柄
    connect(newsocket, SIGNAL(readyRead()), this, SLOT(recv_data())); //关联数据接收槽函数
}

 由于建立的每个socket连接都关联到了同一个数据接收槽函数上了,槽函数需进行如下处理,来获取对应有数据的socket连接进行数据读取:

void Widget::recv_data()
{
    // 取得是哪个localsocket可以读数据了
    QLocalSocket *local = static_cast<QLocalSocket *>(sender());
    if (!local)
        return;

    QByteArray rcv_data = local->readAll();
    qDebug()<<"rcv_data:"<<rcv_data;

    if(is_first_connect(local))
    {
        local->write(QString("欢迎连接myserver!!").toUtf8());
        ui->textBrowser->append(QString("这个socket是首次连接"));
    }
    else
    {
        local->write(rcv_data);
        ui->textBrowser->append(QString("发送数据:%1").arg(QString(rcv_data)));
    }
}

利用 QList来记录socket连接,判断是否为首次连接。(扩展:可以在客户端建立连接后,由客户端向服务端发送特定的数据,服务端首次接收到数据时,将特定数据对应的类型与该socket存进QMap中,建立映射关系。之后通过类型,获取到对应的socket句柄,就能向指定的client发送数据了。)

// QList<QLocalSocket *> local_sockets;  利用Qlist来存储新连接

bool Widget::is_first_connect(QLocalSocket *newsocket)  //是否为首次连接
{
    int len = local_sockets.length();
    for(int i=0; i<len; i++)
    {
        if(newsocket == (QLocalSocket *)local_sockets.at(i))
            return false;
    }
    local_sockets.append(newsocket);
    return true;
}

客户端:

client相对简单,但是考虑到CS模型,如果client先运行,server后运行,则会通信不上,所以需要对客户端增加定时重连处理。

QTimer *reconnect_timer = new QTimer(this);
connect(reconnect_timer,SIGNAL(timeout()),this,SLOT(reconect_to_server()));

m_socket = new QLocalSocket(this);

connect(m_socket,SIGNAL(error(QLocalSocket::LocalSocketError)),this,SLOT(error_proc(QLocalSocket::LocalSocketError))); //数据接收
connect(m_socket,SIGNAL(connected()),this,SLOT(connect_success()));  //数据接收
connect(m_socket,SIGNAL(disconnected()),this,SLOT(disconnect_from_server()));  //连接断开
connect(m_socket,SIGNAL(readyRead()),this,SLOT(rcv_data()));  //数据接收
m_socket->connectToServer("myserver");  //连接到服务器

槽函数实现如下:

扫描二维码关注公众号,回复: 11945607 查看本文章
void Widget::rcv_data() //收到数据
{
    QByteArray data = m_socket->readAll();
    ui->textBrowser->append(QString(data));
    qDebug()<<"data:"<<data;
}

void Widget::connect_success()
{
    ui->textBrowser->append("连接服务端成功!");
}

void Widget::disconnect_from_server()
{
    ui->textBrowser->append("连接断开!");
}

void Widget::reconect_to_server()
{
    reconnect_timer->stop();
    ui->textBrowser->append("连接重试中...");
    if(m_socket)
        m_socket->connectToServer("myserver");
}

void Widget::error_proc(QLocalSocket::LocalSocketError state)
{
    ui->textBrowser->append("连接服务器失败!");
    ui->textBrowser->append(QString("错误码:%1").arg(QString::number(state)));
    m_socket->close();
    reconnect_timer->start(1000);  //1s后重连
}

运行结果展示:

先运行服务端,后运行客户端:

先运行客户端,后运行服务端:

猜你喜欢

转载自blog.csdn.net/fangye945a/article/details/106006819