Qt_网络编程

news/2024/9/29 22:21:09 标签: qt, 开发语言, 网络, c++

目录

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.niftyadmin.cn/n/5683821.html

相关文章

GPT与大模型行业落地实践探索

简介 本课程探讨GPT和大模型技术在行业中的实际应用和发展。课程将涵盖GPT的基础知识、原理、及其在行业中的应用案例&#xff0c;如财报分析和客服机器人。重点在于结合实际案例中的使用效果&#xff0c;讲解如何利用GPT的API开发企业级应用以及利用更高级的功能构造AI Agent。…

第六章 6.1 字符串常用方法

字符串初始原理 JVM在启动过程中&#xff0c;有一个类加载的过程&#xff0c;会进行一系列的初始化&#xff08;包括类的加载&#xff0c;静态变量的初始化&#xff09;会初始化字符串常量池中。JVM一启动&#xff0c;整个字符串直接全部扫描到字符串常量池中。 源码&#xff…

正则表达式和re模块

正则表达式&#xff08;Regular Expression&#xff0c;简称Regex或RegExp&#xff09;是计算机科学中的一个重要概念&#xff0c;它通常被用来检索、替换那些符合某个模式&#xff08;规则&#xff09;的文本。正则表达式是对字符串操作的一种逻辑公式&#xff0c;通过事先定义…

63.5 注意力提示_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录注意力提示生物学中的注意力提示查询、键和值注意力的可视化使用 show_heatmaps 显示注意力权重代码示例 代码解析结果 小结练习 注意力提示 &#x1f3f7;sec_attention-cues 感谢读者对本书的关注&#xff0c;因为读者的注意力是一种稀缺…

linux kernel Gdb在线调试

一、常用命令 (gdb) i registers (gdb) bt (gdb) print x4 参考 GDB调试Linux内核模块_gdb调试内核模块-CSDN博客 GDB调试命令的基本用法_gdb查看pc的值-CSDN博客 使用GDB查看和修改寄存器的值_gdb查看寄存器的值-CSDN博客

加速 Python for 循环

在使用 Python 进行数据处理和计算时&#xff0c;for 循环是一个非常常见的操作。然而&#xff0c;随着数据量的增加&#xff0c;单纯的 for 循环可能会变得缓慢&#xff0c;导致程序效率低下。那么&#xff0c;有哪些方法可以加速 Python 中的 for 循环操作呢&#xff1f;今天…

WASM实现加密与算法保护

随着互联网技术的发展&#xff0c;Web应用的安全性越来越受到开发者的重视。在Web应用中&#xff0c;客户端加密是一个重要的安全措施&#xff0c;它能够确保数据在传输过程中的安全性。然而&#xff0c;传统的JavaScript&#xff08;JS&#xff09;加密方式存在一个明显的缺点…

计算机毕业论文及毕业设计题目,计算机专业大专本科学位毕业论文题目推荐大全集

目录 一 软件工程方向 二 网络安全方向 三 人工智能与机器学习方向 四 大数据方向 五 云计算与虚拟化方向 六 数据库与信息系统方向 计算机专业的毕业论文或毕业设计题目通常需要结合当前的技术趋势以及个人兴趣来确定。一个好的选题不仅能够体现学生的学术水平和技术能…