進程通信(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");  //連接到服務器

槽函數實現如下:

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後重連
}

運行結果展示:

先運行服務端,後運行客戶端:

先運行客戶端,後運行服務端:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章