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

【Linux C | 网络编程】进程池大文件传输的实现详解(三)

上一篇实现了进程池的小文件传输,使用自定义的协议,数据长度+数据本身,类似小火车的形式,可以很好的解决TCP“粘包”的问题。

【Linux C | 网络编程】进程池小文件传输的实现详解(二)

当文件的内容大小少于小火车车厢的时候,上述代码的表现是非常完美的。但是如果一旦文件长度大于火车车厢大小,那么上述代码就无能为力了。
那么当传出大文件的时候有哪些办法呢?

1.使用循环来传输

最自然的思路解决大文件问题就是使用循环机制:发送方使用一个循环来读取文件内容,每当读取一定字节的数据之后,将这些数据的大小和内容填充进小火车当中;接收方就不断的使用 recv 接收小火车的火车头和车厢,先读取4 个字节的火车头,再根据车厢长度接收后续内容。

对于大文件的传输先要获取大文件的长度的信息,这里可以使用fstat()函数。

服务端发送大文件的流程:

1.获取大文件的长度信息

2.使用一个小火车先发送大文件的,文件名长度+文件名内容

3.发送大文件的内容,先发文件内容的长度信息,然后使用小火车循环发送文件的内容

#include "process_pool.h"#define FILENAME "bigfile.avi"//sendn函数可以发送确定的字节数
//sockfd:通信套接字,buff:要发送的内容,len:要发送的内容字节数
int sendn(int sockfd, const void * buff, int len)
{int left = len;const char* pbuf = buff;int ret = -1;while(left > 0) {ret = send(sockfd, pbuf, left, 0);if(ret < 0) {perror("send");return -1;}left -= ret;pbuf += ret;}return len - left;
}int transferFile(int peerfd)
{//读取本地文件int fd = open(FILENAME, O_RDONLY);ERROR_CHECK(fd, -1, "open");//获取文件的长度struct stat st;memset(&st, 0, sizeof(st));fstat(fd, &st);char buff[100] = {0};int filelength = st.st_size;        //获取文件的大小printf("filelength: %d\n", filelength);//进行发送操作//1. 发送文件名train_t t;memset(&t, 0, sizeof(t));t.len = strlen(FILENAME);strcpy(t.buf, FILENAME);sendn(peerfd, &t, 4 + t.len);//2. 再发送文件内容//2.1 发送文件的长度sendn(peerfd, &filelength, sizeof(filelength));int ret = 0;int total = 0;//2.2 再发送文件内容while(total < filelength) {memset(&t, 0, sizeof(t));ret = read(fd, t.buf, 1000);        //每次从文件读取1000个字节的内容,放到一个小火车上if(ret > 0) {t.len = ret;                    //初始化小火车的车头长度//sendn函数确保 4 + t.len 个字节的数据能正常发送ret = sendn(peerfd, &t, 4 + t.len);if(ret < 0) {printf(">> exit while not send.\n");break;//发生了错误,就退出while循环}total += (ret - 4);             //已发送内容,不包括车头}}return 0;
}

服务端接受流程:

1.先接受文件名内容

2.接受文件的内容

#include <func.h>int main()
{//创建客户端的套接字int clientfd = socket(AF_INET, SOCK_STREAM, 0);ERROR_CHECK(clientfd, -1, "socket");struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));//指定使用的是IPv4的地址类型 AF_INETserveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(8080);serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//连接服务器int ret = connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));ERROR_CHECK(ret, -1, "connect");printf("connect success.\n");//进行文件的接收//1. 先接收文件的名字//1.1 先接收文件名的长度int length = 0;ret = recv(clientfd, &length, sizeof(length), 0);printf("filename length: %d\n", length);//1.2 再接收文件名本身char buff[1000] = {0};ret = recv(clientfd, buff, length, 0);printf("1 recv ret: %d\n", ret);int fd = open(buff, O_CREAT|O_RDWR, 0644);ERROR_CHECK(fd, -1, "open");//2. 再接收文件的内容//2.1 先接收文件内容的长度ret = recv(clientfd, &length, sizeof(length), 0);printf("fileconent length: %d\n", length);int total = 0;int len = 0;//每一个分片的长度//2.2 再接收文件内容本身while(total < length) {recv(clientfd, &len, sizeof(len), 0);if(len != 1000) {printf("slice len: %d\n", len);//printf("total: %d bytes.\n", total);}memset(buff, 0, sizeof(buff));//recv函数无法保证每一次接收都能获取len个字节的长度//因此出现了读取长度异常的情况ret = recv(clientfd, buff, len, 0);// ret <= len//printf("slice %d bytes.\n", ret);if(ret > 0) {total += ret;write(fd, buff, ret);//写入本地文件}}close(fd);close(clientfd);return 0;
}

使用md5算法计算哈希值验证文件的正确性:

# client
$md5sum file2
# 计算md5码需要等待一段时间
8e9d11a16f03372c82c5134278a0bd7d file2
# server
$md5sum file2
8e9d11a16f03372c82c5134278a0bd7d file2

存在问题:

一般情况下上述方法确实可以传输完整的文件,但是存在一个大bug:recv函数无法保证每一次接收都能获取len个字节的长度,因此出现了读取长度异常的情况。

比如:内容只传输了一半,后续的数据就直接被当成长度了 出现了长度的偏差,导致传输出现问题,下一次循环开始时,本来希望读取的是长度信息,但其实读取的是内容,从而导致长度数据出现问题。

原因是:TCP是一种流式协议,它只能负责每个报文可靠有序地发送和接收,但是并不能保证传输到网络缓冲区当中的就是完整的一个小火车。这样就有可能会到导致数据读取问题,下面就举一个例子:假设发送方需要传输两个小火车,其中每个 车厢都是1000个字节,那么自然火车头都是4个字节,里面各自存储了1000 (当然是二进制形式),当 两个小火车发送到socket的时候,由于TCP是流式协议,所以小火车与小火车之间边界就不见了,到了 接收方这边, recv可能会先收到4个字节确定第一个小火车的车厢长度,再收到800字节,此时继续再 recv就会从第一个火车车厢中继续取出4个字节,那这4个字节显然就不是第二个小火车的车厢长度 了。

有以下解决方案:

1.1使用MSG_WAITALL(接收完整的长度数据)

recv函数用于从套接字接收数据。它的第四个参数是一个标志,用来控制接收操作的行为。

  • 如果将第四个参数设置为0或者使用MSG_WAITALL标志,recv函数会一直阻塞,直到接收到指定长度的数据。
  • 如果接收到的数据长度小于请求的长度,recv函数会一直阻塞直到接收完指定长度的数据或者发生错误。
#include <func.h>int main()
{//创建客户端的套接字int clientfd = socket(AF_INET, SOCK_STREAM, 0);ERROR_CHECK(clientfd, -1, "socket");struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));//指定使用的是IPv4的地址类型 AF_INETserveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(8080);serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//连接服务器int ret = connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));ERROR_CHECK(ret, -1, "connect");printf("connect success.\n");//进行文件的接收//1. 先接收文件的名字//1.1 先接收文件名的长度int length = 0;ret = recv(clientfd, &length, sizeof(length), 0);printf("filename length: %d\n", length);//1.2 再接收文件名本身char buff[1000] = {0};ret = recv(clientfd, buff, length, 0);printf("1 recv ret: %d\n", ret);int fd = open(buff, O_CREAT|O_RDWR, 0644);ERROR_CHECK(fd, -1, "open");//2. 再接收文件的内容//2.1 先接收文件内容的长度ret = recv(clientfd, &length, sizeof(length), 0);printf("fileconent length: %d\n", length);int total = 0;int len = 0;//每一个分片的长度//2.2 再接收文件内容本身while(total < length) {recv(clientfd, &len, sizeof(len), MSG_WAITALL);if(len != 1000) {printf("slice len: %d\n", len);//printf("total: %d bytes.\n", total);}memset(buff, 0, sizeof(buff));//将recv函数的第四个参数设置为MSG_WAITALL之后,//表示必须要接收len个字节的数据之后,才会返回ret = recv(clientfd, buff, len, MSG_WAITALL);// ret <= len//printf("slice %d bytes.\n", ret);if(ret > 0) {total += ret;write(fd, buff, ret);//写入本地文件}}close(fd);close(clientfd);return 0;
}

1.2每次循环发送和接受指定长度的数据

服务端发来多少客户端就接受多少,服务端封装一个发送指定大小数据的函数,客户端封装一个接收指定大小数据的函数。

客户端代码:

#include <func.h>//接收确定的字节数的数据
//sockfd:通信套接字,buff:接收的内容,len:接收内容的长度
int recvn(int sockfd, void * buff, int len)
{int left = len;char * pbuf = buff;int ret = -1;while(left > 0) {ret = recv(sockfd, pbuf, left, 0);if(ret == 0) {break;} else if(ret < 0) {perror("recv");return -1;}left -= ret;pbuf += ret;}return len - left;
}int main()
{//创建客户端的套接字int clientfd = socket(AF_INET, SOCK_STREAM, 0);ERROR_CHECK(clientfd, -1, "socket");struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));//指定使用的是IPv4的地址类型 AF_INETserveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(8080);serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//连接服务器int ret = connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));ERROR_CHECK(ret, -1, "connect");printf("connect success.\n");//进行文件的接收//1. 先接收文件的名字//1.1 先接收文件名的长度int length = 0;ret = recvn(clientfd, &length, sizeof(length));printf("filename length: %d\n", length);//1.2 再接收文件名本身char buff[1000] = {0};ret = recvn(clientfd, buff, length);printf("1 recv ret: %d\n", ret);int fd = open(buff, O_CREAT|O_RDWR, 0644);ERROR_CHECK(fd, -1, "open");//2. 再接收文件的内容//2.1 先接收文件内容的长度ret = recvn(clientfd, &length, sizeof(length));printf("fileconent length: %d\n", length);int total = 0;int len = 0;//每一个分片的长度//2.2 再接收文件内容本身while(total < length) {ret = recvn(clientfd, &len, sizeof(len));if(len != 1000) {printf("slice len: %d\n", len);//printf("total: %d bytes.\n", total);}memset(buff, 0, sizeof(buff));ret = recvn(clientfd, buff, len);if(ret != 1000) {//printf("slice %d bytes.\n", ret);}if(ret > 0) {total += ret;write(fd, buff, ret);//写入本地文件}}close(fd);close(clientfd);return 0;
}

1.3客户端断开连接   --- SIGPIPE信号的处理

现象:客户端断开连接时,导致服务器中的某一个子进程挂掉了,变成了僵尸进程,导致父子进程通信的管道被关闭了。而父进程一直监听该管道,因此epoll_wait不断返回,才有了服务器疯狂打印的情况出现。

通常情况下,如果程序向一个已经关闭写入的管道写数据,操作系统会发送 SIGPIPE 信号给进程,而默认的行为是终止该进程。但是有时候我们希望在这种情况下不让程序退出,而是希望处理其他错误或者采取其他措施。这时候就可以通过 signal(SIGPIPE, SIG_IGN); 来忽略 SIGPIPE 信号,让程序继续执行下去。

当客户端关闭时,服务器先执行第一次send操作,客户端会返回一个RST报文 当服务器的子进程再次发送第二次send操作时,会接收到SIGPIPE信号,导致子进程奔溃,从而导致子进程与父进程通信的管道也会关掉。

解决该问题:只需要让子进程忽略掉SIGPIPE信号即可。

1.4客户端打印文件传输的进度条

#include <func.h>//接收确定的字节数的数据
int recvn(int sockfd, void * buff, int len)
{int left = len;char * pbuf = buff;int ret = -1;while(left > 0) {ret = recv(sockfd, pbuf, left, 0);if(ret == 0) {break;} else if(ret < 0) {perror("recv");return -1;}left -= ret;pbuf += ret;}return len - left;
}int main()
{//创建客户端的套接字int clientfd = socket(AF_INET, SOCK_STREAM, 0);ERROR_CHECK(clientfd, -1, "socket");struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));//指定使用的是IPv4的地址类型 AF_INETserveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(8080);serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//serveraddr.sin_addr.s_addr = inet_addr("192.168.30.129");//连接服务器int ret = connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));ERROR_CHECK(ret, -1, "connect");printf("connect success.\n");//进行文件的接收//1. 先接收文件的名字//1.1 先接收文件名的长度int length = 0;ret = recvn(clientfd, &length, sizeof(length));printf("filename length: %d\n", length);//1.2 再接收文件名本身char buff[1000] = {0};ret = recvn(clientfd, buff, length);printf("1 recv ret: %d\n", ret);int fd = open(buff, O_CREAT|O_RDWR, 0644);ERROR_CHECK(fd, -1, "open");//2. 再接收文件的内容//2.1 先接收文件内容的长度ret = recvn(clientfd, &length, sizeof(length));printf("fileconent length: %d\n", length);int segment = length / 100;//百分之一的长度int lastSize = 0;#if 1int curSize = 0;int len = 0;//每一个分片的长度//2.2 再接收文件内容本身while(curSize < length) {ret = recvn(clientfd, &len, sizeof(len));memset(buff, 0, sizeof(buff));ret = recvn(clientfd, buff, len);if(ret > 0) {curSize += ret;write(fd, buff, ret);//写入本地文件if(curSize - lastSize > segment) {      //每百分之一打印一次//打印进度条printf("has complete %5.2f%%\r", (double)100 * curSize / length);fflush(stdout);lastSize = curSize;//更新上一次打印百分比时的长度}}}printf("has complete 100.00%%\n");
#endifclose(fd);close(clientfd);return 0;
}

相关文章:

【Linux C | 网络编程】进程池大文件传输的实现详解(三)

上一篇实现了进程池的小文件传输&#xff0c;使用自定义的协议&#xff0c;数据长度数据本身&#xff0c;类似小火车的形式&#xff0c;可以很好的解决TCP“粘包”的问题。 【Linux C | 网络编程】进程池小文件传输的实现详解&#xff08;二&#xff09; 当文件的内容大小少于…...

Mac如何通过SSH连接Github

目录 前言 一、实现步骤 1.生成 SSH 密钥对 2.添加 SSH 密钥到 GitHub&#xff1a; 3.配置 SSH 连接 1.更新远程仓库 URL 2.测试 SSH 连接 前言 GitHub 在 2021 年 8 月 13 日停止了对使用密码进行身份验证的支持。因此&#xff0c;你需要使用其他认证方式&#xff0c;如…...

成就巴西休闲游戏如何借助Google谷歌广告投放优势

在探讨巴西休闲游戏如何借助谷歌广告投放优势实现市场扩张的过程中&#xff0c;我们不得不深入分析巴西市场的独特属性、休闲游戏的兴起背景&#xff0c;以及谷歌广告平台在全球范围内的强大影响力。近年来&#xff0c;随着移动游戏市场的快速发展&#xff0c;特别是中轻度休闲…...

利用python检查磁盘空间使用情况

目录 一.前言 二.使用的库介绍 三.代码实现以及解析 3.1导入模块 3.2邮件发送函数 send_email 3.3检查磁盘空间函数 check_and_clean_disk 3.4主程序逻辑 四.致谢 一.前言 在信息技术飞速发展的今天&#xff0c;数据量的激增使得磁盘空间管理成为系统运维中的一项基…...

卷积神经网络(五)---图像增强的方法

前面的部分专注于卷积神经网络的层结构介绍&#xff0c;同时还介绍了到目前为止比较出名的卷积神经网络&#xff0c;接着使用比较复杂的卷积神经网络提高了 MNIST 数据集的准确率。下面将从另外的角度——图像增强的方面入手&#xff0c;提高模型的准确率和泛化能力。 一直以来…...

矩阵常见分解算法及其在SLAM中的应用

文章目录 常见特殊矩阵定义Cholesky分解&#xff08;正定Hermittian矩阵&#xff0c;分解结果唯一&#xff09;Cholesky分解应用 SVD分解&#xff08;将singularvalues排序后分解唯一&#xff09;SVD 分解的应用&#xff08;任意矩阵&#xff09; QR分解&#xff08;任意矩阵&a…...

【排序】快速排序详解

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;排序 个人主页&#xff1a;Celias blog~ 一、快速排序的思想 快速排序的核心思想是&#xff1a; 选定一个…...

贪心算法总结(2)

一、买卖股票的最佳时机 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int maxProfit(vector<int>& prices) {int miniINT_MAX;int ret0;for(int&price:prices){//遍历的时候&#xff0c;我们随时去更新最小的值&#xff0c;然后让每一位…...

弘景光电:技术实力与创新驱动并进

在光学镜头及摄像模组产品领域&#xff0c;广东弘景光电科技股份有限公司&#xff08;以下简称“弘景光电”&#xff09;无疑是一颗耀眼的明星。自成立以来&#xff0c;弘景光电凭借其强大的研发实力、卓越的产品性能、精密的制造工艺以及严格的质量管理体系&#xff0c;在光学…...

2024年7月23日~2024年7月29日周报

目录 一、前言 二、完成情况 2.1 一种具有边缘增强特点的医学图像分割网络 2.2 融合边缘增强注意力机制和 U-Net 网络的医学图像分割 2.3 遇到的困难 三、下周计划 一、前言 上周参加了一些师兄师姐的论文讨论会议&#xff0c;并完成了初稿。 本周继续修改论文&#xff0…...

M3U8流视频数据爬虫

M3U8流视频数据爬虫 HLS技术介绍 现在大部分视频客户端都采用HTTP Live Streaming&#xff08;HLS&#xff0c;Apple为了提高流播效率开发的技术&#xff09;&#xff0c;而不是直接播放MP4等视频文件。HLS技术的特点是将流媒体切分为若干【TS片段】&#xff08;比如几秒一段…...

保护您的数字财富:模块化沙箱在源代码防泄露中的突破

在数字化浪潮中&#xff0c;企业面临着前所未有的数据安全挑战。源代码、商业机密、客户数据……这些宝贵的数字资产一旦泄露&#xff0c;后果不堪设想。SDC沙盒防泄密系统&#xff0c;以其卓越的技术实力和创新的解决方案&#xff0c;为企业提供了一个坚不可摧的安全屏障。 核…...

FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析

一、引言 AVIOContext是FFmpeg&#xff08;本文演示用的FFmpeg源码版本为5.0.3&#xff09;中的字节流上下文结构体&#xff0c;用来管理输入输出数据。打开一个媒体文件的时候&#xff0c;需要先把数据从硬盘读到缓冲区&#xff0c;然后会用到AVIOContext中的如下成员&#x…...

如何使用 API 查看极狐GitLab 镜像仓库中的镜像?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…...

软件-vscode-plantUML-IDEA

文章目录 vscode基础命令 实操1. vscode实现springboot项目搭建 &#xff08;包括spring data jpa和sqlLite连接&#xff09; PlantUMLIDEA下载及安装Eval Reset插件配置修改IDEA创建项目的默认目录IDEA配置gitIDEA翻译插件translationIDEA断点调试IDEA全局搜索快捷键不能使用代…...

ES6语法详解,面试必会,通俗易懂版

目录 Set的基本使用WeakSet 使用Set 和 WeakSet 区别内存泄漏示例&#xff1a;使用普通 Set 保存 DOM 节点如何避免这个内存泄漏MapWeakMap 的使用 Set的基本使用 在ES6之前&#xff0c;我们存储数据的结构主要有两种&#xff1a;数组、对象。 在ES6中新增了另外两种数据结构&a…...

CTFshow--Web--代码审计

目录 web301 web302 web303 web304 web305 web306 web307 web308 web309 web310 web301 开始一个登录框, 下意识sql尝试一下 发现 1 的时候会到一个 checklogin.php 的路径下, 但啥也没有 好吧, 这是要审计代码的 ,下载好源码, 开始审计 看了一下源码 , 应该就是sql…...

Java语言程序设计——篇十(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 接口介绍 接口概述接口定义接口的实现实战演练 &#x1f445;接口的继承实战演练实战演练 接口的类型常量实战演练 静态方法默认方法解决默认方…...

Qt对比MFC优势

从Qt小白到现在使用了有四年的时间&#xff0c;之前也搞过MFC,WinForm,基本上都是桌面的框架&#xff0c; 从难易程度看MFC>QT>WinForm; 运行的效率上来看MFC>QT>WinForm; 开发效率上WinForm>QT>MFC; 跨平台Qt首选&#xff1b; 界面的美观难易程度Qt>…...

RuntimeError: No CUDA GPUs are available

RuntimeError: No CUDA GPUs are available 目录 RuntimeError: No CUDA GPUs are available 【常见模块错误】 【解决方案】 解决步骤如下&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...