news 2026/4/18 11:01:25

Qt多进程(三)QLocalSocket

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt多进程(三)QLocalSocket

前言

本节将学习第二种IPC方式LocalSocket,它基于CS架构,建立类似于TCP方式的本地连接,实现全双工字节流的持续读写交互。如果我们期望实现本地/本机的两个进程间的持续通信,我们可以使用LocalSocket。当然TCP也是可以的,但它会产生更多的网络开销,效率也不如LocalSocket。TCP更推荐的开始跨机器的局域网连接,本机还是使用LocalSocket更加推荐。

一、LocalSocket

在Qt中,提供QLocalServer和QLocalSocket两个核心类,用以实现本地的连接通信。他们的使用和QTcpServer和QClient几乎完全一样,也就是说Qt官方故意将两者封装成类似的接口,目的是降低学习成本。
但注意,它的底层实现和tcp有本质区别!
在Windows环境下,它的底层协议基于命名管道实现,而unix下则是Unix Domain Socket。它不走网络协议,因此无网络花销,通过内核直接传递数据,建立连接和传输数据的效率都要更快。而Tcp需要走TCP/IP协议栈,需要经历一系列校验和、缓冲、拥塞控制等环节。它使用时也无需像Tcp那样指定ip和端口,只需要指定特定识别的字符串名称,且无需建立所谓三次握手的连接校验。
总的来说,如果你只在本机通信,优先用 QLocalSocket;如果可能跨机器,才用 QTcpSocket。
另外,你可能有疑问,既然LocalSocket不是基于TCP/IP实现的,那它还算是socket通信的一种吗?
的确,如果从狭义理解出发,QLocalSocket不走网络协议栈,因此不属于网络socket。
但是,从广义理解考虑,socket是一种通用的进程间通信抽象概念,它包括:
1.网络 socket:AF_INET(IPv4)、AF_INET6(IPv6)
2.本地 socket:AF_UNIX(Unix Domain Socket)或 Windows 的 Named Pipe(命名管道)
在这个意义上,QLocalServer 就是基于“本地 socket”实现的,因此属于 socket 通信的一种

二、代码示例

和之前一样,我们为QLocalSocket方式设计单独的测试窗口:

#ifndefLOCALSOCKETWINDOW_H#defineLOCALSOCKETWINDOW_H#include<QWidget>#include<QTextEdit>#include<QLineEdit>#include<QPushButton>#include<QVBoxLayout>#include<QHBoxLayout>#include<QLocalServer>#include<QLocalSocket>#include<QProcess>#include<QDateTime>classLocalSocketWindow:publicQWidget{Q_OBJECTpublic:explicitLocalSocketWindow(constQString&role,QWidget*parent=nullptr);privateslots:voidonSendMessage();voidonAcceptConnection();voidonReadyRead();voidonDisconnected();voidonConnectClicked();private:voidappendLog(constQString&msg);voidsetupServer();voidsetupClient();QString m_role;QTextEdit*m_logView;QLineEdit*m_inputEdit;QPushButton*m_sendButton;QPushButton*m_connectButton;QLocalServer*m_server;QLocalSocket*m_socket;QString m_serverName;};#endif// LOCALSOCKETWINDOW_H
#include"localsocketwindow.h"#include<QLabel>LocalSocketWindow::LocalSocketWindow(constQString&role,QWidget*parent):QWidget(parent),m_role(role),m_server(nullptr),m_socket(nullptr),m_serverName("ipc_test_local"){setWindowTitle("Local Socket - "+role);resize(600,500);m_logView=newQTextEdit();m_logView->setReadOnly(true);m_inputEdit=newQLineEdit();m_inputEdit->setPlaceholderText("Enter message to send...");m_sendButton=newQPushButton("Send Message");m_sendButton->setEnabled(false);m_connectButton=newQPushButton("Connect");QVBoxLayout*mainLayout=newQVBoxLayout();mainLayout->addWidget(m_logView);mainLayout->addWidget(m_inputEdit);mainLayout->addWidget(m_sendButton);mainLayout->addWidget(m_connectButton);setLayout(mainLayout);connect(m_sendButton,&QPushButton::clicked,this,&LocalSocketWindow::onSendMessage);connect(m_connectButton,&QPushButton::clicked,this,&LocalSocketWindow::onConnectClicked);if(m_role=="Server"){setupServer();m_connectButton->hide();}else{setupClient();}appendLog("Local Socket "+m_role+" initialized with server name: "+m_serverName);}voidLocalSocketWindow::setupServer(){QLocalServer::removeServer(m_serverName);// Clean up any previous instancem_server=newQLocalServer(this);if(!m_server->listen(m_serverName)){appendLog("Failed to start server: "+m_server->errorString());return;}connect(m_server,&QLocalServer::newConnection,this,&LocalSocketWindow::onAcceptConnection);appendLog("Server listening on "+m_serverName);}voidLocalSocketWindow::setupClient(){m_socket=newQLocalSocket(this);connect(m_socket,&QLocalSocket::connected,this,[this](){appendLog("Connected to server!");m_sendButton->setEnabled(true);m_inputEdit->setPlaceholderText("Enter message to send to server...");});connect(m_socket,&QLocalSocket::readyRead,this,&LocalSocketWindow::onReadyRead);connect(m_socket,&QLocalSocket::disconnected,this,&LocalSocketWindow::onDisconnected);}voidLocalSocketWindow::onAcceptConnection(){if(m_socket){// m_socket->disconnectFromHost();m_socket->disconnectFromServer();m_socket->deleteLater();}m_socket=m_server->nextPendingConnection();if(!m_socket)return;connect(m_socket,&QLocalSocket::readyRead,this,&LocalSocketWindow::onReadyRead);connect(m_socket,&QLocalSocket::disconnected,this,&LocalSocketWindow::onDisconnected);appendLog("Client connected!");m_sendButton->setEnabled(true);m_inputEdit->setPlaceholderText("Enter message to send to client...");}voidLocalSocketWindow::onReadyRead(){if(!m_socket)return;QByteArray data=m_socket->readAll();appendLog("Received: "+QString::fromUtf8(data));}voidLocalSocketWindow::onDisconnected(){appendLog("Disconnected from peer");m_sendButton->setEnabled(false);}voidLocalSocketWindow::onSendMessage(){if(!m_socket||m_socket->state()!=QLocalSocket::ConnectedState){appendLog("Not connected!");return;}QString msg=m_inputEdit->text();if(msg.isEmpty())return;m_socket->write(msg.toUtf8());m_socket->flush();appendLog("Sent: "+msg);m_inputEdit->clear();}voidLocalSocketWindow::onConnectClicked(){if(!m_socket)return;m_socket->connectToServer(m_serverName);appendLog("Attempting to connect to "+m_serverName);}voidLocalSocketWindow::appendLog(constQString&msg){m_logView->append(QDateTime::currentDateTime().toString("hh:mm:ss")+" | "+msg);}

代码看似冗长,实则非常简单和简洁。它和tcp一样,分为服务端和客户端。
先说服务端,创建好对象后,我们只需要指定字符串名字进行监听:

接收到连接之后,就可以保存该客户端的socket对象,之后在sendMessage中往里面写入数据:

而客户端的话,先创建socket对象,然后进行服务器的连接:

连接成功,且服务端对它发送消息后,触发槽函数。

这一系列的步骤,几乎和Qt中的tcp连接一模一样。
直接贴上运行效果图:

三、总结

进程间通信大致分为本机和跨机器两种使用场景,在本机的进程间交互中,如果双方程序都是用Qt编写的,那么使用LocalSocket无疑是更好的选择。而跨机器则应该考虑Tcp的方式。
但事实上,跨机器的通信方式有很多,只要是走网络协议的都可以,但如果扩展到那个层面上,需要学习的就太多了,而且从概念上更偏向于网络通信,而不是进程间通信了。
所以聚焦到本机的进程间通信上,我认为Tcp和Udp还是有使用价值的,下一节将学习记录一下。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 8:20:13

5个简单步骤掌握Blender 3MF插件:3D打印工作流完整指南

5个简单步骤掌握Blender 3MF插件&#xff1a;3D打印工作流完整指南 【免费下载链接】Blender3mfFormat Blender add-on to import/export 3MF files 项目地址: https://gitcode.com/gh_mirrors/bl/Blender3mfFormat Blender作为业界领先的开源3D建模软件&#xff0c;通过…

作者头像 李华
网站建设 2026/4/18 8:37:45

智慧树学习效率革命:从手动操作到智能自动化的蜕变之路

智慧树学习效率革命&#xff1a;从手动操作到智能自动化的蜕变之路 【免费下载链接】zhihuishu 智慧树刷课插件&#xff0c;自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 你是否曾经计算过&#xff0c;在智慧树平台学习一门…

作者头像 李华
网站建设 2026/4/18 8:38:04

智慧树自动学习插件:3步完成高效刷课配置

智慧树自动学习插件&#xff1a;3步完成高效刷课配置 【免费下载链接】zhihuishu 智慧树刷课插件&#xff0c;自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 还在为智慧树网课的手动操作而烦恼吗&#xff1f;每次视频播放结束…

作者头像 李华
网站建设 2026/4/18 10:06:01

Open-AutoGLM开源发布:为何它将成为AI开发者的新宠?

第一章&#xff1a;Open-AutoGLM开源发布&#xff1a;为何它将成为AI开发者的新宠&#xff1f;近日&#xff0c;由深度学习社区主导开发的 Open-AutoGLM 正式开源&#xff0c;迅速在 AI 开发者圈层中引发广泛关注。该项目旨在提供一个轻量、高效且可扩展的通用语言生成框架&…

作者头像 李华
网站建设 2026/4/18 10:18:43

碧蓝航线效率工具深度使用指南

碧蓝航线效率工具深度使用指南 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研&#xff0c;全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript 玩家面临的效率困境 碧蓝航线作为一款内容丰…

作者头像 李华
网站建设 2026/4/17 13:49:32

教你使用服务器搭建一个基于 Cloudflare Workers 的真实地址生成器—— Real-Address-Generator

平时做开发、测试、表单调试或者写自动化脚本时,你大概率遇到过这个需求: 需要“看起来真实”的地址数据,但又不能用真实隐私信息。 一开始我也是随手乱填,后来发现问题不少: 地址格式不规范,校验直接报错 国家/城市/邮编对不上 API 或第三方服务一眼就能识别“假数据…

作者头像 李华