Qt网络编程——QUdpSocket

文章目录

Qt网络编程

网络编程本质上是写应用层代码,需要传输层提供支持。

而传输层最核心的协议就是UDP和TCP,这两个协议有较大差别,所以Qt提供了两套API。

要是有Qt网络编程的API,需要现在.pro文件当中添加network模块。

之前的各种控件,各种内容都是包含在QtCore模块当中的(默认添加)
为什么要划分模块?

Qt本身十分庞大,包含了很多框架,如果把所有的Qt功能放到一起,即是写一个简单的hello world,此时生成的可执行程序也会非常庞大。

比如说,Qt会应用到嵌入式,嵌入式系统的配置并没有那么高,所以对空间的利用 > 对时间的利用

进行模块化处理,在默认情况下,其他额外的模块不参与编译,用的时候在.pro文件中引入对应的模块,才能把对应功能给编译加载进来。

网络编程是操作系统提供的API,

C++标准库里面还未提供网络编程的api封装

QUdpSocket

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

QNetWorkDatagram表示有个UDP数据报

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

Udp回显服务器

一般服务器都没有图形化界面,此处为了更加直观看到,采用图形化界面方式

ui界面:

.pro引入network模块

widget.h

这里需要包含QUdpSocket头文件,包含之后可能还是会显示报错,将程序重新编译运行一下即可

cpp 复制代码
#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:
    Ui::Widget *ui;

    QUdpSocket* socket;

    void processRequest();
    QString process(const QString& request);
};
#endif // WIDGET_H

widget.cpp

构造对象的时候,参数可以是parent,也就是挂到对象树上(如果不写,之后手动delete即可)

要线连接信号槽,再进行bind

一旦进行bind,就意味着请求可以收到了。

如果在连接信号槽之前bind,可能收到请求,而信号槽还没连接,此时请求就丢失了

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QMessageBox>
#include<QNetworkDatagram>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建实例
    socket = new QUdpSocket(this);  //可以挂到对象树

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

    //连接信号槽
    connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);

    //绑定端口号
    //any表示不管几个网卡,都可以绑定上去
    if(!socket->bind(QHostAddress::Any, 8080))
    {
        QMessageBox::critical(this, "bind error", socket->errorString());
        return;
    }
}

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

void Widget::processRequest()
{
    //读取请求并解析
    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
    //data()返回QByteArray  QBtyeArray可以赋值给QString
    QString request = requestDatagram.data();

    //根据请求计算(此时是回显服务器, 收到什么响应什么)
    const QString& response = process(request);

    //把响应写给客户端
    //取出字节数组 发送到的地址 发送到的端口 都包含在requestDatagram
    QNetworkDatagram responseDatagram(response.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());
    socket->writeDatagram(responseDatagram);

    //交互的信息 显式到界面
    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort()) +
            "] req:" + request + ", resp: " + response;

    ui->listWidget->addItem(log);
}

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

Udp客户端

Qt Creator支持同时打开多个项目,但如果姓名中存在同名文件,就非常容易混淆

客户端主动发起请求,界面设置一个输入框,一个发送按钮,一个显示服务器返回内容

widget.h

cpp 复制代码
#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 processResponse();
private:
    Ui::Widget *ui;
    QUdpSocket* socket;
};
#endif // WIDGET_H

widget.cpp

在Linux中,需要考虑一些阻塞问题,什么时候阻塞,什么时候解除阻塞。

而这里直接用信号槽机制,很方便。

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QNetworkDatagram>
const QString SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 8080;

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

    socket = new QUdpSocket(this);
    //修改窗口标题
    this->setWindowTitle("客户端");

    //通过信号槽处理返回的数据
    connect(socket, &QUdpSocket::readyRead, this, &Widget::processResponse);

}

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


void Widget::on_pushButton_clicked()
{
    //获取输入框内容
    const QString& text = ui->lineEdit->text();

    //构造udp请求数据包
    //需要的参数是字节数组,要转换一下 IP地址也要转换一下
    QNetworkDatagram requestDatagram(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);

    //发送请求数据
    socket->writeDatagram(requestDatagram);

    //发送的请求添加到列表框
    ui->listWidget->addItem("client say# " + text);

    //输入框内容清空
    ui->lineEdit->setText("");
}

void Widget::processResponse()
{
    //读取响应数据
    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
    QString response = responseDatagram.data();
    //响应到界面
    ui->listWidget->addItem("server say# " + response);
}
相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
网络研究院16 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展
酣大智16 天前
ARP代理--工作原理
运维·网络·arp·arp代理