文章目录
- 一、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_OBJECTpublic: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)) {// 发送关闭提示给socketsocket->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_OBJECTpublic: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^/)
注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除