开源 C++ QT Widget 开发(十三)IPC通讯--本地套接字 (Local Socket)

发布于:2025-09-08 ⋅ 阅读:(23) ⋅ 点赞:(0)

   文章的目的为了记录使用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) 通过该服务器名连接到服务器。之后,双方就可以像操作网络套接字一样进行读写。

适用场景:需要可靠、双向、基于流的通信。例如,一个主程序与它的插件进程、守护进程或辅助工具进行通信。

二、核心代码分析

1) 服务器端代码分析
1. 服务器启动机制

removeServer() 是必须的,避免"地址已在使用"错误

服务器名称 "MyLocalServer" 是通信的唯一标识

支持启动/停止切换功能

void MainWindow::on_startServer_clicked()
{
    // 清理旧实例 - 防止资源泄漏
    QLocalServer::removeServer(serverName); // 关键:清理可能存在的旧服务器
    
    if (server->listen(serverName)) { // 监听指定名称的服务器
        // 成功监听
    }
}

2. 连接管理机制

单客户端设计:每次只保持一个连接,新连接会替换旧连接

信号槽机制实现异步事件处理

自动内存管理:使用 deleteLater() 安全释放资源

void MainWindow::on_newConnection()
{
    clientConnection = server->nextPendingConnection(); // 获取新连接
    connect(clientConnection, &QLocalSocket::readyRead, this, &MainWindow::on_readyRead);
    connect(clientConnection, &QLocalSocket::disconnected, this, &MainWindow::on_disconnected);
}


 

3. 数据通信机制

使用 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;      // 再删除
    }
    // 类似的客户端连接清理
}


 

2) 客户端代码分析
架构设计

class QLocalSocket; // 前向声明

QLocalSocket *socket; // 客户端套接字对象


核心机制分析
1. 连接管理机制
 

void MainWindow::on_connectButton_clicked()
{
    if (!socket) {
        socket = new QLocalSocket(this); // 父对象管理生命周期
        // 连接所有信号槽
    }
    socket->connectToServer(serverName); // 异步连接
}


技术要点:

连接/断开切换功能

异步连接操作,不阻塞UI线程

父对象管理内存,自动释放

2. 错误处理机制

// 兼容旧版本的错误处理
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 避免编译器警告

3. 状态管理
 

void MainWindow::on_connected()
{
    ui->connectButton->setText("Disconnect"); // UI状态更新
}

void MainWindow::on_disconnected()
{
    ui->connectButton->setText("Connect"); // UI状态恢复
}

3. 通信协议分析
当前实现

// 发送端
QByteArray data = message.toUtf8(); // 简单字符串编码
socket->write(data);

// 接收端  
QByteArray data = socket->readAll(); // 读取所有数据
QString message = QString::fromUtf8(data).trimmed();

三、所有源码

服务器和客户端.pro文件都需要添加

QT += core gui network

1.  服务器端

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

2.  客户端代码

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

四、显示效果


网站公告

今日签到

点亮在社区的每一天
去签到