跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

Qt 网络编程实战:QUdpSocket、QTcpSocket 与 HTTP 客户端

Qt 网络编程基于 Socket API,涵盖 UDP、TCP 及 HTTP 协议实现。文章介绍了 QUdpSocket 与 QNetworkDatagram 的使用,包括绑定端口、信号槽处理 readyRead 事件;QTcpServer 与 QTcpSocket 构建 TCP 服务端与客户端,处理连接建立、数据收发及断开;QNetworkAccessManager 封装 HTTP 请求。通过示例代码演示了回显服务器与客户端的开发流程,强调了 .pro 文件添加 network 模块、先连接信号槽再绑定端口、以及内存管理(deleteLater)等关键实践点。

落日余晖发布于 2026/3/29更新于 2026/9/882 浏览
Qt 网络编程实战:QUdpSocket、QTcpSocket 与 HTTP 客户端

Qt 网络编程

网络编程主要依赖于操作系统提供的 Socket API。需要注意的是,C++标准库本身并未封装网络编程相关的 API。

关于 Qt 网络编程的要点

  1. 网络应用开发本质上是编写应用层代码,需要传输层协议(如 TCP/UDP)的支持
  2. Qt 提供了两套专门的网络编程 API(QUdpSocket 和 QTcpSocket)
  3. 使用 Qt 网络编程 API 时,需先在.pro 文件中添加 network 模块
  4. 之前学习的 Qt 控件和核心功能都属于 QtCore 模块(默认已包含)

Qt 模块化处理是为了避免生成过大的可执行程序。默认情况下额外模块不会参与编译,需要在 .pro 文件中引入对应的模块。

1. QUdpSocket

核心 API 概览

主要的类有两个:

  1. QUdpSocket(同 Linux 理解的 socket 概念,它本质是打开的一个文件描述符,通过这个文件进行通信)
  2. QNetworkDatagram(是 Qt 对 UDP 数据报的完整封装,不仅包含二进制数据,还包含了发送/接收的地址端口等元信息)

QUdpSocket:

名称类型说明对标原生 API
bind(const QHostAddress&, quint16)方法绑定指定的端口号bind
receiveDatagram()方法返回 QNetworkDatagram 读取一个 UDP 数据报recvfrom
writeDatagram(const QNetworkDatagram&)方法发送一个 UDP 数据报sendto
readyRead信号在收到数据并准备就绪后触发无 (类似于 IO 多路复用的通知机制)

QNetworkDatagram:

名称类型说明对标原生 API
QNetworkDatagram(const QByteArray&, const QHostAddress& , quint16 )构造函数通过 QByteArray , 目标 IP 地址,目标端口号 构造一个 UDP 数据报无
data()方法获取数据报内部持有的数据无
senderAddress()方法获取数据报中包含的对端的 IP 地址无
senderPort()方法获取数据报中包含的对端的端口号无

实操 Demo

UDP 回显服务器

实现一个带有界面的 UDP 回显服务器。

  1. 创建 Widget 项目
  2. 创建一个 QListWidget 表示通过内部的 item 表示若干条消息
  3. 创建 QUdpSocket 成员变量 socket 句柄
  4. 构造函数中
    1. 初始化 socket 对象
    2. 设置窗口标题
    3. 连接信号槽 接收来自 socket 的 QUdpSocket 中的 readyRead 信号,触发槽函数
  5. 进行绑定端口号(一定是先连接信号槽,再绑定端口)
    1. socket 成员函数 bind
    2. 第一个参数 ip(任意 ip 也就是 QHostAddress::Any 宏)
    3. 第二个参数 端口号
    4. 一个端口号只能被一个 socket 绑定,所以可能失败,此时就需要进行防御性编程
    5. 通过 bind 的 bool 类型的返回值知道是否成功
    6. 若失败则时可以调用 QMessageBox 对话框提示
      1. 可以使用 critical(严重)方法
      2. 标题可以提示:服务器启动错误
      3. 文本可以填写:socket 的成员函数 errorString 显示错误
      4. 直接 return 结束
  6. 槽函数(processRequest):
    1. 读取请求并解析
      1. 通过 socket 对象的 receiveDatagram 获取请求
      2. 再通过数据报进行类型 QNetworkDatagram 进行接收
      3. 再将该对象调用 data 函数变成 QString 类型方便使用
    2. 根据请求计算响应(因为是回显服务器,就是请求本身)
    3. 把响应写回给客户端
      1. 将 process 处理完后返回回来的值(response)调用 toUtf8 函数
      2. 在设置返回的 ip 地址和端口,它包含在 QNetworkDatagram 内部函数 senderAddress、senderPort 中
    4. 最后通过 socket 对象调用 writeDatagram
    5. 把这次交互的信息显示在界面上

注意在 .pro 加入 network。

主要源码如下:

//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
#include <QHostAddress>
#include <QMessageBox>
#include <QNetworkDatagram>
#include <QDateTime>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    QUdpSocket* socket;
    QString process2(const QString& s);

private slots:
    void process();
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    socket = new QUdpSocket(parent);
    ui->listWidget->setObjectName(QString("UDP 服务器"));
    connect(socket, &QUdpSocket::readyRead, this, &Widget::process);
    // 注意一定要先连接槽函数再绑定端口号
    bool isconnect = socket->bind(QHostAddress::Any, 8081);
    if(!isconnect){
        QMessageBox::critical(this, "错误", "服务器启动错误");
        return;
    }
    setWindowTitle("Udp 服务端");
}

Widget::~Widget()
{
    delete ui;
}

QString Widget::process2(const QString& request)
{
    return request;
}

void Widget::process()
{
    // 1. 读取请求并解析
    QNetworkDatagram requestPacket = socket->receiveDatagram();
    QString request = requestPacket.data();
    QString response = process2(request);
    
    QNetworkDatagram responsePacket(response.toUtf8(), requestPacket.senderAddress(), requestPacket.senderPort());
    socket->writeDatagram(responsePacket);
    
    qint64 msTimestamp = QDateTime::currentMSecsSinceEpoch();
    QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(msTimestamp);
    QString formatted = dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
    QString log = "[" + formatted + " : " + requestPacket.senderAddress().toString() + ":" + QString::number(requestPacket.senderPort()) + "] req: " + request + ", resp: " + response;
    ui->listWidget->addItem(log);
}
UDP 回显客户端
  1. 打开 ui 文件拖拽(要保证可以主动给服务器发起请求)
  2. 创建 QUdpSocket 类型的成员变量 socket
  3. 构造函数
    1. 添加两个全局变量 SERVER_IP "127.0.0.1"(QString)
    2. SERVER_PORT 8081(quint16)
    3. 初始化 socket 对象挂在对象树上
    4. 修改窗口标题 setWindowTitle
  4. 给按钮添加点击信号的槽函数
    1. 获取输入框的内容
    2. 构造 UDP 的请求数据包,创建 QNetworkDatagram 对象
    3. 发送请求数据,调用 writeDatagram
    4. 把发送的请求也添加到列表框中
    5. 最后把输入框中的内容清空 clear

同样对于客户端也要处理下服务器返回回来的响应

  1. 通过信号槽,来处理服务器的数据
    1. 同样是通过 readyRead 信号,触发信号槽
  2. 信号槽 processResponse
    1. 读取响应数据 receiveDatagram
    2. 再通过 data 函数将数据报转换成 QString
    3. 把响应数据显示到界面

主要源码如下:

//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();
    void processResponse();

private:
    Ui::Widget *ui;
    QUdpSocket *socket;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"

QString SERVER_IP = "127.0.0.1";
quint16 SERVER_PORT = 8081;

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    socket = new QUdpSocket(parent);
    setWindowTitle("Udp 客户端");
    // 接收处理服务器发送回来的数据
    connect(socket, &QUdpSocket::readyRead, this, &Widget::processResponse);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    QString context = ui->lineEdit->text();
    QNetworkDatagram request(context.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);
    socket->writeDatagram(request);
    ui->listWidget->addItem("客户端说:" + context);
    ui->lineEdit->clear();
}

void Widget::processResponse()
{
    const QNetworkDatagram& packet = socket->receiveDatagram();
    QString s = QString::fromUtf8(packet.data());
    ui->listWidget->addItem("服务器说" + s);
}

Qt UDP 总结

  1. QNetworkDatagram 数据报类:作为发送和接收的数据报类
  2. QUdpSocket 文件类:完成对消息的读写操作
  3. &QUdpSocket::readyRead 消息准备就绪的信号:注意一定要先连接槽函数再绑定端口号
  4. .pro 中要添加 network

2. TcpSocket

核心 API 概览,核心类是两个:QTcpServer 和 QTcpSocket。

QTcpServer 用于监听端口获取客户端连接:

名称类型说明对标原生 API
listen(const QHostAddress&, quint16 port)方法绑定指定的地址和端口号,并开始监听bind 和 listen
nextPendingConnection()方法从系统中获取到一个已经建立好的 tcp 连接accept
newConnection信号有新的客户端建立连接好之后触发无

QTcpSocket 用于客户端和服务器之间的数据交互:

名称类型说明对标原生 API
readAll()方法读取当前接收缓冲区中的所有数据read
write(const QByteArray& )方法把数据写入 socket 中write
deleteLater方法暂时把 socket 对象标记为无效无
peerAddress方法获取对端客户端的 ip无
peerPort方法获取对端客户端 port无
readyRead信号有数据到达并准备就绪时触发无
disconnected信号连接断开时触发无

实操 Demo

Tcp 回显服务器
  1. 创建 Widget 项目(.pro 中添加 network)
  2. ui 文件中拖拽一个 ListWidget
  3. 添加 QTcpServer 类成员变量句柄
  4. 构造函数中:
    1. 修改窗口标题 服务器
    2. 创建 QTcpServer 的实例,并挂在对象树上
    3. 创建 newConnection 信号的信号槽 processConnection
      1. 通过 nextPendingConnection 获取一个客户端的 socket
      2. 处理客户端发来请求的情况,通过 readyRead 信号
      3. 处理客户端断开连接的情况,通过 disconnected 信号
        1. 手动释放 ClientSocket,建议使用 deleteLater
    4. 绑定并监听端口号
      1. 一定要确保先写好如何处理连接,处理请求再绑定监听
      2. listen(监听接收任意 ip,端口)
      3. 可能失败所以接收 listen 返回值进行判断

实际使用 TCP 的过程中,TCP 是面向字节流的,存在粘包问题。更严谨的做法是约定应用层协议格式。

主要源码:

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowTitle("服务器");
    server = new QTcpServer(parent);
    connect(server, &QTcpServer::newConnection, this, &Widget::processConnection);
    // 注意监听前一定要先把上述内容先绑定了!
    bool ret = server->listen(QHostAddress::Any, 8080);
    if(!ret){
        QMessageBox::critical(this, "服务器启动失败", server->errorString());
        return;
    }
}

Widget::~Widget()
{
    delete ui;
}

QString Widget::process(QString& request)
{
    return request;
}

void Widget::processConnection()
{
    QTcpSocket *clientSocket = server->nextPendingConnection();
    QHostAddress add = clientSocket->peerAddress();
    quint16 port = clientSocket->peerPort();
    ui->listWidget->addItem("[" + add.toString() + ":" + QString::number(port) + "]: 客户端已上线");

    // 处理客户端发来的请求
    connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
        QString request = clientSocket->readAll();
        const QString& response = process(request);
        clientSocket->write(response.toUtf8());
        QString log = "[" + add.toString() + ":" + QString::number(port) + "] req:" + request + ",res:" + response;
        ui->listWidget->addItem(log);
    });

    // 处理客户端断开
    connect(clientSocket, &QTcpSocket::disconnected, this, [=](){
        QString log = "[" + add.toString() + ":" + QString::number(port) + "] 已下线";
        qDebug() << log;
        ui->listWidget->addItem(log);
        clientSocket->deleteLater();
    });
}
Tcp 客户端的实现
  1. 创建新的 Widget 项目
  2. 创建 QTcpSocket 对象 (socket)
  3. 构造函数中:
    1. 设置窗口
    2. 创建 socket 的实例(并指定父对象)
    3. 和服务器建立连接 connectToHost(服务器 ip、端口)
    4. 通过 readyRead 信号的信号槽,处理响应
    5. 等待连接建立的结果,确定是否连接成功 waitForConnected
  4. 给按钮添加点击的槽函数:
    1. 获取输入框内容
    2. 发送输入框内容给服务器注意转成 QByteArray
    3. 把消息显示到界面上
    4. 清空输入框内容

主要源码:

//widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    socket = new QTcpSocket(parent);
    socket->connectToHost("127.0.0.1", 8080);
    // 处理返回的消息
    connect(socket, &QTcpSocket::readyRead, this, [=](){
        QString response = socket->readAll();
        ui->listWidget->addItem("服务端说:" + response);
    });
    // 处理连接失败
    if(!socket->waitForConnected()){ 
        QMessageBox::critical(this, "client 连接服务器失败", socket->errorString());
        exit(0);
    }
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    QString request = ui->lineEdit->text();
    socket->write(request.toUtf8());
    ui->listWidget->addItem("客户端说:" + request);
    ui->lineEdit->clear();
}

Qt Tcp 总结

  1. 服务器中 QTcpServer::newConnection 处理新链接的信号
  2. nextPendingConnection() 从就绪的连接中获取一个
  3. QTcpSocket 常用的函数:peerAddress, peerPort, readAll, write, connectToHost
  4. QTcpSocket::readyRead,同前面客户端消息就绪的信号
  5. QTcpSocket::disconnected 用户断开时会触发的信号

3. HTTP Client

Qt 中也是提供了 Http 的客户端,Http 协议本质上也就是基于 TCP 协议实现的。

核心 API

关键类主要是三个:

  1. QNetworkAccessManager
  2. QNetworkRequest
  3. QNetworkReply

QNetworkAccessManager 提供了 HTTP 的核心操作

方法说明
get(const QNetworkRequest& )发起一个 HTTP GET 请求
post(const QNetworkRequest& , const QByteArray& )发起一个 HTTP POST 请求

QNetworkRequest 表示一个 HTTP 请求

方法说明
QNetworkRequest(const QUrl& )通过 URL 构造一个 HTTP 请求
setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)设置请求头

QNetworkRequest::KnownHeaders 常用取值

取值说明
ContentTypeHeader描述 body 的类型
ContentLengthHeader描述 body 的长度
CookieHeader设置 cookie
UserAgentHeader设置 User-Agent

QNetworkReply 表示一个 HTTP 响应

方法说明
error()获取出错状态
errorString()获取出错原因的文本
readAll()读取响应 body
header(QNetworkRequest::KnownHeaders header)读取响应指定 header 的值

实操 Demo Http 客户端

  1. 同样的.pro中添加 network 标识
  2. 添加 QNetworkAccessManger 成员变量(client)
  3. 构造函数:实例化添加的成员变量,指定父元素
  4. 给按钮添加点击的槽函数:
    1. 获取输入框中的 url
    2. 通过 client 调用 get 方法并传递 url
    3. 构造一个 HTTP 请求对象 request
    4. 通过 response 的 finished 信号 的槽函数进行处理
      1. 判断 response 的 error 若等于 NoError 则代表响应正确获取
      2. 通过 readAll 读取 response 的 html 数据
      3. 若失败,则将错误信息设置到 PlainText 中
      4. 最后同样调用 response 的 deleteLater

源码:

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    client = new QNetworkAccessManager(parent);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    QUrl url = ui->lineEdit->text();
    QNetworkRequest request(url);
    QNetworkReply *response = client->get(request);
    
    connect(response, &QNetworkReply::finished, this, [=](){
        if(response->error() == QNetworkReply::NoError){
            QString req = response->readAll();
            ui->plainTextEdit->setPlainText(req);
        } else {
            ui->plainTextEdit->setPlainText(response->errorString());
        }
        response->deleteLater();
    });
}

Qt Http 总结

  1. QNetworkRequest 构造一个 http 请求,初始化填入 url
  2. QNetworkAccessManager 中的 get/post 函数进行发送请求
  3. 响应处不是阻塞的,需要设置一个信号槽,信号是 QNetworkReply::finished
  4. 当 QNetworkReply 的 error 函数和 QNetworkReply::NoError 相等就代表没有问题

目录

  1. Qt 网络编程
  2. 关于 Qt 网络编程的要点
  3. 1. QUdpSocket
  4. 核心 API 概览
  5. 实操 Demo
  6. UDP 回显服务器
  7. UDP 回显客户端
  8. Qt UDP 总结
  9. 2. TcpSocket
  10. 实操 Demo
  11. Tcp 回显服务器
  12. Tcp 客户端的实现
  13. Qt Tcp 总结
  14. 3. HTTP Client
  15. 核心 API
  16. 实操 Demo Http 客户端
  17. Qt Http 总结

更多推荐文章

查看全部
  • 2024 年 GitHub 谷歌学术与 Sci-Hub 镜像站使用指南
  • Web 聊天室消息加解密方案详解
  • 网络安全自学指南:从入门到就业的路径规划
  • C++ 递归实战:合并有序链表与反转链表
  • Git 连接 GitHub 失败:Failed to connect to github.com port 443
  • Android 设备免 Root 安装霞鹜文楷屏幕阅读版字体
  • Linux initramfs 原理与实现:从内核启动到根文件系统
  • 程序员为何要尽早掌握基础知识与设计模式
  • Python 兼职实战指南:从爬虫开发到数据分析变现路径
  • Neo4j Desktop 2.0 安装及自定义路径配置教程
  • Photoshop 中集成 Stable Diffusion 的 5 个核心技巧
  • 字节跳动音视频前端一面面经与性能优化深度解析
  • Spring Cloud 微服务架构概述与工程搭建
  • 千卡 GPU 利用率超 98%:JuiceFS 在 MLPerf 存储基准测试中的实践
  • Microi 吾码开源低代码平台技术架构与功能解析
  • C++ 进阶:哈希、位图与布隆过滤器的海量数据处理
  • Llama-3.2-3B 部署优化:Ollama 上下文窗口与 Token 限制配置
  • ms-swift 大模型工程实践与 GitHub 项目管理
  • 66 个 Java 常见代码示例:从基础语法到并发编程
  • Coze 工作流实战:逻辑控制、数据处理与 AIGC 多媒体应用

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online