【Qt】网络

目录

一、Udp Socket

二、Tcp Socket

三、HTTP


在进行网络编程之前,需要在项目中的 .pro 文件中添加 network 模块
有时添加之后要手动编译一下项目,使 Qt Creator 能够加载对应模块的头文件

一、Udp Socket

主要的类有两个:QUdpSocket 和 QNetworkDatagram

QUdpSocket 表示一个 UDP 的 socket 文件

QNetworkDatagram 表示一个 UDP 数据报

代码示例:回显服务器

创建界面,包含一个 QListWidget 用来显示消息

widget.hpp

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include <QMessageBox>
#include <QNetworkDatagram>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void processRequest();
    QString process(const QString& request);

private:
    Ui::Widget *ui;
    QUdpSocket* socket;
};
#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);

    setWindowTitle("服务器");
    //实例化socket
    socket = new QUdpSocket(this);
    //连接信号槽
    connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);
    //绑定端口
    bool ret = socket->bind(QHostAddress::Any, 9090);
    if(!ret) {
        QMessageBox::critical(nullptr, "服务器启动出错", socket->errorString());
        return;
    }

}

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

void Widget::processRequest()
{
    //读取请求
    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
    QString request = requestDatagram.data();
    //根据请求计算响应
    const QString& response = process(request);
    //将响应写回客户端
    QNetworkDatagram responseDatagram(response.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());
    socket->writeDatagram(responseDatagram);
    //打印日志
    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + \
            QString::number(requestDatagram.senderPort()) + "] request:" + \
            request + ", response:" + response;
    ui->listWidget->addItem(log);
}

//本代码实现的是回显服务器,所以process方法中不包含实质性的业务代码
QString Widget::process(const QString &request) {
    return request;
}

代码示例:回显客户端

创建界面,包含一个 QLineEdit、QPushButton、QListWidget

widget.hpp

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include <QMessageBox>
#include <QNetworkDatagram>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    
    void sendRequest();
    void recvResponse();

private:
    Ui::Widget *ui;
    QUdpSocket* socket;
};
#endif // WIDGET_H

widget.cpp

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

const QString SERVER_IP = "127.0.0.1";
const qint16 SERVER_PORT = 9090;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowTitle("客户端");
    //实例化socket
    socket = new QUdpSocket(this);
    //连接按钮的信号槽
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::sendRequest);
    //连接socket的信号槽
    connect(socket, &QUdpSocket::readyRead, this, &Widget::recvResponse);
}

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

void Widget::sendRequest()
{
    //获取输入框的内容
    const QString& text = ui->lineEdit->text();
    //构造请求数据
    QNetworkDatagram requestDatagram(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);
    //发送请求
    socket->writeDatagram(requestDatagram);
    //消息添加到列表框中
    ui->listWidget->addItem("客户端:" + text);
    //清空输入框
    ui->lineEdit->setText("");
}

void Widget::recvResponse()
{
    //读取请求
    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
    QString response = responseDatagram.data();
    //消息添加入列表
    ui->listWidget->addItem(QString("服务器:") + response);
}

二、Tcp Socket

核心类是两个:QTcpServer 和 QTcpSocket

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

名称类型说明对标原生API
listen(const QHostAddress&, quint16 port)方法绑定指定的地址和端口号并开始监听bind和listen
nextPendingConnection()方法

从系统中获取一个已经建立好的tcp连接

返回一个TcpSocket,表示这个连接

通过这个socket对象完成与客户端之间的通信

accept
newConnection信号有新的客户端建立好连接后触发无(类似与IO多路复用中的通知机制)

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

QByteArray 用于表示一个字节数组,可以很方便的和 QString 进行相互转换

  • 使用 QString 的构造函数即可把 QByteArray 转成 QString
  • 使用 QString 的 toUtf8 函数即可把 QString 转成 QByteArray

代码示例:回显服务器

创建界面,包含一个 QListWidget,用于显示收到的数据

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMessageBox>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void processConnection();
    QString process(const QString&);

private:
    Ui::Widget *ui;
    QTcpServer* tcpServer;
};
#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);
    setWindowTitle("服务端");
    //实例化tcpServer
    tcpServer = new QTcpServer(this);
    //信号槽处理新连接
    connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);
    //监听端口
    bool ret = tcpServer->listen(QHostAddress::Any, 9090);
    if(!ret) {
        QMessageBox::critical(nullptr, "服务器启动出错", tcpServer->errorString());
        return;
    }
}

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

void Widget::processConnection()
{
    //获取新连接对应的socket
    QTcpSocket* socket = tcpServer->nextPendingConnection();
    //打印日志
    QString log = "[" + socket->peerAddress().toString() + ":" + \
            QString::number(socket->peerPort()) + "]客户端上线";
    ui->listWidget->addItem(log);

    //通过信号槽处理接收到的请求
    connect(socket, &QTcpSocket::readyRead, [=]() {
        //读取请求
        QString request = socket->readAll();
        //根据请求处理响应
        QString response = process(request);
        //将响应写回客户端
        socket->write(response.toUtf8());
        //打印日志
        QString log = QString("[") + socket->peerAddress().toString() \
                + ":" + QString::number(socket->peerPort()) + "] request: " + \
                request + ", response: " + response;
        ui->listWidget->addItem(log);
    });

    //通过信号槽处理断开连接的情况
    connect(socket, &QTcpSocket::disconnected, this, [=]() {
        QString log = QString("[") + socket->peerAddress().toString() \
                + ":" + QString::number(socket->peerPort()) + "] 客⼾端下线";
        ui->listWidget->addItem(log);
        // 删除 socket
        socket->deleteLater();
    });
}

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

删除socket时最好不要直接使用delete,而是使用deleteLate

因为整个槽函数都是围绕socket进行操作的,务必确保delete是函数中的最后一步。使用deleteLater更加保险,其不会立即销毁socket,而是在下一轮事件循环中再进行销毁操作。槽函数都是在事件循环中执行的,进入到下一轮事件循环,意味着上一轮事件循环肯定结束了(即槽函数肯定执行完成了)

代码示例:回显客户端

创建界面,包含一个 QLineEdit、QPushButton、QListWidget

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void processRequest();
    void processResponse();

private:
    Ui::Widget *ui;
    QTcpSocket* socket;
};
#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);
    setWindowTitle("客户端");
    //实例化socket
    socket = new QTcpSocket(this);
    //与服务端建立连接
    socket->connectToHost("127.0.0.1", 9090);
    //等待并确认连接是否出错
    if(!socket->waitForConnected()) {
        QMessageBox::critical(nullptr, "连接服务器出错", socket->errorString());
        exit(1);
    }
    //信号槽处理接收请求并响应
    connect(socket, &QTcpSocket::readyRead, this, &Widget::processResponse);
    //信号槽处理发送请求
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::processRequest);
}

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

void Widget::processRequest()
{
    //获取输入框中的内容
    const QString& text = ui->lineEdit->text();
    //清空输入框中的内容
    ui->lineEdit->setText("");
    //将消息显示在界面上
    ui->listWidget->addItem(QString("客户端:") + text);
    //将消息发送给服务端
    socket->write(text.toUtf8());
}

void Widget::processResponse()
{
    QString response = socket->readAll();
    ui->listWidget->addItem(QString("服务端:") + response);
}

三、HTTP

关键类主要是三个:QNetworkAccessManager、QNetworkRequest、QNetworkReply

QNetworkAccessManager 提供了 HTTP 的核心操作

QNetworkRequest 表示一个 HTTP 请求(不含body)
若需要发送一个带有 body 的请求(如post),会在 QNetworkAccessManager 的 post 方法中通过单独的参数来传入 body

QNetworkRequest::KnownHeaders 是一个枚举类型,常用取值:

取值说明
ConnectTypeHeader描述body的类型
ConnectLengthHeader描述body的长度
LocationHeader用于重定向报文中指定重定向地址(响应中使用,请求中无使用场景)
CookieHeader设置cookie
UserAgentHeader设置User-Agent

QNetworkReply 表示一个 HTTP 响应,这个类同时也是 QIODevice 的子类

QNetworkReply 还有一个重要的信号 finished 会在客户端收到完整的响应数据之后触发

代码示例:HTTP客户端

创建界面,包含一个 QLineEdit、QPushButton、QPlainTextEdit

此处建议使用 QPlainTextEdit 而不是 QTextEdit,主要因为 QTextEdit 要进行富文本解析,若得到的 HTTP 响应体积过大,会导致界面渲染缓慢甚至被卡住

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QNetworkAccessManager>
#include <QNetworkReply>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void processRequest();

private:
    Ui::Widget *ui;
    QNetworkAccessManager* manager;
};
#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);
    setWindowTitle("客户端");
    //实例化manager
    manager = new QNetworkAccessManager(this);
    //信号槽处理发送请求
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::processRequest);

}

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

void Widget::processRequest()
{
    //获取输入框中url
    QUrl url(ui->lineEdit->text());
    //构造HTTP请求
    QNetworkRequest request(url);
    //发送GET请求
    QNetworkReply* response = manager->get(request);

    //通过信号槽处理响应
    connect(response, &QNetworkReply::finished, [=]() {
        if(response->error() == QNetworkReply::NoError) {
            QString html(response->readAll());
            ui->plainTextEdit->setPlainText(html);
        }
        else {
            ui->plainTextEdit->setPlainText(response->errorString());
        }
        response->deleteLater();
    });
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/529859.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Octopus:2B 参数语言模型即可媲美 GPT-4 的函数调用性能

近年来&#xff0c;大语言模型在 PC、智能手机和可穿戴设备的操作系统中应用逐渐成为趋势。 例如&#xff0c;MultiOn (Garg, 2024) 和 Adept AI (Luan, 2024) 等 AI 助理工具&#xff0c;以及 Rabbit R1 (Lyu, 2024) 和 Humane AI Pin (Chaudhri, 2024) 等 AI 消费产品在消费者…

Mac 装 虚拟机 vmware、centos7等,21年网络安全面经分享

链接: https://pan.baidu.com/s/1oZw1cLyl6Uo3lAD2_FqfEw?pwdzjt4 提取码: zjt4 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 centos8 链接: https://pan.baidu.com/s/10KWpCUa2JkwcjYlJZVogKQ?pwdn99a 提取码: n99a 复制这段内容后打开百度网盘手机App&…

电脑总是蓝屏怎么办,电脑总是蓝屏怎么办开不了机

工作离不开电脑&#xff0c;不过电脑经常会出现一些故障让我们崩溃不已&#xff0c;尤其类似电脑蓝屏开不了机这种&#xff0c;总是突然发生&#xff0c;让人猝不及防。那么面对这一情况&#xff0c;相信很多人都是不知道该如何处理的。这里提供两个方法&#xff0c;第一种方法…

网络安全---RSA公钥加密与签名

实验项目&#xff1a;RSA公钥加密与签名实验 1.实验目的 本实验的学习目标是让学生获得 RSA 算法的动手经验。 通过课堂学习&#xff0c;学生应该已经了解 RSA 算法的理论部分&#xff0c; 知道在数学上如何生成公钥、私钥以及如何执行加密、解密和签名生成、验证。 通过使用…

防SSL证书泄露服务器IP教程

在Web CDN&#xff08;内容分发网络&#xff09;中&#xff0c;防止SSL泄露源服务器IP是一个重要的安全考虑。下面是一些建议的方法来实现这一目标&#xff1a; 首先呢&#xff0c;我们隐藏服务器IP不要使用服务器IP生成的SSL证书&#xff0c;不然会泄露我们的服务器IP。 泄露了…

【BEV 视图变换】Fast-Ray 基于查找表LUT、多视角到单个三维体素转换

前言 在BEV感知方案中&#xff0c;将图像特征转为BEV特征&#xff0c;是关键的一步&#xff0c;这过程也称为2D视图变换。 本文介绍Fast-Ray方法&#xff0c;在Fast-BEV中被提出的&#xff0c;它是一种轻量级并且易于部署的视图转换方法&#xff0c;用于快速推理。 通过将多…

.net 6 集成NLog

.net 6 webapi项目集成NLog 上代码step 1 添加nugetstep 2 添加支持step 3 添加配置文件 结束 上代码 step 1 添加nuget 添加nuget 包 Roc step 2 添加支持 修改program.cs var builder WebApplication.CreateBuilder(args); // 添加NLog日志支持 builder.AddRocNLog();ste…

java中static关键字(尚未完善)

文章目录 static关键字static可修饰static方法举例static代码块拓展其他链接 static关键字 加载顺序类是构建对象的模板&#xff0c;一个类多个对象static修饰的方法或者变量都属于类&#xff0c;类独有的 static可修饰 修饰变量&#xff08;属于类变量&#xff0c;被创建出来…

极狐GitLab 如何在 helm 中恢复数据

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了如何在极狐GitLab …

mysql运维知识总结

1. 日志 1.1 错误日志 错误日志是 MySQL 中最重要的日志之一&#xff0c;它记录了当 mysqld 启动和停止时&#xff0c;以及服务器在运行过 程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 该日志是默认开启的&…

Linux: 工具: tshark 抓到了收方向的ESP明文包?

根据这个描述&#xff0c;看着是正常的&#xff0c; 抓到包之后&#xff0c;可以方便的分析问题&#xff0c;省去在wireshark里解码的问题。 经过调查发现是内核将ESP解开之后&#xff0c;如果是tunnel模式&#xff0c;内核又重新将skb丢给了interface去做处理。这样tshark/tcp…

搭建Grafana+Prometheus监控Spring Boot应用

Spring项目改造 maven依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency><dependency><groupId>io.micrometer</groupId><artif…

Linux服务器上搭建深度学习环境(安装anaconda、创建虚拟环境、安装pytorch)

Linux服务器的搭配 Linux服务器上安装anaconda创建虚拟环境linux上安装pytorchxshell连接服务器 Linux服务器上安装anaconda 链接 创建虚拟环境 参考教程&#xff1a;此处 linux上安装pytorch 链接 xshell连接服务器 链接

本地项目提交 Github

工具 GitIdeaGithub 账号 步骤 使用注册好的 Github 账号&#xff0c;登陆 Github&#xff1b; 创建 Repositories (存储库)&#xff0c;注意填写图上的红框标注&#xff1b; 创建完成之后&#xff0c;找到存储库的 ssh 地址或 https 地址&#xff0c;这取决于你自己的配置…

JRT判断数据是否存在优化

有一种业务情况类似下图&#xff0c;质控能做的项目是仪器关联的项目。这时候维护质控物时候开通项目时候要求加载仪器项目里面的项目&#xff08;没有开通的子业务数据的部分&#xff09;。对右边已经开通的部分要求加载仪器项目里面的项目&#xff08;有开通业务子数据的部分…

微信小程序使用iconfont

进入iconfont&#xff0c;添加至项目 进入项目&#xff0c;点击生成代码&#xff0c;或更新代码 点击打开样式 复制内容到小程序的style文件夹下 最后引入到app.wxss

鹅厂实习offer

#转眼已经银四了&#xff0c;你收到offer了吗# 本来都打算四月再投实习了&#xff0c;突然三月初被wxg捞了&#xff08;一年前找日常实习投的简历就更新了下&#xff09;&#xff0c;直接冲了&#xff0c;流程持续二十多天&#xff0c;结果是运气还不错&#xff0c;应该是部门比…

C# 之 Task、async和 await 、Thread 的简单整理

1、异步方法(async/await) 在 C# 5.0 中出现的 async 和 await &#xff0c;让异步编程变得更简单。 此方法利用了 .NET Framework 4.5 及更高版本、.NET Core 和 Windows 运行时中的异步支持。 编译器可执行开发人员曾进行的高难度工作&#xff0c;且应用程序保留了一个类似…

CAXA3D实体设计2022版 下载地址及安装教程

CAXA 3D是一款专业的实体设计软件&#xff0c;由中国软件公司CAXA开发。它提供了丰富的功能和工具&#xff0c;用于进行三维实体建模和设计。 CAXA 3D具备强大的建模和绘图功能&#xff0c;使用户能够创建复杂的三维实体模型。它支持多种建模方式&#xff0c;包括实体建模、曲…

智过网:报考中级注册安全工程师需要什么条件?

随着社会的快速发展和科技的日新月异&#xff0c;安全生产问题越来越受到人们的关注。中级注册安全工程师作为专业安全管理人才&#xff0c;其职责与角色日益凸显。那么&#xff0c;想要报考中级注册安全工程师&#xff0c;需要满足哪些条件呢&#xff1f; 首先&#xff0c;报考…