当前位置:   article > 正文

Qt 进程间通信_qt进程间通信

qt进程间通信

Qt进程间通信的方法:

  1. TCP/IP
  2. Local Server/Socket
  3. 共享内存
  4. D-Bus (Unix库)
  5. QProcess
  6. 会话管理

TCP/IP :

使用套接字的方式,进行通信(之前介绍了,这里就不介绍了)。

Local Server/Socket 

跨平台Qt网络模块提供的类使本地网络编程变得可移植且简单。它提供了QLocalServer和QLocalSocket类,允许在本地设置中进行类似网络的通信。它们的 TCP 对应项可以用作直接替代品,以使跨网络通信工作。(使用方法和TCP相似) 

QLocalServer :

使用方法:

  1. listen()监听指定密钥上的传入连接
  2. 有客户端连接时,会发出newConnection()信号
  3. 使用nextPengingConnection()获取连接的QLocalSocket
  4. close()关闭监听

listen(QString)

监听
close()关闭监听
errorString()报告的当前错误的人类可读消息
fullServerName()返回服务器正在侦听的完整路径
isListrening()服务器正在侦听传入连接,true,否则为false
maxPendingConnection()返回挂起的接受连接的最大数目。默认值为 30
nextPengingConnection()将一个挂起的连接作为连接的 QLocalServer 对象返回
serverName()则返回服务器名称
socketOption()返回在套接字上设置的套接字选项
waitForNewConnection(int)堵塞多少毫秒或直到传入连接可用

QLocalSocket

在Windows上是一个命名管道,在Unix上是一个本地套接字。

QLocalSocket 设计用于事件循环,但也可以在没有事件循环的情况下使用它。在这种情况下,您必须使用 waitForConnected()、waitForReadyRead()、waitForBytesWrite() 和 waitForDisconnected(),它们会阻止操作完成或超时到期。

使用方法:

  1. 使用connectToServer()与服务器简建立连接。
  2. 可以使用readData()读取数据,writeData()写入数据
  3. abort()断开连接
  4. close(),或disconnectFromServer()关闭套接字
connectToServer()连接服务器
bytesAvailable()获取数据大小
error()返回上次发生的错误类型
flush()此函数尽可能多地从内部写入缓冲区写入套接字,而不会阻塞。如果写入了任何数据返回true
fullServerName()返回套接字连接到的服务器路径
isVaild()判断套接字是否可用
readData()读取数据
writeData()写入数据
setReadBufferSize()设置内部缓冲区大小
waitForConnected()等待连接
waitForReadyRead()等待读取
waitForBytesWrite()等待写入
waitForDisconnected()等待断开

 LoaclServer的搭建:

pro文件中添加:

QT  +network

在ui界面中添加:

QTextEdit 、QPushButton 和QLineEdit

.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include<QLocalServer>
  5. #include<QLocalSocket>
  6. #include<QMessageBox>
  7. #include<QDataStream>
  8. QT_BEGIN_NAMESPACE
  9. namespace Ui { class Widget; }
  10. QT_END_NAMESPACE
  11. class Widget : public QWidget
  12. {
  13. Q_OBJECT
  14. public:
  15. Widget(QWidget *parent = nullptr);
  16. ~Widget();
  17. private slots:
  18. void on_pushButton_clicked();
  19. private:
  20. Ui::Widget *ui;
  21. QLocalServer *localserver;//服务端
  22. QLocalSocket*localsocket=nullptr;//套接字
  23. };
  24. #endif // WIDGET_H

.cpp文件:

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. Widget::Widget(QWidget *parent)
  4. : QWidget(parent)
  5. , ui(new Ui::Widget)
  6. {
  7. ui->setupUi(this);
  8. setWindowTitle("服务器端");
  9. localserver=new QLocalServer(this);
  10. localserver->listen("Good People");//Good People作为连接字符
  11. connect(localserver,&QLocalServer::newConnection,[=]()
  12. {
  13. localsocket=localserver->nextPendingConnection();//获取连接的套接字
  14. connect(localsocket,&QLocalSocket::readyRead,[=]()//如果有可读数据的话
  15. {
  16. QByteArray block;
  17. block=localsocket->readAll();
  18. QString S=block.data();
  19. ui->textEdit->append(QString("客户端:%1").arg(S));
  20. });
  21. });
  22. }
  23. Widget::~Widget()
  24. {
  25. delete ui;
  26. }
  27. void Widget::on_pushButton_clicked()//发送数据
  28. {
  29. if(ui->lineEdit->text().isEmpty())
  30. {
  31. QMessageBox::information(this,"提示信息","请输入内容",QMessageBox::Ok);
  32. return;
  33. }
  34. ui->textEdit->setText(QString("服务器端:%1").arg(ui->lineEdit->text()));//设置内容
  35. if(localsocket->isValid())
  36. {
  37. QString S=ui->lineEdit->text();
  38. localsocket->write(S.toUtf8());//发送消息
  39. }
  40. }

LocalSocket客户端的搭建:

pro文件中添加:

QT +=network

ui界面中添加:

.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include<QLocalSocket>
  5. #include<QMessageBox>
  6. QT_BEGIN_NAMESPACE
  7. namespace Ui { class Widget; }
  8. QT_END_NAMESPACE
  9. class Widget : public QWidget
  10. {
  11. Q_OBJECT
  12. public:
  13. Widget(QWidget *parent = nullptr);
  14. ~Widget();
  15. private slots:
  16. void on_pushButton_clicked();
  17. void on_pushButton_2_clicked();
  18. private:
  19. Ui::Widget *ui;
  20. QLocalSocket * localsocket;//套接字
  21. };
  22. #endif // WIDGET_H

.cpp文件:

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. Widget::Widget(QWidget *parent)
  4. : QWidget(parent)
  5. , ui(new Ui::Widget)
  6. {
  7. ui->setupUi(this);
  8. localsocket=new QLocalSocket(this);
  9. setWindowTitle("客户端");
  10. connect(localsocket,&QLocalSocket::readyRead,[=]()
  11. {
  12. QByteArray block;
  13. block=localsocket->readAll();//读取信息
  14. ui->textEdit->append(QString("服务器:%1").arg(block.data()));//聊天框添加信息
  15. });
  16. }
  17. Widget::~Widget()
  18. {
  19. delete ui;
  20. }
  21. void Widget::on_pushButton_clicked()//连接
  22. {
  23. localsocket->abort();//先断开
  24. localsocket->connectToServer("Good People");//连接
  25. }
  26. void Widget::on_pushButton_2_clicked()//发送
  27. {
  28. if(ui->lineEdit->text().isEmpty())
  29. {
  30. QMessageBox::information(this,"提示信息","请输入内容。",QMessageBox::Ok);
  31. return;
  32. }
  33. if(localsocket->isValid())
  34. {
  35. localsocket->write(ui->lineEdit->text().toUtf8());
  36. ui->textEdit->append(QString("客户端:%1").arg(ui->lineEdit->text()));
  37. }
  38. }

运行效果:

 共享内存:

QSharedMemory

QSharedMemory 提供通过多个线程和进程对共享内存段的访问。它还为单个线程或进程提供了一种锁定内存以进行独占访问的方法。

使用此类时,请注意以下平台差异:

  • Windows:QSharedMemory 不“拥有”共享内存段。当将 QSharedMemory 实例附加到特定共享内存段的所有线程或进程都销毁其 QSharedMemory 实例或退出时,Windows 内核会自动释放共享内存段。
  • Unix:QSharedMemory“拥有”共享内存段。当将 QSharedMemory 实例附加到特定共享内存段的最后一个线程或进程通过销毁其 QSharedMemory 实例与该段分离时,Unix 内核将释放共享内存段。但是,如果在未运行 QSharedMemory 析构函数的情况下最后一个线程或进程崩溃,则共享内存段将在崩溃中幸存下来。
  • HP-UX:每个进程只允许一个附加到共享内存段。这意味着 QSharedMemory 不应在 HP-UX 中同一进程中的多个线程中使用。

注意:由于是共享内存,所以使用共享内存之前使用lock()锁定共享内存,使用完后需要解锁。当 QSharedMemory 的最后一个实例从共享内存段分离时,QSharedMemory 会自动销毁该段,并且不会保留对该段的引用。

常用函数:

setKey(QString)设置此共享内存对象的平台独立
setNativeKey()设置此共享内存对象的特定于平台的本机密钥。如果 key 与当前本机键相同,则函数返回而不执行任何操作。
lock()对共享内存上锁
unlock()对共享内存解锁
size()返回附加的共享内存段的大小。如果未连接共享内存段,则返回 0。
isAttached()如果此进程连接到共享内存段返回true
errorString()错误信息
detach()将进程与共享内存段分离。如果这是附加到共享内存段的最后一个进程,则系统将释放共享内存段,即内容被销毁。
data()返回指向共享内存段内容的指针(如果已连接)
create()使用传递给构造函数的键创建大小为字节共享内存段
constData()返回指向共享内存段内容的 const 指针(如果已连接)
attach()尝试将进程附加到由传递给构造函数的键或对 setKey() 或 setNativeKey() 的调用标识的共享内存段。访问模式默认为读写

QSharedMemory::AccessMode

QSharedMemory::ReadOnly共享内存段是只读的。不允许写入共享内存段。尝试写入使用 ReadOnly 创建的共享内存段会导致程序中止。
QSharedMemory::ReadWrite读取和写入共享内存段都是允许的。

基本使用流程:

 发送内容到共享内存

  1. 设置一个标识  setKey();
  2. 使用isAttached()判断进程是否与共享内存相连
  3. 使用detach()断开连接
  4. create(size)创建共享内存
  5. 上锁
  6. 将数据写入共享内存
  7. 解锁

从共享内存中读取数据:

  1. attach()连接到共享内存
  2. 上锁
  3. 读取数据
  4. 解锁

例子:写入和读取数据

  1. QSharedMemory memory;创建对象
  2. memory.setKey("1000");//设置标识
  3. //写入数据
  4. if(memory.isAttached())//已经连接的话
  5. {
  6. memory.detach();//断开连接
  7. }
  8. QString Str="SharedMemory";
  9. QBuffer buffer;
  10. buffer.open(QBuffer::ReadWrite);
  11. QDataStream S(&buffer);
  12. S<<Str;//将数据写到缓存中
  13. memory.create(buffer.size());//创建共享内存
  14. memory.lock();//上锁
  15. char * M=(char *)memory.data();//获取memory的指针
  16. const char *N=buffer.data().data();//获取缓存的指针
  17. memcpy(M,N,buffer.size());//把数据复制到共享内存中
  18. memory.unlock();//解锁
  19. //读取数据
  20. memory.attach();//连接共享内存
  21. QBuffer buffer1;
  22. QDataStream in(&buffer);
  23. QString Str1;
  24. memory.lock();//上锁
  25. buffer.setData((char*)memory.constData(),memory.size());//存入数据
  26. buffer.open(QBuffer::ReadOnly);//只读模式
  27. in>>Str1;//读取数据
  28. memory.unlock();//解锁
  29. memory.detach();//断开连接
  30. qDebug()<<Str1;

使用共享内存实现两个进程的聊天:

ui界面:

.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include<QSharedMemory>
  5. #include<QBuffer>
  6. #include<QMessageBox>
  7. #include<QDataStream>
  8. QT_BEGIN_NAMESPACE
  9. namespace Ui { class Widget; }
  10. QT_END_NAMESPACE
  11. class Widget : public QWidget
  12. {
  13. Q_OBJECT
  14. public:
  15. Widget(QWidget *parent = nullptr);
  16. ~Widget();
  17. private slots:
  18. void on_pushButton_clicked();
  19. void on_pushButton_2_clicked();
  20. void on_pushButton_3_clicked();
  21. private:
  22. Ui::Widget *ui;
  23. QSharedMemory sharedmemory;//共享内存
  24. };
  25. #endif // WIDGET_H

.cpp文件:

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include<QDebug>
  4. Widget::Widget(QWidget *parent)
  5. : QWidget(parent)
  6. , ui(new Ui::Widget)
  7. {
  8. ui->setupUi(this);
  9. sharedmemory.setKey("GG");//设置标识
  10. }
  11. Widget::~Widget()
  12. {
  13. delete ui;
  14. }
  15. void Widget::on_pushButton_clicked()//读取数据
  16. {
  17. if(!sharedmemory.attach())
  18. {
  19. QMessageBox::information(this,"提示信息","连接共享内存失败",QMessageBox::Ok);
  20. return;
  21. }
  22. QBuffer buffer;
  23. QString S1;
  24. QDataStream in(&buffer);
  25. sharedmemory.lock();
  26. buffer.setData((char *)sharedmemory.constData(),sharedmemory.size());//设置数据
  27. buffer.open(QBuffer::ReadOnly);
  28. in>>S1;//读取数据
  29. sharedmemory.unlock();//解锁
  30. sharedmemory.detach();//断开连接
  31. ui->textEdit->append(QString("他人:%1").arg(S1));
  32. }
  33. void Widget::on_pushButton_2_clicked()//发送数据
  34. {
  35. if(sharedmemory.isAttached())//内存和进程连接
  36. {
  37. if(sharedmemory.detach())//断开连接
  38. {
  39. QMessageBox::information(this,"提示信息","已断开连接");
  40. }
  41. else
  42. {
  43. QMessageBox::information(this,"提示信息","断开连接失败");
  44. return;
  45. }
  46. }
  47. if(ui->lineEdit->text().isEmpty())
  48. {
  49. QMessageBox::information(this,"提示信息","内容不能为空。",QMessageBox::Ok);
  50. }
  51. QBuffer buffer;
  52. buffer.open(QBuffer::ReadWrite);
  53. QDataStream out(&buffer);
  54. QString Str=ui->lineEdit->text();
  55. out<<Str;//把内容写入缓存区
  56. int size=buffer.size();
  57. if(!sharedmemory.create(size))
  58. {
  59. QMessageBox::information(this,"提示信息","内存创建失败",QMessageBox::Ok);
  60. return;
  61. }
  62. sharedmemory.lock();
  63. const char * Buff=buffer.data().data();//获取buffer的指针
  64. char *Sha=(char *)sharedmemory.data();//获取共享内存的指针
  65. memcpy(Sha,Buff,qMin(sharedmemory.size(),size));
  66. sharedmemory.unlock();//解锁
  67. ui->textEdit->append(QString("本人:%1").arg(ui->lineEdit->text()));//显示信息
  68. }
  69. void Widget::on_pushButton_3_clicked()//断开连接
  70. {
  71. if(sharedmemory.detach())//断开连接
  72. {
  73. QMessageBox::information(this,"提示信息","已断开连接");
  74. }
  75. }

main函数:

  1. #include "widget.h"
  2. #include <QApplication>
  3. int main(int argc, char *argv[])
  4. {
  5. QApplication a(argc, argv);
  6. Widget w1;
  7. w1.setWindowTitle("widget1");
  8. w1.show();//进程1
  9. Widget w2;
  10. w2.setWindowTitle("widget2");
  11. w2.show();//进程2
  12. return a.exec();
  13. }

这里解释一下为什么要有断开连接的按钮:

经过多次测试发现 :

A端发完数据,B端接收数据后,A端需要主动断开连接后B端才能正常发送数据。

(懂的大佬请在评论区解释一下)

效果:

widget2 发送信息给widget1

 记住发送端需要主动断开连接:

widget1 发送信息给 widget2

 QProcess

参考文章:关于Qt的QProcess进程间双向通信_qprocess进程间通信_weixin_46424582的博客-CSDN博客

QProcess进程间通信:

 主线程构建思路:

  • 发送数据到子进程:使用write()函数
  • 读取子进程发送的数据数据:监听readReadyStandardOutput()信号

创建一个项目:

ui界面中添加以下控件: textEdit   pushButton  lineEdit

.h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include<QProcess>
  5. #include<QMessageBox>
  6. #include<QDebug>
  7. QT_BEGIN_NAMESPACE
  8. namespace Ui { class Widget; }
  9. QT_END_NAMESPACE
  10. class Widget : public QWidget
  11. {
  12. Q_OBJECT
  13. public:
  14. Widget(QWidget *parent = nullptr);
  15. ~Widget();
  16. private slots:
  17. void on_pushButton_clicked();
  18. void on_pushButton_2_clicked();
  19. private:
  20. Ui::Widget *ui;
  21. QProcess *process;
  22. };
  23. #endif // WIDGET_H

.cpp文件:

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. Widget::Widget(QWidget *parent)
  4. : QWidget(parent)
  5. , ui(new Ui::Widget)
  6. {
  7. ui->setupUi(this);
  8. setWindowTitle("主进程");
  9. process=new QProcess;
  10. connect(process,&QProcess::readyReadStandardOutput,[=]()//读取信息
  11. {
  12. QString S=process->readAllStandardOutput().data();
  13. ui->textEdit->append(QString("子线程:%1").arg(S));
  14. });
  15. }
  16. Widget::~Widget()
  17. {
  18. delete ui;
  19. }
  20. void Widget::on_pushButton_clicked()//打开子进程
  21. {
  22. //子线程的开启
  23. process->start("D:/Qt_data/build-QProcess_text1-Desktop_Qt_5_9_9_MinGW_32bit-Debug/debug/QProcess_text1.exe");
  24. }
  25. void Widget::on_pushButton_2_clicked()//发送数据
  26. {
  27. if(ui->lineEdit->text().isEmpty())
  28. {
  29. QMessageBox::information(this,"提示信息","输入框不能为空。",QMessageBox::Ok);
  30. return;
  31. }
  32. if(process->Running)
  33. {
  34. QString S=ui->lineEdit->text();//获取lineEdit中的内容
  35. process->write(S.toStdString().c_str());//写入数据
  36. ui->textEdit->append("父线程:"+S);//在聊天框显示内容
  37. }
  38. return;
  39. }

子线程的构建思路:

  • 发送数据到父线程:使用文件打开 stdout,然后把数据写入stdout中
  • 获取父线程发送过来的数据:
    • linux中:使用QSocketNotifier 监听 stdin文件,当改文件有变化是,读取信息
    • Windows中:需要开启一个线程来管理stdin的文件变化,这个需要使用Windows API函数

这里介绍Windows中的子线程构建:

创建一个项目:

在pro文件中添加:

QT +=concurrent

ui界面中添加:

 .h文件:

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include<QFile>
  5. #include<QSocketNotifier>
  6. #include<QBuffer>
  7. #include<QMessageBox>
  8. #include<QFuture>
  9. #include<QtConcurrent>
  10. #include<QIODevice>
  11. QT_BEGIN_NAMESPACE
  12. namespace Ui { class Widget; }
  13. QT_END_NAMESPACE
  14. class Widget : public QWidget
  15. {
  16. Q_OBJECT
  17. public:
  18. Widget(QWidget *parent = nullptr);
  19. ~Widget();
  20. private slots:
  21. void on_pushButton_clicked();
  22. void readStdin();//读取数据
  23. private:
  24. Ui::Widget *ui;
  25. QFile file;
  26. signals:
  27. void sig_log(QString S);//显示数据
  28. void sig_receivedCommand(QString S);//显示数据
  29. };
  30. #endif // WIDGET_H

.cpp文件:

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include<iostream>
  4. #include<windows.h>
  5. #include<io.h>
  6. #include<fcntl.h>
  7. #include<QSocketNotifier>
  8. Widget::Widget(QWidget *parent)
  9. : QWidget(parent)
  10. , ui(new Ui::Widget)
  11. {
  12. ui->setupUi(this);
  13. setWindowTitle("子进程");
  14. QFuture<void> fu=QtConcurrent::run(this,&Widget::readStdin);//开启一个线程
  15. connect(this,&Widget::sig_receivedCommand,this,[&](QString s)
  16. {
  17. ui->textEdit->append("父线程:"+s);
  18. });
  19. connect(this,&Widget::sig_log,this,[=](QString s)
  20. {
  21. ui->textEdit->append("父线程:"+s);
  22. });
  23. }
  24. Widget::~Widget()
  25. {
  26. delete ui;
  27. }
  28. void Widget::readStdin()//读取数据
  29. {
  30. bool ok=true;
  31. char chbuf[1024];//缓存
  32. DWORD dwRead;//32位无符号整数
  33. HANDLE hStdinDup;//HANDLE 句柄类型
  34. const HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);//GetStdHandle获取标准输入的句柄
  35. if(hStdin==INVALID_HANDLE_VALUE)//为无效句柄的话
  36. return;
  37. DuplicateHandle(GetCurrentProcess(),hStdin,
  38. GetCurrentProcess(),&hStdinDup,0,false,DUPLICATE_SAME_ACCESS);//创建一个新句柄
  39. CloseHandle(hStdin);//关闭旧句柄
  40. while(ok)
  41. {
  42. ok=ReadFile(hStdinDup,chbuf,sizeof(chbuf),&dwRead,NULL);//读取hstdinDup句柄文件中的数据
  43. emit sig_log(QLatin1String("ok is:")+QString::number(ok));
  44. if(ok &&dwRead!=0)
  45. {
  46. emit sig_receivedCommand(QString::fromUtf8(chbuf,dwRead));//读取数据
  47. }
  48. }
  49. }
  50. void Widget::on_pushButton_clicked()//发送数据
  51. {
  52. if(ui->lineEdit->text().isEmpty())
  53. {
  54. QMessageBox::information(this,"提示信息","输入框不能为空。",QMessageBox::Ok);
  55. return;
  56. }
  57. QFile fileout;
  58. if(fileout.open(stdout,QIODevice::WriteOnly))
  59. {
  60. QString S=ui->lineEdit->text();
  61. fileout.write(S.toStdString().c_str());
  62. ui->textEdit->append("子线程:"+S);
  63. fileout.close();//关闭文件
  64. }
  65. }

运行效果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/121171
推荐阅读
相关标签
  

闽ICP备14008679号