文章目录
- 一、IPC通信示例图
- 1.1 设置关键字并连接的示例图
- 1.2 进程间简单的数据通信示例图
- 1.3 断开连接的示例图
- 1.3.1 由Server主动断开连接
- 1.3.2 由Socket主动断开连接
- 1.4 Server停止监听后的效果
- 二、个人理解与一些心得
- 三、一些疑问(求教 家人们😂)
- 四、源码
- CMainWindowServer
- CMainWindowServer.h
- CMainWindowServer.cpp
- CMainWindowServer.ui
- CMainWindowSocket
- CMainWindowSocket.h
- CMainWindowSocket.cpp
- CMainWindowSocket.ui
- 总结
- 相关文章
一、IPC通信示例图
1.1 设置关键字并连接的示例图
如下,分别在各个界面的关键字控件中填入key,依次连接。
1.2 进程间简单的数据通信示例图
如下,简单演示了server与全部、指定socket通信及接收socket发送的数据。
1.3 断开连接的示例图
1.3.1 由Server主动断开连接
如下,演示了单独断开一个及断开全部的操作,其中断开操作是由server发送数据通知socket断开,server这边则等待断开返回。
1.3.2 由Socket主动断开连接
如下演示了socket程序主动断开的操作
1.4 Server停止监听后的效果
如下,演示了server停止监听后仍可以与已经连接过的socket的通信的效果。
二、个人理解与一些心得
- 若要使用QLocalServer/QLocalSocket,需要在 pro添加network模块(添加这一行QT += network)。
- 在我个人使用中发现,在同一进程中,调用socket的write是不会触发当前进程的readyRead信号链接的信号槽。
- 在QLocalServer停止监听后不会影响已经连接好的Socket对象,因为QLocalServer的close仅负责停止监听,并不断开。
三、一些疑问(求教 家人们😂)
- 在帮助中又下方的帮助代码,但是在本地测试发现不能先调用disconnectFromServer,后面的waitForDisconnected总是拿不到状态。
socket->disconnectFromServer();
if (socket->waitForDisconnected(1000))
qDebug("Disconnected!");
- 以及在个人理解中的第2点也存在一些疑问
四、源码
CMainWindowServer
CMainWindowServer.h
#ifndef CMAINWINDOWSERVER_H
#define CMAINWINDOWSERVER_H
#include <QMainWindow>
#include <QLocalServer>
namespace Ui {
class CMainWindowServer;
}
class QLocalSocket;
class CMainWindowServer : public QMainWindow
{
Q_OBJECT
public:
explicit CMainWindowServer(QWidget *parent = nullptr);
~CMainWindowServer();
private:
/**
* @brief disconnectSocketByStr 指定socket断开函数(复用)
* @param socketStr 指定的socket套接字字符串
*/
void disconnectSocketByStr(const QString &socketStr);
private slots:
/**
* @brief on_btnListen_clicked 开始监听按钮
*/
void on_btnListen_clicked();
/**
* @brief on_btnStopListen_clicked 停止监听按钮
*/
void on_btnStopListen_clicked();
/**
* @brief on_newConnection 新连接槽函数
*/
void on_newConnection();
/**
* @brief on_socketReadyRead 数据接收槽函数
*/
void on_socketReadyRead();
/**
* @brief on_btnDisconnectSocket_clicked 断开socket槽函数
*/
void on_btnDisconnectSocket_clicked();
/**
* @brief on_btnSend_clicked 数据发送按钮
*/
void on_btnSend_clicked();
private:
Ui::CMainWindowServer *ui;
QLocalServer m_localServer; // 通信服务对象
QList<QLocalSocket *> m_listLocalSockets; // 本地套接字列表
};
#endif // CMAINWINDOWSERVER_H
CMainWindowServer.cpp
#include "CMainWindowServer.h"
#include "ui_CMainWindowServer.h"
#include <QLocalServer>
#include <QMessageBox>
#include <QLocalSocket>
#include <QDebug>
#include <QTimer>
CMainWindowServer::CMainWindowServer(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::CMainWindowServer)
{
ui->setupUi(this);
// 关联套接字连接槽函数
connect(&m_localServer, &QLocalServer::newConnection, this, &CMainWindowServer::on_newConnection);
}
CMainWindowServer::~CMainWindowServer()
{
delete ui;
}
void CMainWindowServer::disconnectSocketByStr(const QString &socketStr)
{
// 强转当前指针字符串或者socket指针对象
QLocalSocket *socket = (QLocalSocket *)socketStr.toUInt();
// 判断是否存在于socket容器中
if(m_listLocalSockets.contains(socket)) {
// 发送关闭提示给socket
socket->write(u8"服务器断开!");
// 等待3000毫秒接收断开链接的信息
if(!socket->waitForDisconnected(3000)) {
QMessageBox::information(this, u8"提示", "断开超时");
}
else {
// 移除当前位置的控件
QMessageBox::information(this, u8"提示", "断开成功");
// 移除当前指定的
ui->comboBoxSockets->removeItem(ui->comboBoxSockets->findText(socketStr));
m_listLocalSockets.removeOne(socket);
}
}
else {
QMessageBox::information(this, u8"提示", socketStr + u8"地址无记录");
}
}
void CMainWindowServer::on_btnListen_clicked()
{
QString listenKey = ui->lineEditListenKey->text();
// 获取是否监听成功
bool flag = m_localServer.listen(listenKey);
if(!flag) {
QMessageBox::information(this, u8"提示", m_localServer.errorString());
}
else {
QMessageBox::information(this, u8"提示", u8"监听成功");
// 监听后‘开始监听’按钮禁用,‘停止监听’按钮启用
ui->btnListen->setEnabled(false);
ui->btnStopListen->setEnabled(true);
}
}
void CMainWindowServer::on_btnStopListen_clicked()
{
m_localServer.close();
if(!m_localServer.isListening()) {
QMessageBox::information(this, u8"提示", u8"停止监听成功");
// 停止监听后‘开始监听’按钮启用,‘停止监听’按钮禁用
ui->btnListen->setEnabled(true);
ui->btnStopListen->setEnabled(false);
}
else {
QMessageBox::information(this, u8"提示", u8"停止监听失败");
}
}
void CMainWindowServer::on_newConnection()
{
// 判断是否存在新的socket连接
if(m_localServer.hasPendingConnections()) {
// 获取套接字对象
QLocalSocket *socketTmp = m_localServer.nextPendingConnection();
// 套接字对象添加到套接字容器中
m_listLocalSockets.append(socketTmp);
// 套接字地址转为数值
QString socketStr = QString::number((uint64_t)socketTmp);
// 套接字文本添加到下拉列表中并在界面做出连接提示
ui->comboBoxSockets->addItem(socketStr);
ui->textEdit->append(socketStr + "加入连接!");
// 关联新数据的信号槽
connect(socketTmp, &QLocalSocket::readyRead, this, &CMainWindowServer::on_socketReadyRead);
}
}
void CMainWindowServer::on_socketReadyRead()
{
// 获取发送信号的对象
QLocalSocket *curSocket = dynamic_cast<QLocalSocket *>(sender());
// 将数据直接读取并添加到多行文本框中
ui->textEdit->append(QString::number((uint64_t)curSocket) + ":" + curSocket->readAll());
}
void CMainWindowServer::on_btnDisconnectSocket_clicked()
{
// 获取将要断开的文本并弹出断开提示
QString socketStr = ui->comboBoxSockets->currentText();
QMessageBox::StandardButton flag = QMessageBox::information(this, u8"提示", u8"是否断开" + socketStr + "?");
if(QMessageBox::Ok != flag) {
return;
}
// 根据断开文本做不不同断开操作
if(0 == socketStr.compare(u8"全部")) {
foreach(QLocalSocket *socket, m_listLocalSockets) {
disconnectSocketByStr(QString::number((uint64_t)socket));
}
}
else {
disconnectSocketByStr(socketStr);
}
}
void CMainWindowServer::on_btnSend_clicked()
{
// 获取将要接收数据的识别文本
QString socketStr = ui->comboBoxSockets->currentText();
// 获取将要发送的数据
QString data = ui->textEditSendData->toPlainText();
// 根据识别文本做出不同的操作
if(0 == socketStr.compare(u8"全部")) {
foreach(QLocalSocket *socket, m_listLocalSockets) {
socket->write(data.toUtf8());
}
}
else {
// 直接将当前文本强转为套接字对象(因为该文本为指针地址强转而来)
QLocalSocket *socket = (QLocalSocket *)socketStr.toUInt();
if(m_listLocalSockets.contains(socket)) {
socket->write(data.toUtf8());
}
else {
QMessageBox::information(this, u8"提示", socketStr + "地址找不到");
}
}
}
CMainWindowServer.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CMainWindowServer</class>
<widget class="QMainWindow" name="CMainWindowServer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>340</width>
<height>420</height>
</rect>
</property>
<property name="windowTitle">
<string>CMainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,3,1,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEditListenKey"/>
</item>
<item>
<widget class="QPushButton" name="btnListen">
<property name="text">
<string>监听</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnStopListen">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>停止监听</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QComboBox" name="comboBoxSockets">
<item>
<property name="text">
<string>全部</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDisconnectSocket">
<property name="text">
<string>断开当前链接选项</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEdit"/>
</item>
<item>
<widget class="QTextEdit" name="textEditSendData"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnSend">
<property name="text">
<string>发送</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>340</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
CMainWindowSocket
CMainWindowSocket.h
#ifndef CMAINWINDOWSOCKET_H
#define CMAINWINDOWSOCKET_H
#include <QMainWindow>
#include <QLocalSocket>
namespace Ui {
class CMainWindowSocket;
}
class CMainWindowSocket : public QMainWindow
{
Q_OBJECT
public:
explicit CMainWindowSocket(QWidget *parent = nullptr);
~CMainWindowSocket();
private slots:
/**
* @brief on_btnConnect_clicked 连接按钮信号槽
*/
void on_btnConnect_clicked();
/**
* @brief on_btnSend_clicked 发送按钮信号槽
*/
void on_btnSend_clicked();
/**
* @brief on_btnDisConnected_clicked 断开连接信号槽
*/
void on_btnDisConnected_clicked();
/**
* @brief on_socketReadyRead 数据接收信号槽
*/
void on_socketReadyRead();
private:
Ui::CMainWindowSocket *ui;
QLocalSocket m_localSocket; // 套接字对象
};
#endif // CMAINWINDOWSOCKET_H
CMainWindowSocket.cpp
#include "CMainWindowSocket.h"
#include "ui_CMainWindowSocket.h"
#include <QMessageBox>
#include <QTimer>
CMainWindowSocket::CMainWindowSocket(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::CMainWindowSocket)
{
ui->setupUi(this);
// 关联数据接收信号槽
connect(&m_localSocket, &QLocalSocket::readyRead, this, &CMainWindowSocket::on_socketReadyRead);
}
CMainWindowSocket::~CMainWindowSocket()
{
delete ui;
}
void CMainWindowSocket::on_btnConnect_clicked()
{
// 根据key连接服务
m_localSocket.connectToServer(ui->lineEditConnectKey->text());
// 等待一秒是否连接成功
if(m_localSocket.waitForConnected(1000)) {
QString tip = u8"连接成功";
// 连接成功后打开读写通道
if(!m_localSocket.open(QIODevice::ReadWrite)) {
tip.append(QString(u8",但Socket读写打开失败(%1)").arg(m_localSocket.errorString()));
}
QMessageBox::information(this, u8"提示", tip);
// 连接后‘连接’按钮禁用,‘断开连接’按钮启用
ui->btnConnect->setEnabled(false);
ui->btnDisConnected->setEnabled(true);
}
else {
QMessageBox::information(this, u8"提示", u8"连接失败");
}
}
void CMainWindowSocket::on_btnSend_clicked()
{
// 写入数据
m_localSocket.write(ui->textEditSendData->toPlainText().toUtf8());
// 等待写入信号,若未写入成功弹出提示
if(!m_localSocket.waitForBytesWritten(100)) {
QMessageBox::information(this, u8"提示", m_localSocket.errorString());
}
}
void CMainWindowSocket::on_btnDisConnected_clicked()
{
if(QLocalSocket::ConnectedState == m_localSocket.state()) {
m_localSocket.write(QString(u8"%1已断开!").arg((uint64_t)this).toUtf8());
m_localSocket.disconnectFromServer();
// 断开连接后‘连接’按钮启用,‘断开连接’按钮禁用
ui->btnConnect->setEnabled(true);
ui->btnDisConnected->setEnabled(false);
}
else {
QMessageBox::information(this, u8"提示", u8"断开失败,当前并非连接状态!" );
}
}
void CMainWindowSocket::on_socketReadyRead()
{
// 读取索引数据
QString data = m_localSocket.readAll();
// 识别数据文本,当复合条件是断开连接
if(0 == data.compare(u8"服务器断开!")) {
on_btnDisConnected_clicked();
}
ui->textEdit->append(data);
}
CMainWindowSocket.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CMainWindowSocket</class>
<widget class="QMainWindow" name="CMainWindowSocket">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>420</height>
</rect>
</property>
<property name="windowTitle">
<string>CMainWindowSocket</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,3,1,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEditConnectKey"/>
</item>
<item>
<widget class="QPushButton" name="btnConnect">
<property name="text">
<string>连接</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDisConnected">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>断开连接</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="textEdit"/>
</item>
<item>
<widget class="QTextEdit" name="textEditSendData"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnSend">
<property name="text">
<string>发送</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
总结
在使用QLocalServer和QLocalSocket的过程中,发现QLocalSocket不是数据通道的持有对象,而是数据通道本身(如共享内存是通过data获取共享内存的地址,而QLocalSocket是直接调用write写入,当然和他的继承有关系),而相对来说QLocalServer更像使用者。不过IPC相对于共享内存来说可能有及时性的特点,因为数据一来IPC就直接读取,而共享内存则是需要定时读取数据。
相关文章
Qt之进程通信-共享内存(含源码+注释)
友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)
注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除