進程通信(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後重連
}
運行結果展示:
先運行服務端,後運行客戶端:
先運行客戶端,後運行服務端: