文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT Widget 开发(一)工程文件结构-CSDN博客
开源 C++ QT Widget 开发(二)基本控件应用-CSDN博客
开源 C++ QT Widget 开发(三)图表--波形显示器-CSDN博客
开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-CSDN博客
开源 C++ QT Widget 开发(五)通讯--串口调试-CSDN博客
开源 C++ QT Widget 开发(六)通讯--TCP调试-CSDN博客
开源 C++ QT Widget 开发(七)线程--多线程及通讯-CSDN博客
开源 C++ QT Widget 开发(八)网络--Http文件下载-CSDN博客
开源 C++ QT Widget 开发(九)图表--仪表盘-CSDN博客
开源 C++ QT Widget 开发(十)IPC进程间通信--共享内存-CSDN博客
开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信-CSDN博客
开源 C++ QT Widget 开发(十二)图表--环境监测表盘-CSDN博客
推荐链接:
开源 java android app 开发(一)开发环境的搭建-CSDN博客
开源 java android app 开发(二)工程文件结构-CSDN博客
开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客
开源 java android app 开发(四)GUI界面重要组件-CSDN博客
开源 java android app 开发(五)文件和数据库存储-CSDN博客
开源 java android app 开发(六)多媒体使用-CSDN博客
开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客
开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客
开源 java android app 开发(九)后台之线程和服务-CSDN博客
开源 java android app 开发(十)广播机制-CSDN博客
开源 java android app 开发(十一)调试、发布-CSDN博客
开源 java android app 开发(十二)封库.aar-CSDN博客
推荐链接:
开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客
开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客
开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客
开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客
开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客
内容:Qt 有名管道 IPC 通信代码,本地套接字 (Local Socket)。这是在同一台机器上进行进程间通信的最常用、最推荐的方式之一。它基于 Windows 的命名管道和 Unix 的本地域套接字,提供了类似于网络 TCP 的流式通信接口。
目录:
1.功能介绍
2.核心代码分析
3.所有源码
4.显示效果
一、功能介绍
工作原理:一个服务器进程 (QLocalServer) 监听一个唯一的服务器名(如 "MyAwesomeServer")。客户端进程 (QLocalSocket) 通过该服务器名连接到服务器。之后,双方就可以像操作网络套接字一样进行读写。
适用场景:需要可靠、双向、基于流的通信。例如,一个主程序与它的插件进程、守护进程或辅助工具进行通信。
二、核心代码分析
- 服务器端代码分析
- 服务器启动机制
removeServer() 是必须的,避免"地址已在使用"错误
服务器名称 "MyLocalServer" 是通信的唯一标识
支持启动/停止切换功能
void MainWindow::on_startServer_clicked()
{
// 清理旧实例 - 防止资源泄漏
QLocalServer::removeServer(serverName); // 关键:清理可能存在的旧服务器
if (server->listen(serverName)) { // 监听指定名称的服务器
// 成功监听
}
}
- 连接管理机制
单客户端设计:每次只保持一个连接,新连接会替换旧连接
信号槽机制实现异步事件处理
自动内存管理:使用 deleteLater() 安全释放资源
void MainWindow::on_newConnection()
{
clientConnection = server->nextPendingConnection(); // 获取新连接
connect(clientConnection, &QLocalSocket::readyRead, this, &MainWindow::on_readyRead);
connect(clientConnection, &QLocalSocket::disconnected, this, &MainWindow::on_disconnected);
}
- 数据通信机制
使用 readAll() 而非 readLine(),更稳定但需要自定义协议
UTF-8 编码确保跨平台兼容性
简单的字符串协议,
void MainWindow::on_readyRead()
{
QByteArray data = clientConnection->readAll(); // 读取所有可用数据
QString message = QString::fromUtf8(data).trimmed();
}
正确的资源释放顺序,避免内存泄漏
MainWindow::~MainWindow()
{
if (server) {
server->close(); // 先关闭
delete server; // 再删除
}
// 类似的客户端连接清理
}
- 客户端代码分析
架构设计
class QLocalSocket; // 前向声明
QLocalSocket *socket; // 客户端套接字对象
核心机制分析
-
连接管理机制
void MainWindow::on_connectButton_clicked()
{
if (!socket) {
socket = new QLocalSocket(this); // 父对象管理生命周期
// 连接所有信号槽
}
socket->connectToServer(serverName); // 异步连接
}
技术要点:
连接/断开切换功能
异步连接操作,不阻塞UI线程
父对象管理内存,自动释放
-
错误处理机制
// 兼容旧版本的错误处理
connect(socket, SIGNAL(error(QLocalSocket::LocalSocketError)),
this, SLOT(on_errorOccurred(QLocalSocket::LocalSocketError)));void MainWindow::on_errorOccurred(QLocalSocket::LocalSocketError socketError)
{
Q_UNUSED(socketError); // 不使用错误代码,直接获取错误字符串
ui->textBrowser->append("Error: " + socket->errorString());
}
使用传统 SIGNAL/SLOT 语法确保版本兼容性
errorString() 提供人类可读的错误信息
Q_UNUSED 避免编译器警告
-
状态管理
void MainWindow::on_connected()
{
ui->connectButton->setText("Disconnect"); // UI状态更新
}void MainWindow::on_disconnected()
{
ui->connectButton->setText("Connect"); // UI状态恢复
} -
通信协议分析
当前实现
// 发送端
QByteArray data = message.toUtf8(); // 简单字符串编码
socket->write(data);
// 接收端
QByteArray data = socket->readAll(); // 读取所有数据
QString message = QString::fromUtf8(data).trimmed();
三、所有源码
服务器和客户端.pro文件都需要添加
QT += core gui network
- 服务器端
mianwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLocalServer>
#include <QLocalSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_startServer_clicked();
void on_sendMessage_clicked();
void on_newConnection();
void on_readyRead();
void on_disconnected();
private:
Ui::MainWindow *ui;
QLocalServer *server;
QLocalSocket *clientConnection;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, server(nullptr)
, clientConnection(nullptr)
{
ui->setupUi(this);
setWindowTitle("Server - Named Pipe Example");
}
MainWindow::~MainWindow()
{
if (server) {
server->close();
delete server;
}
if (clientConnection) {
clientConnection->close();
delete clientConnection;
}
delete ui;
}
void MainWindow::on_startServer_clicked()
{
if (server) {
server->close();
delete server;
server = nullptr;
}
server = new QLocalServer(this);
connect(server, &QLocalServer::newConnection, this, &MainWindow::on_newConnection);
QString serverName = "MyLocalServer";
if (server->listen(serverName)) {
ui->textBrowser->append("Server started successfully!");
ui->textBrowser->append("Listening on: " + serverName);
ui->startServer->setText("Stop Server");
} else {
ui->textBrowser->append("Failed to start server: " + server->errorString());
delete server;
server = nullptr;
}
}
void MainWindow::on_newConnection()
{
if (clientConnection) {
clientConnection->close();
delete clientConnection;
}
clientConnection = server->nextPendingConnection();
connect(clientConnection, &QLocalSocket::readyRead, this, &MainWindow::on_readyRead);
connect(clientConnection, &QLocalSocket::disconnected, this, &MainWindow::on_disconnected);
ui->textBrowser->append("New client connected!");
}
void MainWindow::on_readyRead()
{
if (clientConnection && clientConnection->canReadLine()) {
QByteArray data = clientConnection->readLine();
QString message = QString::fromUtf8(data).trimmed();
ui->textBrowser->append("Received: " + message);
}
}
void MainWindow::on_disconnected()
{
ui->textBrowser->append("Client disconnected");
if (clientConnection) {
clientConnection->deleteLater();
clientConnection = nullptr;
}
}
void MainWindow::on_sendMessage_clicked()
{
if (!clientConnection || clientConnection->state() != QLocalSocket::ConnectedState) {
QMessageBox::warning(this, "Warning", "No client connected!");
return;
}
QString message = ui->lineEdit->text();
if (message.isEmpty()) {
return;
}
QByteArray data = (message + "\n").toUtf8();
clientConnection->write(data);
clientConnection->flush();
ui->textBrowser->append("Sent: " + message);
ui->lineEdit->clear();
}
ui_mainwindow.h
/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created by: Qt User Interface Compiler version 5.14.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MainWindow
{
public:
QWidget *centralwidget;
QWidget *verticalLayoutWidget;
QVBoxLayout *verticalLayout;
QTextEdit *textBrowser;
QLineEdit *lineEdit;
QPushButton *sendMessage;
QPushButton *startServer;
QHBoxLayout *horizontalLayout;
QMenuBar *menubar;
QStatusBar *statusbar;
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(753, 365);
centralwidget = new QWidget(MainWindow);
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
verticalLayoutWidget = new QWidget(centralwidget);
verticalLayoutWidget->setObjectName(QString::fromUtf8("verticalLayoutWidget"));
verticalLayoutWidget->setGeometry(QRect(10, 1, 741, 301));
verticalLayout = new QVBoxLayout(verticalLayoutWidget);
verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
verticalLayout->setContentsMargins(0, 0, 0, 0);
textBrowser = new QTextEdit(verticalLayoutWidget);
textBrowser->setObjectName(QString::fromUtf8("textBrowser"));
verticalLayout->addWidget(textBrowser);
lineEdit = new QLineEdit(verticalLayoutWidget);
lineEdit->setObjectName(QString::fromUtf8("lineEdit"));
verticalLayout->addWidget(lineEdit);
sendMessage = new QPushButton(verticalLayoutWidget);
sendMessage->setObjectName(QString::fromUtf8("sendMessage"));
verticalLayout->addWidget(sendMessage);
startServer = new QPushButton(verticalLayoutWidget);
startServer->setObjectName(QString::fromUtf8("startServer"));
verticalLayout->addWidget(startServer);
horizontalLayout = new QHBoxLayout();
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
verticalLayout->addLayout(horizontalLayout);
MainWindow->setCentralWidget(centralwidget);
menubar = new QMenuBar(MainWindow);
menubar->setObjectName(QString::fromUtf8("menubar"));
menubar->setGeometry(QRect(0, 0, 753, 26));
MainWindow->setMenuBar(menubar);
statusbar = new QStatusBar(MainWindow);
statusbar->setObjectName(QString::fromUtf8("statusbar"));
MainWindow->setStatusBar(statusbar);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
sendMessage->setText(QCoreApplication::translate("MainWindow", "\345\217\221\351\200\201\346\266\210\346\201\257", nullptr));
startServer->setText(QCoreApplication::translate("MainWindow", "\345\220\257\345\212\250\346\234\215\345\212\241\345\231\250", nullptr));
} // retranslateUi
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_MAINWINDOW_H
- 客户端代码
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLocalSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_connectButton_clicked();
void on_sendButton_clicked();
void on_connected();
void on_readyRead();
void on_disconnected();
void on_errorOccurred(QLocalSocket::LocalSocketError socketError); // 修改参数
private:
Ui::MainWindow *ui;
QLocalSocket *socket;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, socket(nullptr)
{
ui->setupUi(this);
setWindowTitle("Client - Named Pipe Example");
}
MainWindow::~MainWindow()
{
if (socket) {
socket->close();
delete socket;
}
delete ui;
}
void MainWindow::on_connectButton_clicked()
{
if (socket && socket->state() == QLocalSocket::ConnectedState) {
socket->disconnectFromServer();
return;
}
if (!socket) {
socket = new QLocalSocket(this);
connect(socket, &QLocalSocket::connected, this, &MainWindow::on_connected);
connect(socket, &QLocalSocket::readyRead, this, &MainWindow::on_readyRead);
connect(socket, &QLocalSocket::disconnected, this, &MainWindow::on_disconnected);
// 兼容旧版本 Qt 的错误处理方式
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
connect(socket, &QLocalSocket::errorOccurred, this, &MainWindow::on_errorOccurred);
#else
connect(socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error),
this, &MainWindow::on_errorOccurred);
#endif
}
QString serverName = "MyLocalServer";
socket->connectToServer(serverName);
ui->textBrowser->append("Connecting to server...");
}
void MainWindow::on_connected()
{
ui->textBrowser->append("Connected to server successfully!");
ui->connectButton->setText("Disconnect");
}
void MainWindow::on_readyRead()
{
if (socket && socket->canReadLine()) {
QByteArray data = socket->readLine();
QString message = QString::fromUtf8(data).trimmed();
ui->textBrowser->append("Received: " + message);
}
}
void MainWindow::on_disconnected()
{
ui->textBrowser->append("Disconnected from server");
ui->connectButton->setText("Connect");
}
void MainWindow::on_errorOccurred(QLocalSocket::LocalSocketError socketError)
{
Q_UNUSED(socketError);
ui->textBrowser->append("Error: " + socket->errorString());
}
void MainWindow::on_sendButton_clicked()
{
if (!socket || socket->state() != QLocalSocket::ConnectedState) {
QMessageBox::warning(this, "Warning", "Not connected to server!");
return;
}
QString message = ui->lineEdit->text();
if (message.isEmpty()) {
return;
}
QByteArray data = (message + "\n").toUtf8();
socket->write(data);
socket->flush();
ui->textBrowser->append("Sent: " + message);
ui->lineEdit->clear();
}
ui_mianwindow.h
/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created by: Qt User Interface Compiler version 5.14.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QTextBrowser>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MainWindow
{
public:
QWidget *centralwidget;
QWidget *verticalLayoutWidget;
QVBoxLayout *verticalLayout;
QTextBrowser *textBrowser;
QLineEdit *lineEdit;
QPushButton *sendButton;
QPushButton *connectButton;
QMenuBar *menubar;
QStatusBar *statusbar;
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(581, 401);
centralwidget = new QWidget(MainWindow);
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
verticalLayoutWidget = new QWidget(centralwidget);
verticalLayoutWidget->setObjectName(QString::fromUtf8("verticalLayoutWidget"));
verticalLayoutWidget->setGeometry(QRect(10, 10, 561, 321));
verticalLayout = new QVBoxLayout(verticalLayoutWidget);
verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
verticalLayout->setContentsMargins(0, 0, 0, 0);
textBrowser = new QTextBrowser(verticalLayoutWidget);
textBrowser->setObjectName(QString::fromUtf8("textBrowser"));
verticalLayout->addWidget(textBrowser);
lineEdit = new QLineEdit(verticalLayoutWidget);
lineEdit->setObjectName(QString::fromUtf8("lineEdit"));
verticalLayout->addWidget(lineEdit);
sendButton = new QPushButton(verticalLayoutWidget);
sendButton->setObjectName(QString::fromUtf8("sendButton"));
verticalLayout->addWidget(sendButton);
connectButton = new QPushButton(verticalLayoutWidget);
connectButton->setObjectName(QString::fromUtf8("connectButton"));
verticalLayout->addWidget(connectButton);
MainWindow->setCentralWidget(centralwidget);
menubar = new QMenuBar(MainWindow);
menubar->setObjectName(QString::fromUtf8("menubar"));
menubar->setGeometry(QRect(0, 0, 581, 26));
MainWindow->setMenuBar(menubar);
statusbar = new QStatusBar(MainWindow);
statusbar->setObjectName(QString::fromUtf8("statusbar"));
MainWindow->setStatusBar(statusbar);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
sendButton->setText(QCoreApplication::translate("MainWindow", "\345\217\221\351\200\201\346\266\210\346\201\257", nullptr));
connectButton->setText(QCoreApplication::translate("MainWindow", "\350\277\236\346\216\245", nullptr));
} // retranslateUi
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_MAINWINDOW_H
四、显示效果
