Qt_网络编程

devtools/2024/9/29 9:48:28/

目录

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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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();//拿到客户端的socketQString 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);// 删除 clientSocketclient->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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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.ppmy.cn/devtools/118631.html

相关文章

pdf.js滚动翻页的例子

可以使用 pdf.js 创建一个简单的滚动翻页效果。以下是一个基本示例&#xff0c;展示如何实现&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthd…

【QT 开发日志】QT 基础控件详解:按钮、文本框与标签的使用

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 博主简介 博主致力于嵌入式、Python、人工智能、C/C领域和各种前沿技术的优质博客分享&#xff0c;用最优质的内容带来最舒适的…

JDK1.8安装配置教程(图文结合,最简洁易懂)

分为两大步骤&#xff1a;安装JDK、配置环境变量 (环境变量是什么&#xff1f;) 一、安装JDK 1、双击运行安装程序 2、点击【下一步】 3.点击【公共JRE】&#xff0c;选择【此功能将不可用】&#xff0c;点击【下一步】 4.安装完成&#xff0c;点击【关闭】 二、配置…

【SQL】累计统计方法,使用SQL详细写出

一、累计统计方法 累计统计通常指的是在一组数据中&#xff0c;计算每个数据点的累积总和或者累积其他统计量。在SQL中&#xff0c;这通常可以通过使用窗口函数&#xff08;如 SUM() OVER()&#xff09;来实现。以下是一些常见的累计统计方法的例子&#xff1a; 累计求和 假…

借助spring的IOC能力消除条件判断

shigen坚持更新文章的博客写手&#xff0c;记录成长&#xff0c;分享认知&#xff0c;留住感动。个人IP&#xff1a;shigen 在前边讲到了如何借助HashMap、枚举类、switch-case消除条件判断&#xff0c;这里讲到我们最常见的用spring的IOC能力来消除代码中的逻辑判断。其实大部…

ARM base instruction -- ccmp

Conditional Compare (register) sets the value of the condition flags to the result of the comparison of two registers if the condition is TRUE, and an immediate value otherwise. 条件比较&#xff08;寄存器&#xff09;如果条件为真&#xff0c;则将条件标志的值…

time命令:轻松测量Linux命令执行时间!

一、命令简介 用途&#xff1a; 用于测量 Linux 命令执行的时间&#xff0c;包括实际时间、用户 CPU 时间和系统 CPU 时间。刚开始以为是用来“看现在几点钟”的 &#x1f972;。标签&#xff1a; 实用工具&#xff0c;性能分析。 ‍ 二、命令参数 2.1 命令格式 time [选项…

golang 如何生成唯一的 UUID

推荐学习文档 golang应用级os框架&#xff0c;欢迎stargolang应用级os框架使用案例&#xff0c;欢迎star案例&#xff1a;基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识&#xff0c;这里有免费的golang学习笔…