当前位置: 首页 > news >正文

【Qt】探索Qt网络编程:构建高效通信应用

文章目录

  • 前言:
  • 1. Qt 网络编程介绍
    • 1.1 什么是网络编程?
    • 1.2 Qt的模块
  • 2. UDP Socket
    • 2.1 核心 API 概述
    • 2.2 写一个带有界面的 Udp 回显服务器
    • 2.3 写一个带有界面的 Udp 客户端
  • 3. TCP Socket
    • 3.1 核心 API 概述
    • 3.2 代码:
  • 4. HTTP Client
    • 4.1 核心API
    • 4.2 代码示例
  • 总结:

前言:

在当今信息化社会,网络编程已成为软件开发中不可或缺的一部分。Qt,作为一个跨平台的应用程序框架,提供了丰富的网络编程API,使得开发者能够便捷地实现客户端和服务器之间的通信。本文将深入探讨Qt网络编程的基本概念、核心API以及实际应用示例,帮助读者理解并掌握使用Qt进行网络编程的方法。

1. Qt 网络编程介绍

1.1 什么是网络编程?

网络编程,操作系统提供了一组API(Socket API)
C++标准库中,并没有提供网络编程的 api 的封装。
进行网络编程的时候,本质是在编写应用层代码,需要传输层进行支持。
传输层最核心的协议,有 UDP 和 TCP,并且这两协议,差别还很大。
Qt 也提供了两套 API。

1.2 Qt的模块

使用 Qt 网络编程的 API,需要在 .pro 文件中添加 network 模块!
之前我们学过的 Qt 的各种控件,各种内容,都是包含在QtCore 模块中(默认就添加的)

为什么Qt要划分出这些模块呢?
Qt本身是一个非常庞大,包罗万象的框架。
如果把所有的 Qt 的功能都放到一起,即使咱们就只写一个简单的 hello world, 此时生产的可执行文件也会非常庞大。(这里就包含了大量其实没有使用的功能)

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

Qt 其实提供了静态库的版本和动态库的版本。

2. UDP Socket

2.1 核心 API 概述

主要有两个 QUdpSocket(一个文件) 和 QNetworkDatagram(数据包,UDP是面向数据报的)

QUdpSocket 表示一个 UDP 的 socket 文件。
在这里插入图片描述
readyRead:当socket 收到请求的时候,QUdpSocket 就会触发这个信号。
此时就可在槽函数里完成读取请求的操作了。

基于信号槽,就天然达成了"事件驱动"这样的一种网络编程的方式!

QNetWorkDatagram 表示一个UDP数据报
在这里插入图片描述

2.2 写一个带有界面的 Udp 回显服务器

一个正经的服务器很少会有图形化界面(一般都是命令行)
Qt 也是完全可以编写控制台程序的。

在 .pro 文件中添加一个network
在这里插入图片描述

注意:一定是先连接信号槽,后绑定端口号。 一旦绑定端口了,意味着请求就可以被收到了!
如果在绑定之后,在连接信号槽之前,有客户端把请求发过来了,此时就可能读不到这样的请求(就没了)

// 绑定端口号
socket->bind(QHostAddress::Any, 9090);

一个端口号只能被一个socket绑定,万一9090被别人绑定了呢? 返回绑定失败信息

代码:

#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 是用于通过对象树自动delete掉对象的// 不然就要在析构中去手动delete掉// 设置窗口标题this->setWindowTitle("服务器");// 连接信号槽connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);// 绑定端口号bool ret = socket->bind(QHostAddress::Any, 9090);if (!ret) {// 绑定失败!QMessageBox::critical(this, "服务器启动出错", socket->errorString());// socket->本质上也是对系统的errno机制进行封装, 相当与Linux中的perrorreturn;}}Widget::~Widget()
{delete ui;
}// 这个函数完成的逻辑,就是服务器的最核心逻辑了
void Widget::processRequest()
{// 1. 读取请求并解析const QNetworkDatagram& requestDatagram = socket->receiveDatagram();QString request = requestDatagram.data(); // .data() 返回的是一个QByteArray// QByteArray 是可以赋值给QString// 2. 根据请求计算响应(由于是回显服务器,响应不需要计算,就是请求本身)const QString& response = process(request);// 3. 把响应写回客户端QNetworkDatagram responseDatagram(response.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());// .toUtf8 取出QString 内部的字节数组,客户端是谁就包含在requestDatagram中了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;
}

2.3 写一个带有界面的 Udp 客户端

在这里插入图片描述
Qt Creator 中是可以同时打开多个项目。此时,如果这俩项目中存在同名文件就非常容易混淆。

此时写的客户端,要能够主动给服务器发起请求
在这里插入图片描述

// 定义两个常量,来描述服务器的 地址 和 端口
const QString& SERVER_IP = "127.0.0.1";
const quint16 PORT = 9090; 

端口号本质上是一个 2字节的 无符号 整数,quint16本质上就是一个unsigned short,虽然short通常都是2字节但是C++标准中没有明确规定这一点,只是说short不应该少于2个字节。

什么时候用引用?什么时候用赋值?

const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
QString response = responseDatagram.data();

啥时候使用引用类型,啥时候使用值类型,需要平时写代码的时候,多去思考,多去注意的 !
大的原则,肯定是能用引用尽量用引用,但有的时候注意到,尤其是上面这种不同的值进行互相转换的时候,大概率是要用值类型的!

代码:

#include "widget.h"
#include "ui_widget.h"
#include <QNetworkDatagram>// 定义两个常量,来描述服务器的 地址 和 端口
const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;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->textEdit->toPlainText();// 2. 构造 UDP 的请求数据QNetworkDatagram requestDatagram(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT); // 这里字符串的IP要转换为点分十进制的IP// 3. 发送请求数据socket->writeDatagram(requestDatagram);// 4. 把发送的请求也添加到列表框中ui->listWidget->addItem("客户端说:" + text);// 5. 把输入框的内容也清空一下ui->textEdit->setText("");
}void Widget::processResponse()
{// 通过这个函数来处理收到的响应// 1. 读取到响应数据const QNetworkDatagram& responseDatagram = socket->receiveDatagram();QString response = responseDatagram.data();// 啥时候使用引用类型,啥时候使用值类型,需要平时写代码的时候,多去思考,多去注意的// 大的原则,肯定是能用引用尽量用引用,但有的时候注意到,尤其是上面这种不同的值进行互相转换的时候,大概率是要用值类型的!// 2. 把响应数据显示到界面上ui->listWidget->addItem("服务器说:" + response);
}

在这里插入图片描述
在这里插入图片描述
如何启动多个客户端?
在这里插入图片描述
多启动几个可执行程序就好!
在这里插入图片描述
之前学Linux网络编程的时候,是使用云服务器部署服务器程序,其它的同学们也能连上。

  • 能否把现在的UDP服务器放到云服务器上呢?

大概率不行,取决于你的服务器是否安装了图形化界面,Qt程序需要依赖于图形化界面来运行的!Linux的云服务器,一般都是没有图形化界面的如果需要你需要手动额外安装。作为一个服务器,本身就是没有图形界面的(此处只是为了演示Qt网络编程的情况),也不会使用Qt来写服务器程序!

  • 能否使用先在的UDP客户端连接,Linux上写的UDP服务器呢?

这是完全OK的,这也是网络编程/协议的意义!
一般商业公司的项目,都是通过其它方式编写的服务器程序(大概率不会是Qt),但是使用Qt编写客户端!

3. TCP Socket

  • UDP 属于无连接,不可靠传输,面向数据报,全双工
  • TCP 有连接,可靠传输,面向字节流,全双工
    因此,TCP的代码,要比UDP稍微复杂一点点!

三次握手四次挥手(TCP建立链接或断开链接的时候完成的),操作系统系统内核负责完成的!
应用层的代码,只能告诉内核,我要发起一个连接(客户端),或者告诉内核,我要拿到一个已经建立好的连接(服务器)

3.1 核心 API 概述

核心类是:QTcpServerQTcpSocket

QTcpServer 用于监听客户端口,和获取客户端连接
在这里插入图片描述
QTcpSocket 用户客户端和服务器之间的数据交互
在这里插入图片描述

事件循环:简单理解,可以认为是Qt程序内部带有一个“生物钟”这样的东西!周期性的执行一些逻辑!

QByteArray ⽤于表⽰⼀个字节数组. 可以很⽅便的和 QString 进⾏相互转换. 例如:

  • 使⽤ QString 的构造函数即可把 QByteArray 转成 QString
  • 使⽤ QString 的 toUtf8 函数即可把 QString 转成 QByteArray
// 2. 通过信号槽,来处理客户端发来请求的情况// 2. 通过信号槽,来处理客户端发来请求的情况connect(clientSocket, &QTcpSocket::readyRead, this, [=](){// a)读取出请求数据,此处readAll 返回的是QByteArray, 通过赋值转成 QStringQString request = clientSocket->readAll();// b)根据请求处理响应const QString& response = process(request);// c) 把响应写回到客户端clientSocket->write(response.toUtf8());// d)把上述信息记录到日志中QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]"+ " req: " + request + ", resp:" + response;ui->listWidget->addItem(log);});

在Linux 网络编程,需要搞一个循环,循环的读取请求,循环处理…
在 Qt 中基于信号槽就不必循环了!
每次客户端发来请求,都能触发 readyRead 信号。
即使多个请求,槽函数也是可以顺利的执行到的!

  • 上述的代码其实是不够严谨,作为回显服务器是已经够了的!

  • 实际上使用TCP的过程中,TCP是面向字节流的,一个完整的请求,可能分成多段字节数组进行传输!

  • 虽然TCP已经帮我们处理了很多棘手的问题了,但是TCP本身不负责区分,从哪里到哪里是一个完整的应用层数据报(粘包问题)。

  • 更严谨的做法,应该是每次收到的数据都给放到一个大的字节数组缓冲区中,并且提前约定好应用层的协议的格式(分隔符?长度?其它办法?)

  • 再按照协议格式对缓冲区进行更细致的解析处理(当前不打算写这么复杂了)再按照协议格式对缓冲区数据进行更细致的解析处理

 QTcpSocket* clientSocket

每个客户端都有一个这样的对象,存在N个的,随着服务器的运行,客户端越来越多,如果不释放,此时累计的clientSocket也会越来越多!
QTcpServer QUdpSocket都是只有一份的(就算不释放,影响不大) 内存泄漏其实影响不大,但是如果是文件描述符泄漏呢?
现在的机器内存都很大,而文件描述符表的长度,则是操作系统的一个参数。Linux可以通过ulimits 命令来查看和调整!

// b) 手动释放 clientSocket
delete clientSocket;

一旦要是 delete 就意味着其它逻辑无法使用 clientSocket

务必要保证delete 是这个槽函数的最后一步,而且也要保证 delete 肯定能执行到,不会被return / 抛出异常 给跳过…

clientSocket->deleteLater();

直接使用delete是下策,使用deleteLater 更加合适 这个操作,不是立即销毁 clientSocket,
而是告诉Qt,下一轮事件循环中(槽函数都是在事件循环中执行的,进入到下一轮事件循环,意味着上一轮事件循环肯定结束了,也以为着当前的槽函数肯定是结束了),再进行上述销毁操作!

当然,上述做法都是权宜之计,相比之下,Java/Python/Go 等全自动化垃圾回收更好用一些!

socket->connectToHost("127.0.0.1", 9090);

在这里插入图片描述
在这里插入图片描述

在Linux写的TCP的回显服务器的时候,遇到了一个问题,多个客户端同时访问的时候,就只会有一个生效;后来引入了多线程,每个客户端安排一个单独的
线程,每个客户端安排一个单独的线程,问题才得到改善

在这里插入图片描述

在Linux中,之所以出现上述问题,和TCP,和多线程都没啥关系。从来没有说法,说TCP服务器必须使用多线程编写!
之前存在这个问题的本质原因,是写了一个双重循环,里层循环没有及时结束,导致外层循环不能快速的第二次调用到accept,导致第二个客户端无法处理了!
引入多线程,本质上就是把双重循环,化简成为两个独立的循环。

而在咱们Qt的服务器中,其实一个循环都没写,是通过Qt内置的信号槽来驱动的!
信号槽机制很好的化简了咱们的程序!但是基本没有正经的服务器用Qt来写!

3.2 代码:

服务端:

#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, 9090);if (!ret) {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, [=](){// a)读取出请求数据,此处readAll 返回的是QByteArray, 通过赋值转成 QStringQString request = clientSocket->readAll();// b)根据请求处理响应const QString& response = process(request);// c) 把响应写回到客户端clientSocket->write(response.toUtf8());// d)把上述信息记录到日志中QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]"+ " req: " + request + ", resp:" + response;ui->listWidget->addItem(log);});// 3. 通过信号槽,来处理客户端断开连接的情况connect(clientSocket, &QTcpSocket::disconnected, this, [=](){// a) 把断开连接的连接的信息通过日志显示出来QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线!";ui->listWidget->addItem(log);
//        // b) 手动释放 clientSocket, 直接使用delete是下策,使用deleteLater 更加合适
//        delete clientSocket;clientSocket->deleteLater();});
}// 此处写的是回显服务器
QString Widget::process(const QString request)
{return request;
}

客户端:

 #include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 1. 设置窗口标题this->setWindowTitle("客户端");// 2. 创建socket对象实例socket = new QTcpSocket(this);// 3. 和服务器建立连接socket->connectToHost("127.0.0.1", 9090);// 调用者个函数,此时系统内核就会和对方的服务器之间进行三次握手了// 三次握手也是需要消耗一定的时间的// 4. 连接信号槽,处理响应connect(socket, &QTcpSocket::readyRead, this, [=](){// a) 读取出响应内容QString response = socket->readAll();// b) 把显示内容显示到界面上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("");
}

4. HTTP Client

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

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

HTTP 使用比 TCP/UDP 更多一些。
Qt中也提供了HTTP的客户端,HTTP协议本质上也就是基于TCP协议实现的,实现一个HTTP客户端/服务器,本质上就是基于TCP
socket 进行封装。 HTTP客户端:Qt只是提供了HTTP客户端,而没有提供HTTP服务器的库

4.1 核心API

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

  • QNetworkAccessManager 提供了HTTP的核心操作
    在这里插入图片描述

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

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

在这里插入图片描述

QVariant 表示一个“类型可变”的值,类似于 C 语言中的void*,泛型编程,虽然在C++中还是挺常见的,但是使用门槛还是比较高,里面有一些坑!

  • 其中的 QNetworkRequest::KnownHeaders 是一个枚举类型,常用取值:
    在这里插入图片描述
    QNetworkReply 表示一个HTTP响应,这个类同时也是QIODevice的子类
    在这里插入图片描述
    此外,QNetworkReply 还有一个重要的信号finished 会在客户端收到完整的响应数据之后触发!

4.2 代码示例

给服务器发送一个GET请求

此处显示的响应结果,大概率是一个HTML,QPlainTextEdit 来进行表示,能够看到响应的原始模样!

QTextEidit(天然支持对HTML的解析),会对HTML进行解析渲染,最终显示的效果就不是原始的HTML。QTextEdit
背后还做了很多工作,当得到的HTML比较大的时候,也会造成卡顿!

QNetworkReply* response = manager->get(request);

get本身不是阻塞函数,get只是负责发出去请求,不负责等待响应回来
finished 信号

在这里插入图片描述
代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QNetworkReply>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("客户端");manager = new QNetworkAccessManager(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 1. 获取到输入框中的URLQUrl 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());}// 还需要对 response 进行释放response->deleteLater();});
}

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

总结:

本文首先介绍了网络编程的基础知识,解释了网络编程中传输层的核心协议UDP和TCP,以及Qt框架如何通过提供相应的API简化了网络编程的过程。接着,文章详细讲解了UDP和TCP Socket编程,包括核心API的概述、如何编写带有界面的UDP回显服务器和客户端,以及如何实现TCP服务器和客户端的通信。此外,还介绍了Qt中的HTTP客户端编程,包括QNetworkAccessManagerQNetworkRequestQNetworkReply等关键类的作用和使用方式。

通过本文的学习,读者应该能够理解Qt网络编程的基本概念,掌握使用Qt进行UDP、TCP以及HTTP通信的方法,并能够根据实际需求编写网络应用程序。Qt的模块化设计和信号槽机制大大简化了网络编程的复杂性,使得开发者可以更加专注于业务逻辑的实现。最后,希望读者能够将本文的知识应用到实际项目中,提升网络编程的能力和效率。

相关文章:

【Qt】探索Qt网络编程:构建高效通信应用

文章目录 前言&#xff1a;1. Qt 网络编程介绍1.1 什么是网络编程&#xff1f;1.2 Qt的模块 2. UDP Socket2.1 核心 API 概述2.2 写一个带有界面的 Udp 回显服务器2.3 写一个带有界面的 Udp 客户端 3. TCP Socket3.1 核心 API 概述3.2 代码&#xff1a; 4. HTTP Client4.1 核心…...

【Android Studio】原生应用部署第三方插件(探针)

一、本地引入包流程 &#xff08;一&#xff09;本地引入包内容 &#xff08;二&#xff09;本地引入包操作步骤 将 【probe-android-sdk】目录里面所有的aar包复制到嵌码项目工程&#xff08;App级别&#xff09;的 libs 目录下 二、添加插件 &#xff08;一&#xff09;…...

嵌入式学习之路 15(C语言基础学习——指针操作一维字符型数组)

字符型数组的定义和初始化 char s[] "hello";&#xff1a;在栈上开辟空间并初始化。const char *p "hello";&#xff1a;指针 p 指向字符串常量区的 "hello"&#xff0c;只能读取不能修改。 指针变量的类型确定 指针变量的类型由其所指向的数据…...

C++ STL专题 list的底层实现

目录 1.模拟实现list 2.节点模板讲解 3.迭代器模板讲解 3.1为什么template 有三个类型参数 (1).class T (2).class ref (3).class ptr 3.2 *重载 3.3 ->重载 3.4 前置和后置的重载 3.5 前置--和--后置的重载 3.6 和!的重载 4. list模板讲解 4.1 begin()函数 …...

【JavaEE】线程池

目录 前言 什么是线程池 线程池的优点 ThreadPollExecutor中的构造方法 corePoolSize && maximumPoolSize keepAliveTime && unit workQueue threadFactory 如何在java中使用线程池 1.创建线程池对象 2.调用submit添加任务 3.调用shutdown关闭线程池…...

lvs实战项目-dr模式实现

一、环境准备 主机名IP地址router eth0&#xff1a;172.25.254.100 eth1&#xff1a;192.168.0.100 clienteth0&#xff1a;172.25.254.200lvseth1&#xff1a;192.168.0.50web1web2 1、client配置 [rootclient ~]# cat /etc/NetworkManager/system-connections/eth0.nmconne…...

JSONP跨域

1 概述 定义 json存在的意义&#xff1a; 不同类型的语言&#xff0c;都能识别json JSONP(JSON with Padding)是JSON的一种“使用模式”&#xff0c;可用于解决主流浏览器的跨域数据访问的问题。由于同源策略&#xff0c;一般来说位于 server1.example.com 的网页无法与不是 s…...

Linux--shell脚本语言—/—终章

一、shell函数 1、shell函数定义格式 参数说明&#xff1a; 1、可以带function fun() 定义&#xff0c;也可以直接fun() 定义,不带任何参数。 2、参数返回&#xff0c;可以显示加&#xff1a;return 返回&#xff0c;如果不加&#xff0c;将以最后一条命令运行结果&#xff…...

免费代理池是什么,如何使用代理IP进行网络爬虫?

互联网是一个庞大的数据集合体&#xff0c;网络信息资源丰富且繁杂&#xff0c;想要从中找到自己需要的信息要花费较多的时间。为了解决这个问题&#xff0c;网络爬虫技术应运而生&#xff0c;它的主要作用就是在海量的互联网信息中进行爬取&#xff0c;抓取有效信息并存储。然…...

CAN直接网络管理(20240805)

长安CAN网络管理规范 个人理解&#xff1a;管理CAN网络中各NM节点的工作模式&#xff08;状态&#xff09;&#xff1b; 1.术语定义 &#x1f449;节点地址&#xff1a;用于唯一标识网络中每个节点的单字节数字&#xff0c;取值范围是 0x00~0xFF。&#x1f449;状态迁移&#x…...

HTML5+CSS3笔记(Xmind格式):第二天

Xmind鸟瞰图&#xff1a; 简单文字总结&#xff1a; 新增选择器&#xff1a; 1.选择相邻兄弟 2.属性选择器 3.结构性伪类选择器 4.整体结构类型 5.标签结构类型 6.指定子元素的序号 7.文本选择伪元素 8.表单中使用的状态伪类选择器 9.内容…...

视频压缩文件太大了怎么缩小?6个视频压缩技巧,速度收藏起来!

高清视频文件&#xff0c;尤其是那些以 1080p 和 720p 清晰度为特征的视频&#xff0c;通常都拥有相当大的体积&#xff0c;会占据大量计算机存储空间。因此&#xff0c;为了更好地将它们进行分享和存储&#xff0c;您可能需要对它们进行压缩&#xff0c;以减小它们的尺寸。然而…...

Python接口自动化测试数据提取分析:Jmespath

1、引言 在处理JSON数据时&#xff0c;我们常常需要提取、筛选或者变换数据。手动编写这些操作的代码不仅繁琐&#xff0c;而且容易出错。Python作为一个功能强大的编程语言&#xff0c;拥有丰富的库和工具来处理这些数据。今天&#xff0c;将介绍一个实用的Python库——JMESP…...

特种设备作业叉车司机题库及答案

1.在我们平时工作中&#xff0c;经常接触的汽油、柴油、机油、油棉纱、木材等均为() A、助燃物质 B、可燃物质 C、着火源 参考答案:B 2.叉车满载行驶时&#xff0c;如合成重心靠后() A、有利于纵向稳定 B、有利于横向稳定 C、纵向和横向均有利 参考答案:A 3.蓄电池车行驶中放…...

Linux 操作系统速通

一、安装虚拟机 1. VmWare 安装下载 vmware workstation pro 16 下载 win R 输入 ncpa.cpl 确保网卡正常 2. CentOS 系统下载 CentOS 系统下载 将 CentOS 系统安装到虚拟机 3. 查看虚拟机 IP 命令 ifconfig 4. finalShell 安装下载 finalShell 下载 输入用户名一般是 ro…...

IIS漏洞大全(附修复方法)

IIS6.0 IlS Server 在 Web 服务扩展中开启了 WebDAV&#xff0c;配置了可以写入的权限&#xff0c;造成任意文件上传。 漏洞复现 fofa:"llS-6.0" or 本地搭建2003 server 1)开启 WebDAV 和写权限: 做好准备工作后开启环境&#xff0c;然后我们去访问配置的IP&#…...

HarmonyOS笔记3:从网络数据接口API获取数据

面向HarmonyOS的移动应用一般采用MVVM模式&#xff08;见参考文献【1】&#xff09;&#xff0c;其中&#xff1a; M&#xff08;Model层)&#xff1a;模型层&#xff0c;存储数据和相关逻辑的模型。它表示组件或其他相关业务逻辑之间传输的数据。Model是对原始数据的进一步处理…...

Mac 下生成core dump

mac下生成core dump 使用ulimit -c查看ulimit设置,显示unlimited表示开启,显示0表示关闭,通过ulimit -c unlimited打开设置; 但是这个只在当前窗口有效果。如果需要变成系统全局设置。 就需要去改/etc/profile文件&#xff0c;打开&#xff0c;然后加上ulimit -c unlimited就可…...

详解Xilinx FPGA高速串行收发器GTX/GTP(1)--SerDes和GTX的关系

目录 1、SerDes和GTX的关系 2、传输总线的变化 2.1、从串行到并行 2.2、从并行又回到串行 文章总目录点这里:《FPGA接口与协议》专栏的说明与导航 1、SerDes和GTX的关系 Hold On,这个系列文章不是讲GTX收发器的吗?怎么一开始就扯到SerDes上了?GTX和SerDes之间有…...

golang实现Digest认证鉴权接口

什么是Digest认证鉴权接口? Digest认证鉴权接口是一种基于摘要算法的身份验证方法,用于确保API请求的安全性。在实际应用中,常常使用HTTP协议的Digest认证鉴权接口来验证请求的合法性。下面是一种常见的Digest认证鉴权流程: 1. 客户端发送HTTP请求到服务器,请求接口资源…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...