Qt开发⑪Qt网络+Qt音视频_使用实操

目录

[1. Qt 网络](#1. Qt 网络)

[1.1 UDP Socket](#1.1 UDP Socket)

[1.2 TCP Socket](#1.2 TCP Socket)

[1.3 HTTP Client](#1.3 HTTP Client)

[2. Qt 音视频](#2. Qt 音视频)

[2.1 Qt 音频](#2.1 Qt 音频)

[2.2 Qt 视频](#2.2 Qt 视频)

本篇完。


1. Qt 网络

和多线程类似,Qt 为了支持跨平台, 对网络编程的 API 也进行了重新封装。

实际 Qt 开发中进行网络编程,也不一定使用 Qt 封装的网络 API,也有一定可能使用的是系统原生 API 或者其他第三方框架的 API。

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

为什么 Qt 要划分出这些模块呢?

因为Qt 本身是一个非常庞大,包罗万象的框架。如果把所有的 Qt 的功能都放到一起,既是我们就只是写一个简单的 hello world,那此时生成的可执行程序也会非常庞大(就包含了大量并没有使用的功能)。

模块化处理:

  • 其它的功能分别封装成不同的模块。
  • 默认情况下,这些额外的模块不会参与编译。
  • 需要在 .pro 文件中引入对应的模块,才能把对于功能给编译加载进来。

1.1 UDP Socket

(1)核心 API 概览

主要的类有两个:QUdpSocketQNetworkDatagram

QUdpSocket 表示一个 UDPsocket 文件

QNetworkDatagram 表示一个 UDP 数据报


(2)回显服务器

  1. 创建界面,包含一个 QListWidget 用来显示消息
  1. 创建 QUdpSocket 成员

不能直接添加头文件,否则无法编译通过,需要先添加网络模块。

修改 widget.h

修改 widget.cpp,完成 socket 后续的初始化

一般来说,要先连接信号槽,再绑定端口。如果顺序反过来,可能会出现端口绑定好了之后,请求就过来了,此时还没来得及连接信号槽,那么这个请求就有可能错过了。


实现 processRequest,完成处理请求的过程

  • 读取请求并解析
  • 根据请求计算响应
  • 把响应写回到客户端

实现 process 函数

由于我们此处是实现回显服务器,所以 process 方法中并没有包含实质性的内容。

此时,服务器程序编写完毕,但是直接运行还看不出效果,还需要搭配客户端来使用。


(3)回显客户端

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

  • 先使用水平布局把 QLineEdit 和 QPushButton 放好,并设置这两个控件的垂直方向的 sizePolicy 为 Expanding。
  • 再使用垂直布局把 QListWidget 和上面的水平布局放好。
  • 设置垂直布局的 layoutStretch 为 5,1(这个尺寸比例可以根据个人喜好微调)。

在 widget.cpp 中,先创建两个全局常量,表示服务器的 IP 和端口

端口到本质上是一个 2 字节的无符号整数。

quint16:本质上就是一个 unsigned short(虽然 short 通常都是 2 个字节,但是 C++ 标准中没有明确规定这一点,只是说 short 不应该少于 2 个字节)。


创建 QUdpSocket 成员:

修改 widget.h,定义成员


修改 widget.cpp,初始化 socket


给发送按钮 slot 函数,实现发送请求


再次修改 Widget 的构造函数,通过信号槽来处理服务器的响应


最终执行效果

客户端服务器测试的基本原则:一定是先启动服务器,后启动客户端。

启动多个客户端都可以正常工作,但是不能在界面选择直接运行,否则会覆盖上一个客户端。


1.2 TCP Socket

(1)核心 API 概览

核心类是两个:QTcpServerQTcpSocket

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

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

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

例如:

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

(2)回显服务器

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


创建 QTcpServer 并初始化

修改 widget.h,添加 QTcpServer 指针成员


修改 widget.cpp,实例化 QTcpServer 并进行后续初始化操作

  • 设置窗口标题
  • 实例化 TCP server(父元素设为当前控件,会在父元素销毁时被一起销毁)
  • 通过信号槽,处理客户端建立的新连接
  • 监听端口

继续修改 widget.cpp,实现处理连接的具体方法 processConnection

  • 获取到新的连接对应的 socket
  • 通过信号槽,处理收到请求的情况
  • 通过信号槽,处理断开连接的情况

上述代码其实不够严谨,但在这里作为回显服务器已经够了。实际在使用 TCP 的过程中,TCP 是面向字节流的,一个完整的请求可能会分成多段字节数组进行传输。虽然 TCP 已经帮我们处理了很多棘手的问题,但是 TCP 本身并不负责区分从哪里到哪里是一个完整的应用层数据(粘包问题)。

更严谨的做法:每次收到的数据都给它放到一个字节数组缓冲区中,并且提前约定好应用层协议的格式(分隔符 / 长度 / 其他办法),再按照协议格式对缓冲区数据进行更细致的解析处理。


实现 process 方法,实现根据请求处理响应

由于此处是实现回显服务器,所以 process 方法中并没有包含实质性的内容。

此时,服务器程序编写完毕,但是直接运行还看不出效果,还需要搭配客户端来使用。


(3)回显客户端

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

  • 先使用水平布局把 QLineEdit 和 QPushButton 放好,并设置这两个控件的垂直方向的 sizePolicy 为 Expanding
  • 再使用垂直布局把 QListWidget 和上面的水平布局放好
  • 设置垂直布局的 layoutStretch 为 5,1(这个尺寸比例可以根据个人喜好微调)

创建 QTcpSocket 并实例化

修改 widget.h,创建成员


修改 widget.cpp,对 QTcpSocket 进行实例化

  • 设置窗口标题
  • 实例化 socket 对象(父元素设为当前控件,会在父元素销毁时被一起销毁)
  • 和服务器建立连接
  • 等待并确认连接是否出错

修改 widget.cpp,给按钮增加点击的 slot 函数,实现发送请求给服务器


修改 widget.cpp 中的 Widget 构造函数,通过信号槽处理收到的服务器的响应

先启动服务器,再启动客户端(可以启动多个),最终执行效果:

由于我们使用信号槽处理同一个客户端的多个请求,不涉及到循环,也就不会使客户端之间相互影响了。


1.3 HTTP Client

进行 Qt 开发时,和服务器之间的通信很多时候也会用到 HTTP 协议。

  • 通过 HTTP 从服务器获取数据
  • 通过 HTTP 向服务器提交数据

(1)核心 API

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

QNetworkAccessManager 提供了 HTTP 的核心操作:

QNetworkRequest表示一个 HTTP 请求(不含 body)。

如果需要发送一个带有 body 的请求(比如 post),会在 QNetworkAccessManager 的 post 方法中通过单独的参数来传入 body。

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

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

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


(2)给服务器发送一个 GET 请求

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

  • 先使用水平布局把 QLineEdit 和 QPushButton 放好,并设置这两个控件的垂直方向的 sizePolicy 为 Expanding。
  • 再使用垂直布局把 QPlainTextEdit 和上面的水平布局放好(QPlainTextEdit 的 readOnly 设为 true)。
  • 设置垂直布局的 layoutStretch 为 5,1(这个尺寸比例可以根据个人喜好微调)。

注意:此处建议使用 QPlainTextEdit,而不是 QTextEdit。主要是因为 QTextEdit 要进行富文本解析,最终显示的结果就不是原始的 HTML 了,如果得到的 HTTP 响应体积很大,会导致界面渲染缓慢甚至被卡住。


修改 widget.h,创建 QNetworkAccessManager 属性


修改 widget.cpp,创建实例


编写按钮的 slot 函数,实现发送 HTTP 请求功能

执行程序,观察效果:

发送 POST 请求代码也是类似,使用 manager->post() 即可。

实际开发中,HTTP Client 获取到的的数据也不一定非得是 HTML,更大的可能性是客户端开发和服务器开发约定好交互的数据格式。按照约定的格式,客户端拿到之后进行解析,并显示到界面上。

Qt 中还提供了 FTP、DNS、SSL 等网络相关的组件工具,这里不做过多介绍。


2. Qt 音视频

2.1 Qt 音频

在 Qt 中,音频主要是通过 QSound 类来实现。但是需要注意的是 QSound 类只⽀持播放 wav 格式的音频文件。也就是说如果想要添加音频效果,那么首先需要将非 wav 格式的音频文件转换为 wav 格式。

通过帮助手册查看 QSound 类如下:

注意:使用 QSound 类时,需要添加模块: multimedia


(1)核心 API 概览


(2)示例


2.2 Qt 视频

在 Qt 中,视频播放的功能主要是通过 QMediaPlayer 类和 QVideoWidget类来实现。在使用这两个类时要添加对应的模块 multimedia 和 multimediawidgets。

(1)核心 API 概览


(2)示例

首先在 .pro 文件中添加 multimedia 和 multimediawidgets 两个模块。

如下图示:

widget.h:

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
#include <QHBoxLayout>  //⽔平布局
#include <QVBoxLayout>  //垂直布局
#include <QVideoWidget> //显⽰视频
#include <QMediaPlayer> //播放声⾳
#include <QPushButton>  //按钮
#include <QStyle>       //设置图标
#include <QFileDialog>  //选择⽂件/⽂件夹
 
class Widget : public QWidget
{
    Q_OBJECT
 
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
 
public slots:
    void chooseVideo();
 
private:
    QMediaPlayer *mediaPlayer;
    QVideoWidget *videoWidget;
    QVBoxLayout *vbox;
 
    //创建两个按钮:选择视频按钮和开播放按钮
    QPushButton *chooseBtn,*playBtn;
 
};
#endif // WIDGET_H

widget.cpp :

cpp 复制代码
#include "widget.h"
#include <QMediaPlayer>
#include <QSlider>
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //对象实例化
    mediaPlayer = new QMediaPlayer(this);
    videoWidget = new QVideoWidget(this);
 
    //设置播放画⾯的窗⼝
    videoWidget->setMinimumSize(600,600);
    
    //实例化窗⼝布局---垂直布局
    this->vbox = new QVBoxLayout(this);
    this->setLayout(this->vbox);
 
    //实例化选择视频按钮
    chooseBtn = new QPushButton("选择视频", this);
 
    //实例化播放按钮
    playBtn = new QPushButton(this);
 
    //设置图标代替⽂件
    playBtn->setIcon(this->style()->standardIcon(QStyle::SP_MediaPlay));
 
    //实例化⼀个⽔平布局,将以上控件放⼊⽔平布局中
    QHBoxLayout *hbox = new QHBoxLayout;
 
    //添加控件
    hbox->addWidget(chooseBtn);
    hbox->addWidget(playBtn);
 
    //将播放窗⼝和⽔平布局都添加到垂直布局中
    vbox->addWidget(videoWidget);
 
    //布局中添加布局
    vbox->addLayout(hbox);
 
    //将选择视频对应的按钮和槽函数进⾏关联
    connect(chooseBtn,&QPushButton::clicked, this, &Widget::chooseVideo);
}
 
void Widget::chooseVideo()
{
    //选择视频,返回⼀个播放视频的名字
    QString name = QFileDialog::getSaveFileName(this, "选择视频", ".", "WMV(*.wmv)");
 
    //设置媒体声⾳
    mediaPlayer->setMedia(QUrl(name));
    
    //输出视频画⾯
    mediaPlayer->setVideoOutput(videoWidget);
 
    //播放
    mediaPlayer->play();
}
 
Widget::~Widget()
{
 
}

本篇完。

下一篇是Qt开发⑫Qt界面优化之CSS。

相关推荐
Hetertopia14 分钟前
在Vscode开发QT,完成QT环境的配置
开发语言·qt
傻啦嘿哟1 小时前
动态HTTP代理与静态HTTP代理的区别及HTTP代理的常见用途与类型
开发语言·php
小志开发1 小时前
Java 多态:代码中的通用设计模式
java·开发语言
慕容晓开1 小时前
c++,优先队列
数据结构·c++·算法
索然无味io1 小时前
Python--内置模块和开发规范(上)
开发语言·windows·笔记·python·web安全·网络安全
大麦大麦2 小时前
2025前端最新面试题-安全篇
开发语言·前端·javascript·安全·面试·ecmascript
wyz09232 小时前
python多线程之ThreadLocal 笔记
开发语言·python
Neil__Hu2 小时前
Go的基本语法学习与练习
java·c语言·c++·python·qt·学习·golang
彬sir哥2 小时前
水仙花数(华为OD)
java·c语言·javascript·c++·python·算法
Eugene__Chen2 小时前
java常见面试01
java·开发语言·面试