Qt--网络编程

Qt 网络

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

在进行网络编程之前, 需要在项目中的 .pro 文件中添加 network 模块.

添加之后要手动编译⼀下项目, 使 Qt Creator 能够加载对应模块的头文件.

UDP Socket

核心 API 概览

主要的类有两个. QUdpSocket 和 QNetworkDatagram

QUdpSocket 表示一个 UDP 的 socket文件.

|-----------------------------------------|--------|------------------------------------|----------------------|
| 名称 | 类型 | 说明 | 对标原生 API |
| bind(constQHostAddress&,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 包含了该功 能. |

客户端和服务器简单回显

server.cpp

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("服务器");

    //连接信号槽
    connect(socket,&QUdpSocket::readyRead,this,&Widget::processRequest);
    //注意!!!先连接信号槽再绑定端口号,如果反过来的话在绑定之后在连接之前右请求就处理不了

    //绑定端口号
    bool ret = socket->bind(QHostAddress::Any,8080);
    if(!ret)
    {
        //绑定失败
        QMessageBox::critical(this,"服务器启动出错",socket->errorString());
        return;
    }


}

Widget::~Widget()
{
    delete ui;
}
//
void Widget::processRequest()
{
    //1.读取请求信息
     QNetworkDatagram requestDatagram =  socket->receiveDatagram();
    QString request = requestDatagram.data();
    //2.根据请求响应计算(由于是回显服务器,响应不需要计算,就是请求本身)
      QString response =  process(request);
    //3.把相应写回给客户端
    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;
}

client.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QNetworkDatagram>
//定义两个常量描述服务器的地址和端口
const QString& SERVER_IP = "127.0.0.1";
const quint16 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()
{
    //1.获取输入框的文本
   const QString & text = ui->lineEdit->text();
    //2.构造UDP请求数据
   QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP) ,PORT);
   //3.发送请求数据
   socket->writeDatagram(requestDatagram);
   //4.把发送的请求也添加到列表框
   ui->listWidget->addItem("客户端说:"+text);
   //5.清空输入框
   ui->lineEdit->setText("");
}

void Widget::processResponse()
{
    //通过这个函数来处理收到的响应
    //1.读取响应
  const QNetworkDatagram& responseDatagram =  socket->receiveDatagram();
  QString response = responseDatagram.data();
    //2.显示响应
   qDebug() << "收到服务器数据:" << response;
  ui->listWidget->addItem("服务器说:"+response);
}

TCP Socket

核心 API 概览

核心类是两个: QTcpServer 和 QTcpSocket

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

|-------------------------------------------|--------|-------------------------------------------------------------------------------|--------------------------|
| 名称 | 类型 | 说明 | 对标原生 API |
| listen(const QHostAddress&,quint16 port) | 方法 | 绑定指定的地址和端口号, 并开始监 听. | bind 和 listen |
| nextPendingConnection() | 方法 | 从系统中获取到一个已经建立好的tcp 连接.返回一个 QTcpSocket , 表示这个客户端的连接.通过这个 socket 对象完成和客户端之间的通信. | accept |
| newConnection | 信号 | 有新的客户端建立连接好之后触发. | 无 (但是类似于 IO 多路复用 中的通知机制) |

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

|----------------------------|----|------------------------------------------|-------------------------|
| 名称 | 类型 | 说明 | 对标原生 API |
| readAll() | 方法 | 读取当前接收缓冲区中的所有数据.返回 QByteArray 对象 | read |
| write(const QByteArray& ) | 方法 | 把数据写入 socket 中. | write |
| deleteLater | 方法 | 暂时把 socket 对象标记为无效. Qt会在下个事件循环中析构释放该对 象. | 无(但是类似于 "半自动化的 垃圾回收") |
| readyRead | 信号 | 有数据到达并准备就绪时触发. | 无 (但是类似于 IO 多路复用中的通知机制) |
| disconnected | 信号 | 连接断开时触发. | 无 (但是类似于 IO 多路复用中的通知机制) |

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

例如:

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

server.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QMessageBox>
#include<QTcpSocket>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //1.修改窗口标题
    this->setWindowTitle("服务器");

    //2.创建QTcpServer的实例
    tcpServer = new QTcpServer(this);

    //3.实现槽函数
    connect(tcpServer,&QTcpServer::newConnection,this,&Widget::processConnection);

    //4.绑定并监听
   bool ret = tcpServer->listen(QHostAddress::Any,8080);
   if (ret == false)
   {
       QMessageBox::critical(this,"服务器启动失败!",tcpServer->errorString());
       exit(1);
   }

}

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

void Widget::processConnection()
{
    //1.通过tcpServer拿到一个socket对象,通过这个对象来和客户端进行通信
    QTcpSocket* clientSocket = tcpServer->nextPendingConnection();

    QString log = "[" +clientSocket->peerAddress().toString()+":"+QString::number(clientSocket->peerPort())+"]客户端上线!";
    ui->listWidget->addItem(log);

    //2.通过信号槽,来处理客户端发来的情况
    connect(clientSocket,&QTcpSocket::readyRead,this,[=](){
        //readAll返回的是QByteArray,通过赋值转成QString
     QString request = clientSocket->readAll();
     const QString& response = process(request);
     //把响应写回客户端
     clientSocket->write(response.toUtf8());
     //把上述信息记录到日志中
     QString log = "[" + clientSocket->peerAddress().toString()+ ":"+QString::number(clientSocket->peerPort())+"]"
             +"req: "+request+",resp: "+response;
     ui->listWidget->addItem(log);
    });
    //3.通过信号槽来处理客户端断开连接的情况
    connect(clientSocket,&QTcpSocket::disconnected,this,[=](){
        //把断开连接的信息通过日志显示出来
        QString log = "[" +clientSocket->peerAddress().toString()+":"+QString::number(clientSocket->peerPort())+"]客户端上线!";
        ui->listWidget->addItem(log);
        //手动释放clienSocket
         clientSocket->deleteLater();
    });
}

QString Widget::process(const QString request)
{
    //回显服务器
    return request;
}

client.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QMessageBox>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //设置窗口标题
    this->setWindowTitle("客户端");
    //2.创建socket的实例
    socket = new QTcpSocket(this);
    //3.和服务器建立连接
    socket->connectToHost("127.0.0.1",8080);//和Linux不一样,qt的是非阻塞
    //4.连接信号槽处理响应
    connect(socket,&QTcpSocket::readyRead,this,[=](){
      QString response = socket->readAll();
        //显示内容
        ui->listWidget->addItem("服务器说: "+ response);
    });

    //5.等待建立结果
    bool ret = socket->waitForConnected();
    if(!ret)
    {
        QMessageBox::critical(this,"连接服务器出错",socket->errorString());
        exit(1);
    }


}

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


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

    //2.发送数据给服务器
    socket->write(text.toUtf8());
    //3把发的消息显示到界面上
    ui->listWidget->addItem("客户端说:"+text);
    //4.清空输入框的内容
    ui->lineEdit->setText("");
}

HTTP Client

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

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

核心 API

关键类主要是三个. QNetworkAccessManager , QNetworkRequest , QNetworkReply。QNetworkAccessManager 提供了 HTTP 的核心操作.

|-----------------------------------------------------|-----------------------------------------|
| 方法 | 说明 |
| get(const QNetworkRequest& ) | 发起一个 HTTP GET 请求. 返回 QNetworkReply 对象. |
| post(const QNetworkRequest& , const QByteArray& ) | 发起一个 HTTP POST 请求. 返回 QNetworkReply 对象. |

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

如果需要发送一个带有 body 的请求(比如 post), 会在 QNetworkAccessManager 的 post 方法

中通过单独的参数来传入 body.

|------------------------------------------------------------------------|---------------------|
| 方法 | 说明 |
| QNetworkRequest(const QUrl& ) | 通过 URL 构造一个 HTTP 请求 |
| setHeader(QNetworkRequest::KnownHeaders header,const QVariant &value) | 设置请求头. |

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

|---------------------|---------------------------------|
| 取值 | 说明 |
| ContentTypeHeader | 描述 body 的类型. |
| ContentLengthHeader | 描述 body 的长度. |
| LocationHeader | 用于重定向报文中指定重定向地址. (响应中使用, 请求用不到) |
| CookieHeader | 设置 cookie |
| UserAgentHeader | 设置 User-Agent |

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

|----------------------------------------------|-------------------|
| 方法 | 说明 |
| error() | 获取出错状态. |
| errorString() | 获取出错原因的文本. |
| readAll() | 读取响应 body. |
| header(QNetworkRequest::KnownHeaders header) | 读取响应指定 header 的值. |

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

发.

http客户端

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QNetworkReply>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("客户端");
    manager = new QNetworkAccessManager(this);

}

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


void Widget::on_pushButton_clicked()
{
    //1.获取到输入框中的url
   QUrl url =  ui->lineEdit->text();
    //2.构造一个HTTP请求对象
   QNetworkRequest request(url);
   //3.发送请求
   QNetworkReply* response = manager->get(request);
   //4.通过信号槽处理响应
   connect(response,&QNetworkReply::finished,this,[=](){
       if(response->error() == QNetworkReply::NoError)
       {
           //响应正确获取到了
          QString html = response->readAll();
          ui->plainTextEdit->setPlainText(html);
       }
       else
       {
           //响应出错了
           ui->plainTextEdit->setPlainText(response->errorString());
           exit(1);
       }
       //释放response
       response->deleteLater();
   });
}

Qt 音视频

Qt 音频

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

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

play() //开始或继续播放当前源。

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QSound>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
     sound = new QSound(":/xiaoxingxing.wav",this);
}

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


void Widget::on_pushButton_clicked()
{
    //音频播放

    sound->play();

}

总结

  1. Qt事件机制 鼠标事件 键盘事件 定时器事件 窗口事件 => 继承多态
  2. 文件操作
  3. 多线程
  4. 网络 API的使用 UDP,TCP,HTTP
  5. 多媒体 播放音频
相关推荐
m0_519196401 小时前
【设计模式】java的习题
开发语言·python
我命由我123451 小时前
Jetpack Compose - Material Design 断点范围、WindowSizeClass、针对不同屏幕尺寸创建预览、四类导航栏
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
captain3761 小时前
多线程进阶
java·开发语言·数据库
郑州光合科技余经理8 小时前
代驾系统架构拆解:订单链路、权限组织与私有化源码交付
开发语言·后端·算法·架构·系统架构·uni-app·php
码哥DFS11 小时前
二叉树的直径
开发语言·javascript·算法
paopaokaka_luck11 小时前
基于springboot3+vue3的企业考勤管理系统(部门树递归、Echarts图形化分析)
开发语言·spring boot·学习·echarts·mybatis·需求分析·代码规范
梦雨生生13 小时前
java开发工具(学习第一天)
java·开发语言·学习
啊啊啊啊啊!!!!14 小时前
【c++】stack和queue的接口使用以及底层实现
开发语言·c++
松仔log15 小时前
Java中级——组合和继承
android·java·开发语言