Qt_网络编程

news2024/11/16 17:43:37

目录

1、Qt的UDP Socket

1.1 用Udp实现服务器 

1.2 用Udp实现客户端 

2、Qt的TCP Socket 

2.1 用Tcp实现服务器

2.2 用Tcp实现客户端

3、Qt的HTTP

3.1使用Qt的HTTP

结语 


前言:

        网络协议是每个平台都必须遵守的,只是不同的平台所提供的网络API不相同,而Qt具有跨平台性,因此Qt对网络编程也封装了一套自己的API。值得注意的是,在使用Qt进行网络编程之前, 需要在项目中的.pro文件中添加network模块。

1、Qt的UDP Socket

        Qt使用UDP通信,需要用到两个类,分别是:1、QUdpSocket,2、QNetworkDatagram。其中在网络通信中关于socket的相关工作都被集成在QUdpSocket类中,而数据传输用到的数据报则用QNetworkDatagram类表示(数据报包括数据内容,对方的端口号、ip地址)。

        QUdpSocket提供的接口介绍如下:

bind(const QHostAddress&, quint16)
绑定端口号、ip地址
receiveDatagram()
返回 QNetworkDatagram,即 对方发送过来的数据报
writeDatagram(const QNetworkDatagram&)
向对方发送一个QNetworkDatagram
readyRead(是一个信号)
在收到数据并准备就绪后触发

        QNetworkDatagram提供的接口介绍如下:

QNetworkDatagram(const QByteArray&, const QHostAddress& , quint16 )
通过 QByteArray, 目标IP地址,目标端⼝号构造⼀个 UDP数据报,通常用于发送数据时
data()
返回QByteArray,表示数据报内部持有的文本
senderAddress()
获取数据报中对方的IP地址
senderPort()
获取数据报中对方的端⼝号

        QByteArray是⼀个字节数组,可以和QString进行相互转换。例如: 使⽤QString的构造函数即可把QByteArray转成QString,使用QString的toUtf8函数即可把QString转成QByteArray

1.1 用Udp实现服务器 

         服务器的界面设计比较简单,因为服务器只需要显示消息即可,服务器回馈给客户端的信息也是程序自动触发的,所以只需要一个QListWidget控件即可,界面设计如下:

        1、首先在widget.h文件中创建一个QUdpSocket对象,代码如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void request();//槽函数

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

        2、在widget.cpp文件中new出一个QUdpSocket对象给到udp指针,并完成信号readyRead与槽函数的连接,而所有的发送、接收逻辑都在该槽函数中实现。于此同时还要完成绑定,目的是让服务器能够接收到客户端的消息。代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QNetworkDatagram>

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

    ser = new QUdpSocket(this);

    this->setWindowTitle("服务器");//设置窗口标题

    connect(ser,&QUdpSocket::readyRead,this,&Widget::request);//绑定操作

    bool ret = ser->bind(QHostAddress::Any,8080);//连接信号与槽
    if(!ret)
    {
        QMessageBox::critical(nullptr, "服务器启动出错", ser->errorString());
        return;
    }
}

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

QString ser_response(QString request)
{
    return "服务器说:"+request;
}

void Widget::request()
{
    QNetworkDatagram request = ser->receiveDatagram();//获取一个请求的数据报
    QString request_text = request.data();//拿到数据报中的文本内容
    QString response_text = ser_response(request_text);//模拟服务器处理请求的动作,生成一个答复

    //将答复发送回去
    QNetworkDatagram response(request_text.toUtf8(),request.senderAddress(),request.senderPort());
    ser->writeDatagram(response);

    //在界面的listwidget中打印出以上信息
    QString log = "["+request.senderAddress().toString()+" "+
            QString::number(request.senderPort())+"]"+"客户端说:"+request_text;

    log+=" "+response_text;
    ui->listWidget->addItem(log);

}


        运行结果:

        此时的运行结果什么也观察不到,原因就是服务器的接收和反馈功能都是在触发readyRead信号时才会执行的,而只有当客户端发送数据才会触发readyRead信号,所以还需要写一个客户端才能看到具体的效果。

1.2 用Udp实现客户端 

         设计一个界面,该界面包含⼀个QLineEdit , QPushButton , QListWidget。其中将要发送的文本写进QLineEdit中,点击QPushButton按钮就进行发送操作,并且发送的信息会显示在QListWidget,方便后续的查看。界面如下:

        1、和服务器一样,先在widget.h文件中创建一个QUdpSocket对象,代码如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_pushButton_clicked();//按钮的槽函数
    void response();//接收的数据时,会执行该槽函数
private:
    Ui::Widget *ui;
    QUdpSocket* cli;
};
#endif // WIDGET_H

        2、首先实现发送消息的逻辑:QPushButton的槽函数,点击QPushButton时,则客户端向服务器发送信息,其次实现接收逻辑,因为客户端要拿到服务器的反馈,因此可以连接readyRead信号与槽,在槽函数中实现接收逻辑。代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QNetworkDatagram>

const QString& ser_ip = "127.0.0.1";//服务器ip
const int ser_port = 8080;//服务器端口号

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

    cli = new QUdpSocket(this);

    //设置窗口标题
    this->setWindowTitle("客户端");
    //连接信号与槽,目的是处理服务器反馈的消息
    connect(cli,&QUdpSocket::readyRead,this,&Widget::response);

}

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


void Widget::on_pushButton_clicked()//按钮的槽函数
{
    QString text = ui->lineEdit->text();//获取要发送给服务器的内容

    //构造数据报
    QNetworkDatagram resquest(text.toUtf8(),QHostAddress(ser_ip),ser_port);
    cli->writeDatagram(resquest);//发送给客户端
    ui->listWidget->addItem("客户端说:"+text);//将发送的内容显示在界面上
    ui->lineEdit->clear();//发送过后情况输入框中的内容

}

void Widget::response()
{
    QString response = cli->receiveDatagram().data();
    ui->listWidget->addItem("服务器说:"+response);
}

        运行结果:

2、Qt的TCP Socket 

        Qt使用TCP 通信,需要用到两个类:1、QTcpServer,2、QTcpSocket。其中QTcpServer是专门给服务器提供的类,客户端用不到该类,服务器用该类进行绑定、监听以及建立连接,连接建立好后,使用QTcpSocket类进行数据的传输和接收。

        QTcpServer提供的函数介绍如下:

listen(const QHostAddress&, quint16 port)
绑定指定的地址和端口号, 并开始监听
nextPendingConnection()
用于建立连接,返回⼀个QTcpSocket类型的指针,表示与客户端建立好了连接,通过这个指针完成与客户端的通信
newConnection (是一个信号)
与新的客户端建立好连接后触发

        QTcpSocket提供的函数介绍如下:

readAll()
读取当前接收缓冲区中的所有数据存放到QByteArray对象并返回
write(const QByteArray& )
将数据发送给对方
deleteLater()
暂时把socket对象标记为⽆效,在下个事件循环中析构释放当前对象
readyRead
在收到数据并准备就绪后触发
disconnected
连接断开时触发
connectconst QHostAddress&, quint16用于给客户端连接上服务器

2.1 用Tcp实现服务器

         Tcp实现服务器的界面逻辑和Udp相似,只需要一个QListWidget即可,界面设计如下:

         1、在widget.h文件中创建一个QUdpSocket对象,代码如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void new_cli();//建立连接时调用的槽函数

private:
    Ui::Widget *ui;
    QTcpServer* ser;
};
#endif // WIDGET_H

        2、当有客户端进行连接时,服务器的QTcpServer会产生newConnection信号,说明有客户端连接服务器了,这时候可以在对应的槽函数中实现通信逻辑,即调用nextPendingConnection函数获取到QTcpSocket指针,通过该指针与新连接的客户端进行数据传输。此时,当客户端发送信息时,QTcpSocket也会产生readyRead信号,在该信号的槽函数中实现具体接收和发送逻辑,最后可以在客户端断开连接的时候,依靠信号disconnected来做一个提示。服务器的widget.cpp代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QTcpSocket>

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

    ser = new QTcpServer(this);

    //设置窗口标题
    this->setWindowTitle("服务器");
    //当有新客户端来连接时,执行new_cli函数
    connect(ser,&QTcpServer::newConnection,this,&Widget::new_cli);
    //绑定、监听
    ser->listen(QHostAddress::Any,8080);

}

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

QString ser_response(QString request)
{
    return request;
}

void Widget::new_cli()
{
    QTcpSocket* client = ser->nextPendingConnection();//拿到客户端的socket
    QString text = "["+client->peerAddress().toString()+" "
            +QString::number(client->peerPort())+"]"+"客户端上线";
    ui->listWidget->addItem(text);

    //lambda处理数据传输
    connect(client,&QTcpSocket::readyRead,this,[=](){
        QString request = client->readAll();//拿到客户端的请求
        QString response = ser_response(request);//模拟服务器处理请求,得到答复
        client->write(response.toUtf8());//反馈给客户端

        QString log = "["+client->peerAddress().toString()+" "
                +QString::number(client->peerPort())+"]"+"客户端说:"+request
                +" "+"服务器说:"+response;
        ui->listWidget->addItem(log);
    });

    //lambda处理断开连接
    connect(client,&QTcpSocket::disconnected,this,[=](){
        QString log = "["+client->peerAddress().toString()+" "
                 + QString::number(client->peerPort()) + "]客⼾端下线!";
         ui->listWidget->addItem(log);
         // 删除 clientSocket
         client->deleteLater();
    });
}

        仅仅有一个服务器的代码是无法测试结果的,因此还需要些一个客户端代码,如下文。 

2.2 用Tcp实现客户端

        Tcp实现的客户端界面和Udp客户端界面是一样的,因此这里不再展示。不同的是,Tcp的客户端无需使用QTcpServer,直接使用QTcpSocket类进行通信即可,客户端的widget.cpp代码如下:

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

const QString& ser_ip = "127.0.0.1";//服务器ip
const int ser_port = 8080;//服务器端口号

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

    cli = new QTcpSocket(this);
    //设置标题
    this->setWindowTitle("客户端");
    //连接服务器
    cli->connectToHost(ser_ip,ser_port);
    //实现客户端接收服务器反馈的逻辑
    connect(cli,&QTcpSocket::readyRead,this,[=]()
    {
       QString ser_text = cli->readAll();
       ui->listWidget->addItem("服务器说:"+ser_text);
    });

}

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


void Widget::on_pushButton_clicked()//按钮的槽函数
{
    QString text = ui->lineEdit->text();//获取输入框的文本
    cli->write(text.toUtf8());//发送该文本
    ui->listWidget->addItem("客户端说:"+text);//显示在listwidget上
    ui->lineEdit->clear();//发送完成后清空输入框

}

        运行结果:

        客户端关闭后:

3、Qt的HTTP

         Qt对HTTP协议做了封装,以供开发者方便使用HTTP协议进行与服务器的交互。Qt使用HTTP协议的核心三个类分别是:1、QNetworkRequest,2、QNetworkAccessManager,3、QNetworkReply。

        1、其中QNetworkRequest提供的核心API如下:

QNetworkRequest(const QUrl& )
通过URL构造⼀个 HTTP 请求

        2、QNetworkAccessManager提供的核心API如下:

get(const QNetworkRequest& )
QNetworkRequest为参数, 发起⼀个 HTTP GET 请求,返回 QNetworkReply对象

        3、QNetworkReply提供的核心API如下:

readAll()
读取响应body
error()
获取出错状态
errorString()
获取出错原因的文本
finished(是一个信号)在客户端收到完整的响应数据之后触发,使用逻辑和上文的readyRead一样,即该信号触发后就可以从网络中读取内容了

3.1使用Qt的HTTP

        界面设计和上述的客户端例子一样,通过发送输入框中的文本内容给服务器,从而从服务器获取到答复,再将该答复显示在QListWidget。

        widget.h代码如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QNetworkAccessManager>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QNetworkAccessManager* manager;
};
#endif // WIDGET_H

         widget.cpp代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QNetworkReply>

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

    manager = new QNetworkAccessManager(this);
}

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


void Widget::on_pushButton_clicked()//实现按钮的槽函数
{
    // 1. 获取到输⼊框中的 URL, 构造 QUrl 对象
     QUrl url(ui->lineEdit->text());
     // 2. 构造 HTTP 请求对象
     QNetworkRequest request(url);
     // 3. 发送 GET 请求
     QNetworkReply* response = manager->get(request);
     // 4. 通过信号槽来处理响应
     connect(response, &QNetworkReply::finished, this, [=]() {
     if (response->error() == QNetworkReply::NoError) {
     // 响应正确
     QString html(response->readAll());
     ui->listWidget->addItem(html);
     // qDebug() << html;
     } else {
     // 响应出错
     ui->listWidget->addItem(response->errorString());
     }
     response->deleteLater();
     });
}

        运行结果:

结语 

        以上就是关于Qt网络编程的讲解,网络编程在任何平台下的编写逻辑都大同小异,不同的只是细节上的问题,Qt中对网络API进行了封装,这不仅让Qt可以在不同的平台下进行网络编程,还方便了开发者的使用。

        最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!    

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[element-ui]记录对el-table表头样式的一些处理

1、表头换行 & 列表项换行 可用element-table组件自带的方法实现列标题换行的效果 2、小圆点样式

Codeforces Round 973 (Div. 2) A-C 题解

C 提交 MLE 了一次&#xff0c;原因是找到答案没加感叹号 A. Zhan’s Blender 题意 原题描述还是不太清楚 你有 n n n 个水果&#xff0c;每秒可以放入搅拌机 y y y 个水果&#xff0c;搅拌机每秒可以搅拌 x x x 个水果&#xff0c;问最终至少需要多少秒能搅完&#xff1…

Python的包管理工具pip安装

Python的包管理工具pip安装 一、安装步骤1.检查 pip是否已安装2.安装 pip方法一&#xff1a;通过 ​ensurepip​ 模块安装(推荐)方法二&#xff1a;通过 ​get-pip.py​ 脚本安装&#xff08;经常应为网络域名问题连接不上&#xff09; 3.验证pip安装4.创建别名5.更新pip 二、常…

Java网络通信—UDP

1.总揽 2.客户端 使用udp通信&#xff0c;需要三个东西&#xff1a;数据本体、通道、数据包装工具 Scanner scanner new Scanner(System.in);String string scanner.next();byte[] bytes string.getBytes();// 数据本体DatagramSocket datagramSocket new DatagramSocket(…

代码随想录冲冲冲 Day58 图论Part9

47. 参加科学大会&#xff08;第六期模拟笔试&#xff09; 根据昨天的dijkstra进行堆优化 使用的原因是点多但边少 所以直接对于边进行操作 1.对于priority_queue来说 这是最小堆, 小于的话就是最大堆 之后由于是根据边来说的 所以新建一个Edge并且初始化一下 之后由于使用…

动态规划入门题目->使用最小费用爬楼梯

1.题目&#xff1a; 2.解析&#xff1a; 做题模式&#xff1a; 步骤一&#xff1a;找状态转移方程 步骤二&#xff1a;初始化 步三&#xff1a;填表 步骤四&#xff1a;返回-> dp[n] dp[i]表示到达 i 位置最小花费 逻辑&#xff1a;要爬到楼顶先找到 i 位置 &#xff0c; 要…

深度学习:(八)深层神经网络参数与流程

深层神经网络 符号规定 L L L &#xff1a;表示神经网络的层数&#xff1b; l l l &#xff1a;表示第几层&#xff1b; n [ l ] n^{[~l~]} n[ l ] &#xff1a;表示第 l l l 层的节点数&#xff1b; a [ l ] a^{[~l~]} a[ l ] &#xff1a;表示第 l l l 层中的激活函数&…

【web安全】——sql注入

1.MySQL基础 1.1information_schema数据库详解 简介&#xff1a; 在mysql5版本以后&#xff0c;为了方便管理&#xff0c;默认定义了information_schema数据库&#xff0c;用来存储数据库元数据信息。schemata(数据库名)、tables(表名tableschema)、columns(列名或字段名)。…

360AI搜索上线慢思考模式:成为全球首个慢思考搜索引擎 大幅提升回答质量

近日&#xff0c;360 AI搜索上线“慢思考模式”&#xff0c;成为国内首个具备慢思考能力的AI产品、全球首个具备慢思考能力的搜索引擎。据悉&#xff0c;慢思考模式基于CoE 技术架构&#xff0c;在该模式下360AI搜索能够大幅提升回答质量&#xff0c;完成多数AI ChatBot无法完成…

技术成神之路:设计模式(十七)组合模式

介绍 组合模式&#xff08;Composite Pattern&#xff09;是一种结构型设计模式&#xff0c;它使你能够将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 1.定义 组合模式允许将叶子对象和组合对象&#xff08;容器…

猫头虎带您解决:‘vue-cli-service’ 不是内部或外部命令

猫头虎带您解决&#xff1a;‘vue-cli-service’ 不是内部或外部命令 &#x1f680; 今天猫头虎收到一个粉丝的提问&#xff1a;“猫哥&#xff0c;我在用 Vue 项目的时候&#xff0c;运行命令 npm run serve 出现了错误提示&#xff0c;vue-cli-service 不是内部或外部命令&a…

玄机--蚁剑流量

木马的连接密码是多少 黑客执行的第一个命令是什么 id 黑客读取了哪个文件的内容&#xff0c;提交文件绝对路径 /etc/passwd 黑客上传了什么文件到服务器&#xff0c;提交文件名 黑客上传的文件内容是什么 黑客下载了哪个文件&#xff0c;提交文件绝对路径 蚁剑流量特征总结 …

参会通知!第三届计算、通信、感知与量子技术国际会议(CCPQT 2024)

参会通知&#xff01;新增特别论坛&#xff1a;国自然基金经验分享 第三届计算、通信、感知与量子技术国际会议&#xff08;CCPQT 2024&#xff09;将于2024年10月25-27日在珠海召开&#xff0c;聚焦感知技术、绿色通信等&#xff0c;由北邮主办&#xff0c;宁波大学协办。会议…

seL4 IPC(五)

官网链接&#xff1a;link 求解 代码中的很多方法例如这一个教程里面的seL4_GetMR(0)&#xff0c;我在官方给的手册和API中都搜不到&#xff0c;想问一下大家这些大家都是在哪里搜的&#xff01;&#xff01; IPC seL4中的IPC和一般OS中讲的IPC概念相差比较大&#xff0c;根…

华大基因用药指导基因检测助力优化治疗方案,科学管理糖尿病

糖尿病是全球范围内的一个重大公共卫生问题。据国际糖尿病联盟&#xff08;IDF&#xff09;统计&#xff0c;全球糖尿病患者人数约为5.39亿&#xff0c;其中中国患者数量约为1.409亿。作为一种慢性代谢性疾病&#xff0c;糖尿病往往不是独立存在的&#xff0c;通常还会伴随着多…

【SSM_Day3】JSON字符串和Java对象互转

【SSM_Day3】JSON字符串和Java对象互转 JSON档案JSON字符串和Java对象互转BEJSON&#xff1a;在线JSON格式化校验工具 JSON档案 数据格式是描述数据保存在文件或记录中的规则&#xff0c;比如Excel就是一种数据格式&#xff0c;数据保存在Excel的表格中。JSON也是一种数据格式…

【JAVA报错已解决】Java.lang.NumberFormatException

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

Python:百度贴吧实现自动化签到

早知道&#xff0c;还是python。 Github项目仓库在这。 相关API 签到贴吧列表 签到分为两个接口&#xff0c;PC端签到一次经验2&#xff0c;而移动端签到则是一次经验6。该用哪个接口已经很明显了。不过这里还是列出PC端的签到API。 # PC端签到接口 # sign_url "https:…

Hive SQL业务场景:连续5天涨幅超过5%股票

一、需求描述 现有一张股票价格表 dwd_stock_trade_dtl 有3个字段分别是&#xff1a; 股票代码(stock_code), 日期(trade_date)&#xff0c; 收盘价格(closing_price) 。 请找出满足连续5天以上&#xff08;含&#xff09;每天上涨超过5%的股票&#xff0c;并给出连续满足…

LLM大模型书籍:专补大模型短板的RAG入门与实战书来了!

文末赠书 RAG自2020年由Facebook AI Research推出后&#xff0c;一下子就窜红了。 毕竟&#xff0c;它是真的帮了大忙&#xff0c;在解决大语言模型的“幻觉”问题上起到了关键作用。 如今&#xff0c;Google、AWS、IBM、微软、NVIDIA等科技巨头都在支持RAG应用的开发。微软…