赞
踩
使用套接字的方式,进行通信(之前介绍了,这里就不介绍了)。
跨平台Qt网络模块提供的类使本地网络编程变得可移植且简单。它提供了QLocalServer和QLocalSocket类,允许在本地设置中进行类似网络的通信。它们的 TCP 对应项可以用作直接替代品,以使跨网络通信工作。(使用方法和TCP相似)
使用方法:
- listen()监听指定密钥上的传入连接
- 有客户端连接时,会发出newConnection()信号
- 使用nextPengingConnection()获取连接的QLocalSocket
- close()关闭监听
listen(QString) | 监听 |
close() | 关闭监听 |
errorString() | 报告的当前错误的人类可读消息 |
fullServerName() | 返回服务器正在侦听的完整路径 |
isListrening() | 服务器正在侦听传入连接,true,否则为false |
maxPendingConnection() | 返回挂起的接受连接的最大数目。默认值为 30 |
nextPengingConnection() | 将一个挂起的连接作为连接的 QLocalServer 对象返回 |
serverName() | 则返回服务器名称 |
socketOption() | 返回在套接字上设置的套接字选项 |
waitForNewConnection(int) | 堵塞多少毫秒或直到传入连接可用 |
在Windows上是一个命名管道,在Unix上是一个本地套接字。
QLocalSocket 设计用于事件循环,但也可以在没有事件循环的情况下使用它。在这种情况下,您必须使用 waitForConnected()、waitForReadyRead()、waitForBytesWrite() 和 waitForDisconnected(),它们会阻止操作完成或超时到期。
使用方法:
- 使用connectToServer()与服务器简建立连接。
- 可以使用readData()读取数据,writeData()写入数据
- abort()断开连接
- close(),或disconnectFromServer()关闭套接字
connectToServer() | 连接服务器 |
bytesAvailable() | 获取数据大小 |
error() | 返回上次发生的错误类型 |
flush() | 此函数尽可能多地从内部写入缓冲区写入套接字,而不会阻塞。如果写入了任何数据返回true |
fullServerName() | 返回套接字连接到的服务器路径 |
isVaild() | 判断套接字是否可用 |
readData() | 读取数据 |
writeData() | 写入数据 |
setReadBufferSize() | 设置内部缓冲区大小 |
waitForConnected() | 等待连接 |
waitForReadyRead() | 等待读取 |
waitForBytesWrite() | 等待写入 |
waitForDisconnected() | 等待断开 |
pro文件中添加:
QT +network
在ui界面中添加:
QTextEdit 、QPushButton 和QLineEdit
.h文件:
- #ifndef WIDGET_H
- #define WIDGET_H
-
- #include <QWidget>
- #include<QLocalServer>
- #include<QLocalSocket>
- #include<QMessageBox>
- #include<QDataStream>
-
- QT_BEGIN_NAMESPACE
- namespace Ui { class Widget; }
- QT_END_NAMESPACE
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
-
- private slots:
- void on_pushButton_clicked();
-
- private:
- Ui::Widget *ui;
- QLocalServer *localserver;//服务端
- QLocalSocket*localsocket=nullptr;//套接字
-
- };
- #endif // WIDGET_H
.cpp文件:
- #include "widget.h"
- #include "ui_widget.h"
-
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- setWindowTitle("服务器端");
- localserver=new QLocalServer(this);
- localserver->listen("Good People");//Good People作为连接字符
- connect(localserver,&QLocalServer::newConnection,[=]()
- {
- localsocket=localserver->nextPendingConnection();//获取连接的套接字
- connect(localsocket,&QLocalSocket::readyRead,[=]()//如果有可读数据的话
- {
- QByteArray block;
- block=localsocket->readAll();
- QString S=block.data();
- ui->textEdit->append(QString("客户端:%1").arg(S));
- });
-
- });
- }
-
- Widget::~Widget()
- {
- delete ui;
- }
-
-
- void Widget::on_pushButton_clicked()//发送数据
- {
- if(ui->lineEdit->text().isEmpty())
- {
- QMessageBox::information(this,"提示信息","请输入内容",QMessageBox::Ok);
- return;
- }
- ui->textEdit->setText(QString("服务器端:%1").arg(ui->lineEdit->text()));//设置内容
- if(localsocket->isValid())
- {
- QString S=ui->lineEdit->text();
- localsocket->write(S.toUtf8());//发送消息
- }
-
- }
pro文件中添加:
QT +=network
ui界面中添加:
.h文件:
- #ifndef WIDGET_H
- #define WIDGET_H
-
- #include <QWidget>
- #include<QLocalSocket>
- #include<QMessageBox>
- QT_BEGIN_NAMESPACE
- namespace Ui { class Widget; }
- QT_END_NAMESPACE
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
-
- private slots:
- void on_pushButton_clicked();
-
- void on_pushButton_2_clicked();
-
- private:
- Ui::Widget *ui;
- QLocalSocket * localsocket;//套接字
- };
- #endif // WIDGET_H
.cpp文件:
- #include "widget.h"
- #include "ui_widget.h"
-
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- localsocket=new QLocalSocket(this);
- setWindowTitle("客户端");
- connect(localsocket,&QLocalSocket::readyRead,[=]()
- {
- QByteArray block;
- block=localsocket->readAll();//读取信息
- ui->textEdit->append(QString("服务器:%1").arg(block.data()));//聊天框添加信息
- });
-
- }
-
- Widget::~Widget()
- {
- delete ui;
- }
-
-
- void Widget::on_pushButton_clicked()//连接
- {
- localsocket->abort();//先断开
- localsocket->connectToServer("Good People");//连接
- }
-
- void Widget::on_pushButton_2_clicked()//发送
- {
- if(ui->lineEdit->text().isEmpty())
- {
- QMessageBox::information(this,"提示信息","请输入内容。",QMessageBox::Ok);
- return;
- }
- if(localsocket->isValid())
- {
- localsocket->write(ui->lineEdit->text().toUtf8());
- ui->textEdit->append(QString("客户端:%1").arg(ui->lineEdit->text()));
- }
-
-
- }
运行效果:
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::ReadOnly | 共享内存段是只读的。不允许写入共享内存段。尝试写入使用 ReadOnly 创建的共享内存段会导致程序中止。 |
QSharedMemory::ReadWrite | 读取和写入共享内存段都是允许的。 |
发送内容到共享内存
- 设置一个标识 setKey();
- 使用isAttached()判断进程是否与共享内存相连
- 使用detach()断开连接
- create(size)创建共享内存
- 上锁
- 将数据写入共享内存
- 解锁
从共享内存中读取数据:
- attach()连接到共享内存
- 上锁
- 读取数据
- 解锁
例子:写入和读取数据
- QSharedMemory memory;创建对象
- memory.setKey("1000");//设置标识
-
- //写入数据
- if(memory.isAttached())//已经连接的话
- {
- memory.detach();//断开连接
- }
- QString Str="SharedMemory";
-
- QBuffer buffer;
- buffer.open(QBuffer::ReadWrite);
- QDataStream S(&buffer);
- S<<Str;//将数据写到缓存中
- memory.create(buffer.size());//创建共享内存
- memory.lock();//上锁
- char * M=(char *)memory.data();//获取memory的指针
- const char *N=buffer.data().data();//获取缓存的指针
- memcpy(M,N,buffer.size());//把数据复制到共享内存中
- memory.unlock();//解锁
-
- //读取数据
- memory.attach();//连接共享内存
- QBuffer buffer1;
- QDataStream in(&buffer);
- QString Str1;
-
- memory.lock();//上锁
- buffer.setData((char*)memory.constData(),memory.size());//存入数据
- buffer.open(QBuffer::ReadOnly);//只读模式
- in>>Str1;//读取数据
- memory.unlock();//解锁
-
- memory.detach();//断开连接
- qDebug()<<Str1;
ui界面:
.h文件:
- #ifndef WIDGET_H
- #define WIDGET_H
-
- #include <QWidget>
- #include<QSharedMemory>
- #include<QBuffer>
- #include<QMessageBox>
- #include<QDataStream>
- QT_BEGIN_NAMESPACE
- namespace Ui { class Widget; }
- QT_END_NAMESPACE
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
-
- private slots:
- void on_pushButton_clicked();
-
- void on_pushButton_2_clicked();
-
- void on_pushButton_3_clicked();
-
- private:
- Ui::Widget *ui;
- QSharedMemory sharedmemory;//共享内存
- };
- #endif // WIDGET_H
.cpp文件:
- #include "widget.h"
- #include "ui_widget.h"
- #include<QDebug>
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- sharedmemory.setKey("GG");//设置标识
- }
-
- Widget::~Widget()
- {
- delete ui;
- }
-
-
- void Widget::on_pushButton_clicked()//读取数据
- {
- if(!sharedmemory.attach())
- {
- QMessageBox::information(this,"提示信息","连接共享内存失败",QMessageBox::Ok);
- return;
- }
- QBuffer buffer;
- QString S1;
- QDataStream in(&buffer);
- sharedmemory.lock();
- buffer.setData((char *)sharedmemory.constData(),sharedmemory.size());//设置数据
- buffer.open(QBuffer::ReadOnly);
- in>>S1;//读取数据
- sharedmemory.unlock();//解锁
- sharedmemory.detach();//断开连接
- ui->textEdit->append(QString("他人:%1").arg(S1));
- }
-
- void Widget::on_pushButton_2_clicked()//发送数据
- {
- if(sharedmemory.isAttached())//内存和进程连接
- {
- if(sharedmemory.detach())//断开连接
- {
- QMessageBox::information(this,"提示信息","已断开连接");
- }
- else
- {
- QMessageBox::information(this,"提示信息","断开连接失败");
- return;
- }
- }
- if(ui->lineEdit->text().isEmpty())
- {
- QMessageBox::information(this,"提示信息","内容不能为空。",QMessageBox::Ok);
- }
- QBuffer buffer;
- buffer.open(QBuffer::ReadWrite);
- QDataStream out(&buffer);
- QString Str=ui->lineEdit->text();
- out<<Str;//把内容写入缓存区
- int size=buffer.size();
- if(!sharedmemory.create(size))
- {
- QMessageBox::information(this,"提示信息","内存创建失败",QMessageBox::Ok);
- return;
- }
- sharedmemory.lock();
- const char * Buff=buffer.data().data();//获取buffer的指针
- char *Sha=(char *)sharedmemory.data();//获取共享内存的指针
- memcpy(Sha,Buff,qMin(sharedmemory.size(),size));
- sharedmemory.unlock();//解锁
- ui->textEdit->append(QString("本人:%1").arg(ui->lineEdit->text()));//显示信息
- }
-
- void Widget::on_pushButton_3_clicked()//断开连接
- {
- if(sharedmemory.detach())//断开连接
- {
- QMessageBox::information(this,"提示信息","已断开连接");
- }
- }
main函数:
- #include "widget.h"
-
- #include <QApplication>
-
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
- Widget w1;
- w1.setWindowTitle("widget1");
- w1.show();//进程1
- Widget w2;
- w2.setWindowTitle("widget2");
- w2.show();//进程2
- return a.exec();
- }
这里解释一下为什么要有断开连接的按钮:
经过多次测试发现 :
A端发完数据,B端接收数据后,A端需要主动断开连接后B端才能正常发送数据。
(懂的大佬请在评论区解释一下)
效果:
widget2 发送信息给widget1
记住发送端需要主动断开连接:
widget1 发送信息给 widget2
参考文章:关于Qt的QProcess进程间双向通信_qprocess进程间通信_weixin_46424582的博客-CSDN博客
- 发送数据到子进程:使用write()函数
- 读取子进程发送的数据数据:监听readReadyStandardOutput()信号
创建一个项目:
ui界面中添加以下控件: textEdit pushButton lineEdit
.h文件:
- #ifndef WIDGET_H
- #define WIDGET_H
-
- #include <QWidget>
- #include<QProcess>
- #include<QMessageBox>
- #include<QDebug>
- QT_BEGIN_NAMESPACE
- namespace Ui { class Widget; }
- QT_END_NAMESPACE
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
-
- private slots:
- void on_pushButton_clicked();
-
- void on_pushButton_2_clicked();
- private:
- Ui::Widget *ui;
- QProcess *process;
- };
- #endif // WIDGET_H
.cpp文件:
- #include "widget.h"
- #include "ui_widget.h"
-
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- setWindowTitle("主进程");
- process=new QProcess;
- connect(process,&QProcess::readyReadStandardOutput,[=]()//读取信息
- {
- QString S=process->readAllStandardOutput().data();
- ui->textEdit->append(QString("子线程:%1").arg(S));
- });
-
- }
-
- Widget::~Widget()
- {
- delete ui;
- }
-
- void Widget::on_pushButton_clicked()//打开子进程
- {
- //子线程的开启
- 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()//发送数据
- {
-
- if(ui->lineEdit->text().isEmpty())
- {
- QMessageBox::information(this,"提示信息","输入框不能为空。",QMessageBox::Ok);
- return;
- }
- if(process->Running)
- {
- QString S=ui->lineEdit->text();//获取lineEdit中的内容
- process->write(S.toStdString().c_str());//写入数据
- ui->textEdit->append("父线程:"+S);//在聊天框显示内容
- }
- return;
-
- }
- 发送数据到父线程:使用文件打开 stdout,然后把数据写入stdout中
- 获取父线程发送过来的数据:
- linux中:使用QSocketNotifier 监听 stdin文件,当改文件有变化是,读取信息
- Windows中:需要开启一个线程来管理stdin的文件变化,这个需要使用Windows API函数
这里介绍Windows中的子线程构建:
创建一个项目:
在pro文件中添加:
QT +=concurrent
ui界面中添加:
- #ifndef WIDGET_H
- #define WIDGET_H
-
- #include <QWidget>
- #include<QFile>
- #include<QSocketNotifier>
- #include<QBuffer>
- #include<QMessageBox>
- #include<QFuture>
- #include<QtConcurrent>
- #include<QIODevice>
- QT_BEGIN_NAMESPACE
- namespace Ui { class Widget; }
- QT_END_NAMESPACE
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
-
- private slots:
- void on_pushButton_clicked();
- void readStdin();//读取数据
-
- private:
- Ui::Widget *ui;
- QFile file;
- signals:
- void sig_log(QString S);//显示数据
- void sig_receivedCommand(QString S);//显示数据
- };
- #endif // WIDGET_H
- #include "widget.h"
- #include "ui_widget.h"
- #include<iostream>
- #include<windows.h>
- #include<io.h>
- #include<fcntl.h>
- #include<QSocketNotifier>
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- setWindowTitle("子进程");
-
- QFuture<void> fu=QtConcurrent::run(this,&Widget::readStdin);//开启一个线程
- connect(this,&Widget::sig_receivedCommand,this,[&](QString s)
- {
- ui->textEdit->append("父线程:"+s);
- });
- connect(this,&Widget::sig_log,this,[=](QString s)
- {
- ui->textEdit->append("父线程:"+s);
- });
-
- }
-
- Widget::~Widget()
- {
- delete ui;
- }
-
-
- void Widget::readStdin()//读取数据
- {
- bool ok=true;
- char chbuf[1024];//缓存
- DWORD dwRead;//32位无符号整数
- HANDLE hStdinDup;//HANDLE 句柄类型
-
- const HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);//GetStdHandle获取标准输入的句柄
- if(hStdin==INVALID_HANDLE_VALUE)//为无效句柄的话
- return;
- DuplicateHandle(GetCurrentProcess(),hStdin,
- GetCurrentProcess(),&hStdinDup,0,false,DUPLICATE_SAME_ACCESS);//创建一个新句柄
- CloseHandle(hStdin);//关闭旧句柄
- while(ok)
- {
- ok=ReadFile(hStdinDup,chbuf,sizeof(chbuf),&dwRead,NULL);//读取hstdinDup句柄文件中的数据
- emit sig_log(QLatin1String("ok is:")+QString::number(ok));
- if(ok &&dwRead!=0)
- {
- emit sig_receivedCommand(QString::fromUtf8(chbuf,dwRead));//读取数据
- }
- }
-
- }
-
- void Widget::on_pushButton_clicked()//发送数据
- {
- if(ui->lineEdit->text().isEmpty())
- {
- QMessageBox::information(this,"提示信息","输入框不能为空。",QMessageBox::Ok);
- return;
- }
- QFile fileout;
- if(fileout.open(stdout,QIODevice::WriteOnly))
- {
- QString S=ui->lineEdit->text();
- fileout.write(S.toStdString().c_str());
- ui->textEdit->append("子线程:"+S);
- fileout.close();//关闭文件
- }
-
- }
运行效果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。