Qt進(jìn)程間通信的方法:
- TCP/IP
- Local Server/Socket
- 共享內(nèi)存
- D-Bus (Unix庫(kù))
- QProcess
- 會(huì)話管理
TCP/IP :
使用套接字的方式,進(jìn)行通信(之前介紹了,這里就不介紹了)。
Local Server/Socket
跨平臺(tái)Qt網(wǎng)絡(luò)模塊提供的類(lèi)使本地網(wǎng)絡(luò)編程變得可移植且簡(jiǎn)單。它提供了QLocalServer和QLocalSocket類(lèi),允許在本地設(shè)置中進(jìn)行類(lèi)似網(wǎng)絡(luò)的通信。它們的 TCP 對(duì)應(yīng)項(xiàng)可以用作直接替代品,以使跨網(wǎng)絡(luò)通信工作。(使用方法和TCP相似)
QLocalServer :
使用方法:
- listen()監(jiān)聽(tīng)指定密鑰上的傳入連接
- 有客戶端連接時(shí),會(huì)發(fā)出newConnection()信號(hào)
- 使用nextPengingConnection()獲取連接的QLocalSocket
- close()關(guān)閉監(jiān)聽(tīng)
listen(QString) | 監(jiān)聽(tīng) | close() | 關(guān)閉監(jiān)聽(tīng) | errorString() | 報(bào)告的當(dāng)前錯(cuò)誤的人類(lèi)可讀消息 | fullServerName() | 返回服務(wù)器正在偵聽(tīng)的完整路徑 | isListrening() | 服務(wù)器正在偵聽(tīng)傳入連接,true,否則為false | maxPendingConnection() | 返回掛起的接受連接的最大數(shù)目。默認(rèn)值為 30 | nextPengingConnection() | 將一個(gè)掛起的連接作為連接的 QLocalServer 對(duì)象返回 | serverName() | 則返回服務(wù)器名稱 | socketOption() | 返回在套接字上設(shè)置的套接字選項(xiàng) | waitForNewConnection(int) | 堵塞多少毫秒或直到傳入連接可用 |
QLocalSocket
在Windows上是一個(gè)命名管道,在Unix上是一個(gè)本地套接字。
QLocalSocket 設(shè)計(jì)用于事件循環(huán),但也可以在沒(méi)有事件循環(huán)的情況下使用它。在這種情況下,您必須使用 waitForConnected()、waitForReadyRead()、waitForBytesWrite() 和 waitForDisconnected(),它們會(huì)阻止操作完成或超時(shí)到期。
使用方法:
- 使用connectToServer()與服務(wù)器簡(jiǎn)建立連接。
- 可以使用readData()讀取數(shù)據(jù),writeData()寫(xiě)入數(shù)據(jù)
- abort()斷開(kāi)連接
- close(),或disconnectFromServer()關(guān)閉套接字
connectToServer() | 連接服務(wù)器 | bytesAvailable() | 獲取數(shù)據(jù)大小 | error() | 返回上次發(fā)生的錯(cuò)誤類(lèi)型 | flush() | 此函數(shù)盡可能多地從內(nèi)部寫(xiě)入緩沖區(qū)寫(xiě)入套接字,而不會(huì)阻塞。如果寫(xiě)入了任何數(shù)據(jù)返回true | fullServerName() | 返回套接字連接到的服務(wù)器路徑 | isVaild() | 判斷套接字是否可用 | readData() | 讀取數(shù)據(jù) | writeData() | 寫(xiě)入數(shù)據(jù) | setReadBufferSize() | 設(shè)置內(nèi)部緩沖區(qū)大小 | waitForConnected() | 等待連接 | waitForReadyRead() | 等待讀取 | waitForBytesWrite() | 等待寫(xiě)入 | waitForDisconnected() | 等待斷開(kāi) |
LoaclServer的搭建:
pro文件中添加:
QT +network
在ui界面中添加:
QTextEdit 、QPushButton 和QLineEdit

.h文件:
namespace Ui { class Widget; } class Widget : public QWidget Widget(QWidget *parent = nullptr); void on_pushButton_clicked(); QLocalServer *localserver;//服務(wù)端 QLocalSocket*localsocket=nullptr;//套接字
.cpp文件:
Widget::Widget(QWidget *parent) setWindowTitle('服務(wù)器端'); localserver=new QLocalServer(this); localserver->listen('Good People');//Good People作為連接字符 connect(localserver,&QLocalServer::newConnection,[=]() localsocket=localserver->nextPendingConnection();//獲取連接的套接字 connect(localsocket,&QLocalSocket::readyRead,[=]()//如果有可讀數(shù)據(jù)的話 block=localsocket->readAll(); ui->textEdit->append(QString('客戶端:%1').arg(S)); void Widget::on_pushButton_clicked()//發(fā)送數(shù)據(jù) if(ui->lineEdit->text().isEmpty()) QMessageBox::information(this,'提示信息','請(qǐng)輸入內(nèi)容',QMessageBox::Ok); ui->textEdit->setText(QString('服務(wù)器端:%1').arg(ui->lineEdit->text()));//設(shè)置內(nèi)容 if(localsocket->isValid()) QString S=ui->lineEdit->text(); localsocket->write(S.toUtf8());//發(fā)送消息
LocalSocket客戶端的搭建:
pro文件中添加:
QT +=network
ui界面中添加:

.h文件:
namespace Ui { class Widget; } class Widget : public QWidget Widget(QWidget *parent = nullptr); void on_pushButton_clicked(); void on_pushButton_2_clicked(); QLocalSocket * localsocket;//套接字
.cpp文件:
Widget::Widget(QWidget *parent) localsocket=new QLocalSocket(this); connect(localsocket,&QLocalSocket::readyRead,[=]() block=localsocket->readAll();//讀取信息 ui->textEdit->append(QString('服務(wù)器:%1').arg(block.data()));//聊天框添加信息 void Widget::on_pushButton_clicked()//連接 localsocket->abort();//先斷開(kāi) localsocket->connectToServer('Good People');//連接 void Widget::on_pushButton_2_clicked()//發(fā)送 if(ui->lineEdit->text().isEmpty()) QMessageBox::information(this,'提示信息','請(qǐng)輸入內(nèi)容。',QMessageBox::Ok); if(localsocket->isValid()) localsocket->write(ui->lineEdit->text().toUtf8()); ui->textEdit->append(QString('客戶端:%1').arg(ui->lineEdit->text()));
運(yùn)行效果:

共享內(nèi)存:
QSharedMemory
QSharedMemory 提供通過(guò)多個(gè)線程和進(jìn)程對(duì)共享內(nèi)存段的訪問(wèn)。它還為單個(gè)線程或進(jìn)程提供了一種鎖定內(nèi)存以進(jìn)行獨(dú)占訪問(wèn)的方法。
使用此類(lèi)時(shí),請(qǐng)注意以下平臺(tái)差異:
- Windows:QSharedMemory 不“擁有”共享內(nèi)存段。當(dāng)將 QSharedMemory 實(shí)例附加到特定共享內(nèi)存段的所有線程或進(jìn)程都銷(xiāo)毀其 QSharedMemory 實(shí)例或退出時(shí),Windows 內(nèi)核會(huì)自動(dòng)釋放共享內(nèi)存段。
- Unix:QSharedMemory“擁有”共享內(nèi)存段。當(dāng)將 QSharedMemory 實(shí)例附加到特定共享內(nèi)存段的最后一個(gè)線程或進(jìn)程通過(guò)銷(xiāo)毀其 QSharedMemory 實(shí)例與該段分離時(shí),Unix 內(nèi)核將釋放共享內(nèi)存段。但是,如果在未運(yùn)行 QSharedMemory 析構(gòu)函數(shù)的情況下最后一個(gè)線程或進(jìn)程崩潰,則共享內(nèi)存段將在崩潰中幸存下來(lái)。
- HP-UX:每個(gè)進(jìn)程只允許一個(gè)附加到共享內(nèi)存段。這意味著 QSharedMemory 不應(yīng)在 HP-UX 中同一進(jìn)程中的多個(gè)線程中使用。
注意:由于是共享內(nèi)存,所以使用共享內(nèi)存之前使用lock()鎖定共享內(nèi)存,使用完后需要解鎖。當(dāng) QSharedMemory 的最后一個(gè)實(shí)例從共享內(nèi)存段分離時(shí),QSharedMemory 會(huì)自動(dòng)銷(xiāo)毀該段,并且不會(huì)保留對(duì)該段的引用。
常用函數(shù):
setKey(QString) | 設(shè)置此共享內(nèi)存對(duì)象的平臺(tái)獨(dú)立鍵 | setNativeKey() | 設(shè)置此共享內(nèi)存對(duì)象的特定于平臺(tái)的本機(jī)密鑰。如果 key 與當(dāng)前本機(jī)鍵相同,則函數(shù)返回而不執(zhí)行任何操作。 | lock() | 對(duì)共享內(nèi)存上鎖 | unlock() | 對(duì)共享內(nèi)存解鎖 | size() | 返回附加的共享內(nèi)存段的大小。如果未連接共享內(nèi)存段,則返回 0。 | isAttached() | 如果此進(jìn)程連接到共享內(nèi)存段返回true | errorString() | 錯(cuò)誤信息 | detach() | 將進(jìn)程與共享內(nèi)存段分離。如果這是附加到共享內(nèi)存段的最后一個(gè)進(jìn)程,則系統(tǒng)將釋放共享內(nèi)存段,即內(nèi)容被銷(xiāo)毀。 | data() | 返回指向共享內(nèi)存段內(nèi)容的指針(如果已連接) | create() | 使用傳遞給構(gòu)造函數(shù)的鍵創(chuàng)建大小為字節(jié)的共享內(nèi)存段 | constData() | 返回指向共享內(nèi)存段內(nèi)容的 const 指針(如果已連接) | attach() | 嘗試將進(jìn)程附加到由傳遞給構(gòu)造函數(shù)的鍵或?qū)?nbsp;setKey() 或 setNativeKey() 的調(diào)用標(biāo)識(shí)的共享內(nèi)存段。訪問(wèn)模式默認(rèn)為讀寫(xiě) |
QSharedMemory::AccessMode
QSharedMemory::ReadOnly | 共享內(nèi)存段是只讀的。不允許寫(xiě)入共享內(nèi)存段。嘗試寫(xiě)入使用 ReadOnly 創(chuàng)建的共享內(nèi)存段會(huì)導(dǎo)致程序中止。 | QSharedMemory::ReadWrite | 讀取和寫(xiě)入共享內(nèi)存段都是允許的。 |
基本使用流程:
發(fā)送內(nèi)容到共享內(nèi)存
- 設(shè)置一個(gè)標(biāo)識(shí) setKey();
- 使用isAttached()判斷進(jìn)程是否與共享內(nèi)存相連
- 使用detach()斷開(kāi)連接
- create(size)創(chuàng)建共享內(nèi)存
- 上鎖
- 將數(shù)據(jù)寫(xiě)入共享內(nèi)存
- 解鎖
從共享內(nèi)存中讀取數(shù)據(jù):
- attach()連接到共享內(nèi)存
- 上鎖
- 讀取數(shù)據(jù)
- 解鎖
例子:寫(xiě)入和讀取數(shù)據(jù)
QSharedMemory memory;創(chuàng)建對(duì)象 memory.setKey('1000');//設(shè)置標(biāo)識(shí) if(memory.isAttached())//已經(jīng)連接的話 memory.detach();//斷開(kāi)連接 QString Str='SharedMemory'; buffer.open(QBuffer::ReadWrite); S<<Str;//將數(shù)據(jù)寫(xiě)到緩存中 memory.create(buffer.size());//創(chuàng)建共享內(nèi)存 char * M=(char *)memory.data();//獲取memory的指針 const char *N=buffer.data().data();//獲取緩存的指針 memcpy(M,N,buffer.size());//把數(shù)據(jù)復(fù)制到共享內(nèi)存中 memory.attach();//連接共享內(nèi)存 buffer.setData((char*)memory.constData(),memory.size());//存入數(shù)據(jù) buffer.open(QBuffer::ReadOnly);//只讀模式 memory.detach();//斷開(kāi)連接
使用共享內(nèi)存實(shí)現(xiàn)兩個(gè)進(jìn)程的聊天:
ui界面:

.h文件:
namespace Ui { class Widget; } class Widget : public QWidget Widget(QWidget *parent = nullptr); void on_pushButton_clicked(); void on_pushButton_2_clicked(); void on_pushButton_3_clicked(); QSharedMemory sharedmemory;//共享內(nèi)存
.cpp文件:
Widget::Widget(QWidget *parent) sharedmemory.setKey('GG');//設(shè)置標(biāo)識(shí) void Widget::on_pushButton_clicked()//讀取數(shù)據(jù) if(!sharedmemory.attach()) QMessageBox::information(this,'提示信息','連接共享內(nèi)存失敗',QMessageBox::Ok); buffer.setData((char *)sharedmemory.constData(),sharedmemory.size());//設(shè)置數(shù)據(jù) buffer.open(QBuffer::ReadOnly); sharedmemory.unlock();//解鎖 sharedmemory.detach();//斷開(kāi)連接 ui->textEdit->append(QString('他人:%1').arg(S1)); void Widget::on_pushButton_2_clicked()//發(fā)送數(shù)據(jù) if(sharedmemory.isAttached())//內(nèi)存和進(jìn)程連接 if(sharedmemory.detach())//斷開(kāi)連接 QMessageBox::information(this,'提示信息','已斷開(kāi)連接'); QMessageBox::information(this,'提示信息','斷開(kāi)連接失敗'); if(ui->lineEdit->text().isEmpty()) QMessageBox::information(this,'提示信息','內(nèi)容不能為空。',QMessageBox::Ok); buffer.open(QBuffer::ReadWrite); QDataStream out(&buffer); QString Str=ui->lineEdit->text(); out<<Str;//把內(nèi)容寫(xiě)入緩存區(qū) if(!sharedmemory.create(size)) QMessageBox::information(this,'提示信息','內(nèi)存創(chuàng)建失敗',QMessageBox::Ok); const char * Buff=buffer.data().data();//獲取buffer的指針 char *Sha=(char *)sharedmemory.data();//獲取共享內(nèi)存的指針 memcpy(Sha,Buff,qMin(sharedmemory.size(),size)); sharedmemory.unlock();//解鎖 ui->textEdit->append(QString('本人:%1').arg(ui->lineEdit->text()));//顯示信息 void Widget::on_pushButton_3_clicked()//斷開(kāi)連接 if(sharedmemory.detach())//斷開(kāi)連接 QMessageBox::information(this,'提示信息','已斷開(kāi)連接');
main函數(shù):
int main(int argc, char *argv[]) QApplication a(argc, argv); w1.setWindowTitle('widget1'); w2.setWindowTitle('widget2');
這里解釋一下為什么要有斷開(kāi)連接的按鈕:
經(jīng)過(guò)多次測(cè)試發(fā)現(xiàn) :
A端發(fā)完數(shù)據(jù),B端接收數(shù)據(jù)后,A端需要主動(dòng)斷開(kāi)連接后B端才能正常發(fā)送數(shù)據(jù)。
(懂的大佬請(qǐng)?jiān)谠u(píng)論區(qū)解釋一下)
效果:
widget2 發(fā)送信息給widget1

記住發(fā)送端需要主動(dòng)斷開(kāi)連接:
widget1 發(fā)送信息給 widget2

QProcess
參考文章:關(guān)于Qt的QProcess進(jìn)程間雙向通信_(tái)qprocess進(jìn)程間通信_(tái)weixin_46424582的博客-CSDN博客
QProcess進(jìn)程間通信:

主線程構(gòu)建思路:
- 發(fā)送數(shù)據(jù)到子進(jìn)程:使用write()函數(shù)
- 讀取子進(jìn)程發(fā)送的數(shù)據(jù)數(shù)據(jù):監(jiān)聽(tīng)readReadyStandardOutput()信號(hào)
創(chuàng)建一個(gè)項(xiàng)目:
ui界面中添加以下控件: textEdit pushButton lineEdit

.h文件:
namespace Ui { class Widget; } class Widget : public QWidget Widget(QWidget *parent = nullptr); void on_pushButton_clicked(); void on_pushButton_2_clicked();
.cpp文件:
Widget::Widget(QWidget *parent) setWindowTitle('主進(jìn)程'); connect(process,&QProcess::readyReadStandardOutput,[=]()//讀取信息 QString S=process->readAllStandardOutput().data(); ui->textEdit->append(QString('子線程:%1').arg(S)); void Widget::on_pushButton_clicked()//打開(kāi)子進(jìn)程 process->start('D:/Qt_data/build-QProcess_text1-Desktop_Qt_5_9_9_MinGW_32bit-Debug/debug/QProcess_text1.exe'); void Widget::on_pushButton_2_clicked()//發(fā)送數(shù)據(jù) if(ui->lineEdit->text().isEmpty()) QMessageBox::information(this,'提示信息','輸入框不能為空。',QMessageBox::Ok); QString S=ui->lineEdit->text();//獲取lineEdit中的內(nèi)容 process->write(S.toStdString().c_str());//寫(xiě)入數(shù)據(jù) ui->textEdit->append('父線程:'+S);//在聊天框顯示內(nèi)容
子線程的構(gòu)建思路:
- 發(fā)送數(shù)據(jù)到父線程:使用文件打開(kāi) stdout,然后把數(shù)據(jù)寫(xiě)入stdout中
- 獲取父線程發(fā)送過(guò)來(lái)的數(shù)據(jù):
- linux中:使用QSocketNotifier 監(jiān)聽(tīng) stdin文件,當(dāng)改文件有變化是,讀取信息
- Windows中:需要開(kāi)啟一個(gè)線程來(lái)管理stdin的文件變化,這個(gè)需要使用Windows API函數(shù)
這里介紹Windows中的子線程構(gòu)建:
創(chuàng)建一個(gè)項(xiàng)目:
在pro文件中添加:
QT +=concurrent
ui界面中添加:

.h文件:
#include<QSocketNotifier> namespace Ui { class Widget; } class Widget : public QWidget Widget(QWidget *parent = nullptr); void on_pushButton_clicked(); void readStdin();//讀取數(shù)據(jù) void sig_log(QString S);//顯示數(shù)據(jù) void sig_receivedCommand(QString S);//顯示數(shù)據(jù)
.cpp文件:
#include<QSocketNotifier> Widget::Widget(QWidget *parent) setWindowTitle('子進(jìn)程'); QFuture<void> fu=QtConcurrent::run(this,&Widget::readStdin);//開(kāi)啟一個(gè)線程 connect(this,&Widget::sig_receivedCommand,this,[&](QString s) ui->textEdit->append('父線程:'+s); connect(this,&Widget::sig_log,this,[=](QString s) ui->textEdit->append('父線程:'+s); void Widget::readStdin()//讀取數(shù)據(jù) DWORD dwRead;//32位無(wú)符號(hào)整數(shù) HANDLE hStdinDup;//HANDLE 句柄類(lèi)型 const HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);//GetStdHandle獲取標(biāo)準(zhǔn)輸入的句柄 if(hStdin==INVALID_HANDLE_VALUE)//為無(wú)效句柄的話 DuplicateHandle(GetCurrentProcess(),hStdin, GetCurrentProcess(),&hStdinDup,0,false,DUPLICATE_SAME_ACCESS);//創(chuàng)建一個(gè)新句柄 CloseHandle(hStdin);//關(guān)閉舊句柄 ok=ReadFile(hStdinDup,chbuf,sizeof(chbuf),&dwRead,NULL);//讀取hstdinDup句柄文件中的數(shù)據(jù) emit sig_log(QLatin1String('ok is:')+QString::number(ok)); emit sig_receivedCommand(QString::fromUtf8(chbuf,dwRead));//讀取數(shù)據(jù) void Widget::on_pushButton_clicked()//發(fā)送數(shù)據(jù) if(ui->lineEdit->text().isEmpty()) QMessageBox::information(this,'提示信息','輸入框不能為空。',QMessageBox::Ok); if(fileout.open(stdout,QIODevice::WriteOnly)) QString S=ui->lineEdit->text(); fileout.write(S.toStdString().c_str()); ui->textEdit->append('子線程:'+S); fileout.close();//關(guān)閉文件
運(yùn)行效果:

|