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

【解决方案】微信小程序如何使用 ProtoBuf 进行 WebSocket 通信

前言

故事背景

简单说下背景,项目中需要用 ProtoBuf 协议转换请求参数,并通过 WebSocket 进行双向通信。重点!一个是 web端(Vue3 + TS),一个是微信小程序端(原生 + JS)。

剧情发展

一开始,web端通过 ts-proto 这个库进行开发,问题倒是不大。将跑通的这部分代码进行 ts 转 js 之后挪用到微信小程序端的时候,奇奇怪怪的问题就出现了。

灵异事件(一)

问题描述

问题1: 发送文本消息(电脑上正常,手机上直接发送失败)

问题描述:
发送文本消息的情况,在电脑上,也就是微信开发者工具中的 模拟器 上,是可以正常通信的;但是在手机上,也就是预览的时候,发送文本是直接无法发送, 报错提示大致意思就是方法执行失败,可以很容易定位到在数据转换(encode)的这个过程有问题

问题截图:

(1)电脑端 - 正确:

模拟器-发送文本消息-正常

(2)手机端 - 报错:

手机端-发送文本消息-失败

问题2:发送语音消息(电脑能发但是服务端解析失败,手机上直接就发不了)

问题描述:
最离谱,也是最坑的事件来了。
当电脑端发送语音消息的时候,WS可以正常来回通信,但是服务端拿到的语音数据解析异常,导致语音文本识别失败,并且这段语音数据生成的url是无法播放的。
当手机端发送语音消息的时候,表现就和上面的发送文本消息一样,直接发送不出去,这个倒是可以接受,可以直接定位到前端数据转换(encode)的过程有问题。

问题截图:

(1)电脑端 - 发送成功,但是服务端获取到的语音数据是异常的,导致语音转文本失败:

电脑-发送语音-发成功但语音解析失败

(2)手机端 - 报错:

手机端-发语音-失败

解决方案

1、分析原因

通过在项目中 断点调试(或者 console.log) 的方法,可以很快定位到是 proto 文件生成的 JS 文件中的变量方法 encode 在小程序端无效。注意:这个方法在浏览器web页面上是可行的!所以,大概率是由于宿主环境不同导致转编译方法的一些内部依赖无效。毕竟,web端用的第三方库主要是针对浏览器环境的,并没有明确说支持微信端
回顾一下 web端项目引用的第三方库是 TS 版本的 protobuf —— ts-proto 那么以上的那个推测原因就更有可能了。

所以,第一步,在小程序端重新引用 JS 版本的 protobuf —— protobufjs

2、npm包介绍

这部分是最精彩的部分,所以单独拎出来,作为第二步讲解。

当我们用 protobufjs 这个库的时候,需要两样东西,一个是代码中需要引用的 protobuf 本人,一个是用来转换 xxx.proto 文件用的脚本命令 pbjs 也就是我下面截图中提到的 protobufjs-cli

当我们打开 protobufjs 这个使用教程的时候,会看到下面这个安装指引。

protobufjs-Installation

这里需要重点说明一下,之前只要执行 npm install protobufjs --save 这端安装脚本之后,就可以使用 pbjs 命令的,但是!改了!一切都变了!请看官方说明—— pbjs-for-javascript

pbjs-for-javascript

这里重点吐槽一下,上面截图中的 its own package 点过去还是个404页面!所以,这里我们只能通过 protobufjs 在安装指引中提到的 protobufjs-cli 联想推测应该指的就是这个库了!

protobuf.js-cli

3、具体步骤

综上所述,你要做的就是:

步骤一:安装 npm 包
  1. 在你的微信小程序项目中,终端打开,执行命令如下:
npm install protobufjs --save
  1. 打开你的电脑终端(这里我以mac为例),执行命令如下:
sudo npm install -g protobufjs-cli

安装完之后,可以执行一下 pbjs 是否可用,正常输出如下:
在这里插入图片描述

步骤二:转换 proto 文件为 js 文件

通过上面安装的库,我们继续使用 pbjs 命令来生成 xxx.proto 对应的 JS文件,例如我这个项目使用的命令如下:

pbjs -t static-module -w commonjs -o ./protobuf/proto/base.js ./protobuf/proto/base.proto

上面这行命令你要改的就是把 输入、输出的文件路径 改成自己项目的路径即可。具体的参数介绍,务必去官网 pbjs-for-javascript 学习了解一下,知其然,知其所以然!

步骤三:修改生成的 xxx.js 文件

通过 pbjs 生成的 JS文件 还没完事,还需要再改一下这份 JS文件 内部的一行代码,具体如下:
pbjs-生成的js文件

步骤四:使用生成的 xxx.js 文件

在我们的业务代码中使用通过 pbjs 生成的 JS文件 ,具体细节如下:

1、导入 protobuf 的方法
import方法
2、使用 protobuf 实例对象中的属性与方法(encode、decode这些)
encode方法

3、本来这部分是要放到 灵异事件2 去讲解引出的,为了确保上下文的完整性,这里直接交代结果了。
ArrayBuff转换

关键代码:

const _xxxArrayBuffer_ = Uint8Array.from(_xxxUint8Array_).buffer;

重点说明:

因为我们用的 protobuf 这个库是通过 Uint8Array 进行 encode 和 decode 等一些列操作的;但是!微信小程序 WebSocket通信 并不支持 Uint8Array 数据,所以我们需要在发起请求之前对数据进行一个转换处理——将 Uint8Array 转成普通的 ArrayBuffer !


截止目前,问题已经解决。下文将继续分享解决过程中遇到的问题,以及涉及到的知识点、参考资料。

灵异事件(二)

这里让我们回到上面 解决方案 / 具体步骤 / 步骤四:使用生成的 xxx.js 文件 当我们通过网上教程正确安装并使用 protobufjs 时,发现离谱的事情又来了!

问题描述

简言之,就是电脑上可以,手机上不行。

问题1:

发送文本消息的时候,电脑端的模拟器上是可行的,一切都正常;手机端发送 websocket 请求时报错,大致意思是不支持的数据类型(fail invalid data type)。

问题2:

发送语音消息的时候,电脑端的模拟器上是 半可行的 ,注意这是最坑爹的,因为这个表现直接误导了问题定位方向 。所谓的 半可行的 就是和上面的 灵异事件(一)/ 问题2:发送语音消息(电脑能发但是服务端解析失败,手机上直接就发不了) 一样,电脑端的模拟器上表现:请求是发出去了,但是服务端获取到的语音数据是异常的,无法播放、无法识别语音文本内容。手机端表现:和上面的问题1一致,也是 websocket 请求失败(fail invalid data type)。

问题截图

(1)电脑端 - 发送文本成功,截图如下:

电脑端-正常

(2)手机端 - websocket 请求失败,截图如下:

小程序端-异常

解决方案

上文中已经交代过了,所以这里知识将上面的内容复制粘贴了一下,不介意的话,可以再看一遍。

本来这部分是要放到 灵异事件2 去讲解引出的,为了确保上下文的完整性, 这里直接交代结果了。
ArrayBuff转换

关键代码:

const _xxxArrayBuffer_ = Uint8Array.from(_xxxUint8Array_).buffer;

重点说明:

因为我们用的 protobuf 这个库是通过 Uint8Array 进行 encode 和 decode 等一些列操作的;但是!微信小程序 WebSocket通信 并不支持 Uint8Array 数据,所以我们需要在发起请求之前对数据进行一个转换处理——将 Uint8Array 转成普通的 ArrayBuffer !


知识点

1、ArrayBuffer、Uint8Array

定义

ArrayBuffer 和 Uint8Array 是 JavaScript 中用于处理二进制数据的两种不同类型的数据结构。

关系

Uint8Array是一种TypedArray(类型化数组),它是基于ArrayBuffer对象来构建的。ArrayBuffer是一个数据存储区,表示一段固定长度的二进制数据,而Uint8Array提供了一种视图,用于以特定的格式(在Uint8Array的情况下是无符号 8 位整数)来访问和操作ArrayBuffer中的数据。

简单地说,ArrayBuffer是底层的数据存储,Uint8Array是操作和访问这些存储数据的一种方式,可以将ArrayBuffer看作是一块内存区域,而Uint8Array则是在这块内存区域上的一种数据解析和操作工具。

区别

1、操作数据的方式:

ArrayBuffer: 由于ArrayBuffer本身没有操作数据的方法,所以不能直接对其存储的数据进行读写操作。如果要操作ArrayBuffer中的数据,必须通过视图(如Uint8Array等类型化数组视图或者DataView)来进行。

Uint8Array: Uint8Array提供了丰富的数组方法来操作数据,因为它将数据视为数组。例如,可以使用索引来访问和修改元素,像uint8Array[0]= 25;这样的操作就可以将Uint8Array视图中的第一个元素(对应ArrayBuffer中的第一个字节)设置为 25。同时,它还支持数组的遍历方法,如forEach、map等。

2、用途:

ArrayBuffer: 常用于在底层存储二进制数据,比如从网络接收的文件数据、图像数据、音频数据等原始字节流。它是一种通用的、原始的数据存储机制,在涉及到与外部数据源进行二进制数据交互时非常有用。

Uint8Array: 适合处理字节级别的数据,比如对二进制数据进行按字节的操作、解析简单的二进制协议等。因为它将数据视为无符号 8 位整数数组,所以在处理字节操作频繁的场景(如加密算法中的字节处理、简单的文件格式解析等)下更加方便和直观。

2、Protobuf

定义

Protocol Buffers(简称 Protobuf)是 Google 开发的一种数据序列化格式,用于将结构化数据序列化和反序列化。它类似于XML或JSON,但更小、更快、也更简单。Protobuf的设计初衷是为了解决通信协议和数据存储格式的问题,使得在多种编程语言之间高效地交换结构化数据成为可能。

数据格式特点

1、高效性

空间效率高: Protobuf 序列化后的数据格式紧凑,相比其他文本格式(如 XML、JSON),在存储和传输时占用更少的空间。例如,对于包含相同信息的整数、字符串等数据,Protobuf 序列化后的字节数通常远小于 JSON 格式。

时间效率高: 序列化和反序列化速度快,因为 Protobuf 使用了二进制格式,并且对数据的编码和解码进行了优化。在处理大量数据时,其性能优势尤为明显。

2、跨语言和平台支持

Protobuf 支持多种编程语言,包括但不限于 Java、C++、Python、Go、JavaScript 等。这意味着在一个用 Java 编写的服务端程序中序列化的数据,可以在一个用 Python 编写的客户端程序中准确地反序列化出来,只要它们使用的 Protobuf 消息定义是一致的。

可以在不同的操作系统(如 Windows、Linux、macOS)和硬件平台上实现无缝的数据交换。

3、可扩展性和兼容性

向前兼容: 当对已有的数据结构进行扩展(如添加新的字段)时,老版本的程序仍然能够正确地读取和处理大部分数据,不会因为新字段的添加而完全失效。

向后兼容: 新的程序也能够读取和处理老版本数据结构序列化的数据,忽略新增字段即可。这种兼容性使得在分布式系统和长期运行的项目中升级数据结构变得更加容易。

使用流程

定义消息类型: 在 .proto 文件中定义数据结构的消息类型,如上面的 Person 消息。

生成代码: 使用 Protobuf 编译器(不同语言有各自的编译器插件)根据 .proto 文件生成对应语言的代码。例如,对于 Java 语言,会生成包含 Person 类的代码,这个类中包含了对消息中各个字段的访问和操作方法。

序列化和反序列化数据: 在程序中使用生成的代码,创建消息对象,填充数据后进行序列化,然后可以将序列化的数据传输或存储。接收方获取到数据后,使用相同的代码进行反序列化,恢复出原始的数据结构。


参考资料

  • protobufjs
  • ts-proto
  • pbjs-for-javascript
  • Language Guide (proto 3)
  • 如何在前端中使用protobuf(vue篇)
  • 微信小程序使用protobuf
  • 小程序websocket 通讯 发送 protobuf数据 Uint8Array 异常?

最后

以上内容主要是介绍了 在微信小程序端通过 WebSocket 用 Protobuf 协议进行前后端通信的解决方案

其实,在整个项目背景下(实现语音聊天功能),实际上还涉及到很多较为复杂的微信小程序 API 操作:

  • 录音功能
  • WebSocket 任务
  • 读取本地文件

关于微信小程序接口的注意事项

1、注意录音格式

直接上代码(仅供参考):

recorderManager.start({// 采样率(pc不支持)sampleRate: 16000,// 编码码率(默认就是 48000)encodeBitRate: 48000,// 音频格式(默认是 aac)format: 'wav',success: () => {// do sth.}
});

文档地址: RecorderManager.start

关键参数介绍:
音频格式
采样率与编码码率限制

2、注意读取本地文件的编码格式

这是清理后的伪代码(仅供参考):

/** 录音结束后的回调函数 */
function recorderOnStopHandler(res) {const { tempFilePath, duration } = res;const { friendUserId } = this.data;// 发送 WS 请求(语音消息)const fs = wx.getFileSystemManager();fs.readFile({filePath: tempFilePath,// encoding: 指定读取文件的字符编码,如果不传 encoding,则以 ArrayBuffer 格式读取文件的二进制内容// encoding 不要设置,默认就是 ArrayBuffer// encoding: 'binary', ———— 不要用这个!success: (res) => {// 读取完成后,res.data 包含文件内容的二进制字符串const arrayBuffer = res.data;// 转成 protobuf 需要的 uint8Arrayconst uint8Array = new Uint8Array(arrayBuffer);// 创建消息对象const payload = {"friendUserId": friendUserId,"duration": duration,"audio": uint8Array};// 序列化消息const paramsU8Array = ChatAudioParams.encode(payload).finish();// 这里还不能直接发送 - 需要转成 arraybuff// this.wsSend(buffer2);// 微信小程序的通信数据不支持 Uint8Array >>> Uint8Array 转出 arraybuffconst paramsBuffer = Uint8Array.from(paramsU8Array).buffer;// 发送 buffer 到服务器this.wsSend(paramsBuffer);},fail: (err) => {console.error('读取文件失败', err);}});
}

文档地址: FileSystemManager.readFile

关键参数介绍:

encoding参数介绍

3、关于微信开发者工具的坑

上文虽然解决了 实际问题 ,但是 微信开发者工具模拟器 上发语音还是有问题,发送文本是正常的,但是发送语音的音频数据异常,服务端无法识别语音内容,同时这段音频在电脑上是可以播放的,但是手机上无法播放。大致原因就是电脑端模拟器上音频数据在 encode 之后的数据格式异常!

在这里插入图片描述

END.

相关文章:

【解决方案】微信小程序如何使用 ProtoBuf 进行 WebSocket 通信

前言 故事背景 简单说下背景,项目中需要用 ProtoBuf 协议转换请求参数,并通过 WebSocket 进行双向通信。重点!一个是 web端(Vue3 TS),一个是微信小程序端(原生 JS)。 剧情发展 …...

独立游戏开发者面临的挑战与困境

在当今竞争激烈的游戏市场中,独立游戏开发者面临着诸多挑战与困境。从游戏版号申请到游戏被抄袭,再到产品同质化以及流量获取难题,乃至外包内卷现象,每一个环节都考验着开发者的智慧与毅力。以下是对这些挑战与闲境的详细分析。 …...

KVM 虚拟机Anolis OS 8.9 下利用宝塔面板中的 Docker 配置 Nextcloud + onlyoffice

第一部分:安装配置 nextcloud 准备 (1)启动一个 Anolis OS 8.9 虚拟机,见下图。该虚拟机为 anlisos8…0.2 虚拟机的 ssh、hostname 、IP地址都已配置好。 (2)宝塔面板也已安装好docker 一、环境 do…...

串口扫盲TTL,TX/TR/GND

1. 串口扫盲TTL,TX/TR/GND 1. 串口扫盲TTL,TX/TR/GND 1.1. TTL1.2. USB转TTL1.3. 串口通信1.4. 引脚缩写1.5. 参考资料 1.1. TTL TX(TXD) 来源于 Transmit 一词,意思为发送,发射RX(RXD) 来源于 Receive 一词 意思为接收,收到GND 地线&…...

Python酷库之旅-第三方库Pandas(181)

目录 一、用法精讲 836、pandas.api.types.is_file_like函数 836-1、语法 836-2、参数 836-3、功能 836-4、返回值 836-5、说明 836-6、用法 836-6-1、数据准备 836-6-2、代码示例 836-6-3、结果输出 837、pandas.api.types.is_list_like函数 837-1、语法 837-2、…...

Python数据分析NumPy和pandas(十七、pandas 二进制格式文件处理)

以二进制格式存储(或序列化)数据的一种简单方法是使用 Python 的内置 pickle 模块。同时,pandas 构造的对象都有一个 to_pickle 方法,该方法以 pickle 格式将数据写入磁盘。 我们先把之前示例用到的ex1.csv文件加载到pandas对象中…...

matlab计算相关物理参数

function Rx1Jetfire1_1(di,Ct,Tf,Tj,alpha,Ma,Mf,RH,P0,P,k,Cd,elta,deltaHc,tau,directory) % 一共15个独立变量,为了方便输入修改,所有变量存入Jetfire1_1excel表, % dj为孔口直径,m;Ct为燃料空气混合摩尔系数,可…...

nmcli、ip、ifcfg配置网络区分方法

文章目录 一、检查NetworkManager状态使用nmcli命令:检查NetworkManager服务状态: 二、检查ip命令的使用三、检查ifcfg文件查看/etc/sysconfig/network-scripts/目录:查看/etc/network/interfaces文件(针对Debian系)&a…...

第四届智能电力与系统国际学术会议(ICIPS 2024)

文章目录 一、会议详情二、重要信息三、大会介绍四、出席嘉宾五、征稿主题六、咨询 一、会议详情 二、重要信息 大会官网:https://ais.cn/u/vEbMBz提交检索:EI Compendex、IEEE Xplore、Scopus 三、大会介绍 四、出席嘉宾 五、征稿主题 如想"投稿…...

区块链样题第4套解析 后端应用开发部分

任务3-2:区块链应用后端开发 使用JAVA-SDK与区块链进行交互,通过solc2Java工具将Solidity智能合约转译为可供Java调用的文件,实现区块链编程。 前言:题目只是单纯考了对于fisco-java-sdk的简单使用 教程参考: 1.这边建议还是学习完JavaWeb课程。 黑马程序员JavaWeb...

C语言实现408考研真题2016年43题

#include <iostream> // 定义分区函数&#xff0c;返回两个子数组之和的差值 int setPartition(int a[], int n) { int pivotkey, low 0, low0 0, high n - 1, high0 n - 1, flag 1, k n / 2, i; int s1 0, s2 0; // 当low等于k-1&#xff0c;…...

2024年,Rust开发语言,现在怎么样了?

Rust开发语言有着一些其他语言明显的优势&#xff0c;但也充满着争议&#xff0c;难上手、学习陡峭等。 Rust 是由 Mozilla 主导开发的通用、编译型编程语言&#xff0c;2010年首次公开。 在 Stack Overflow 的年度开发者调查报告中&#xff0c;Rust 连续多年被评为“最受喜爱…...

三种网络配置方法nmcli、ip、ifcfg文件

文章目录 总结nmcli配置网络定义与功能&#xff1a;特点&#xff1a;示例&#xff1a; ip配置网络定义与功能&#xff1a;特点&#xff1a;示例&#xff1a; ifcfg配置网络定义与功能&#xff1a;特点&#xff1a;示例&#xff1a; 总结 nmcli&#xff1a;适合需要动态管理网络…...

AES_ECB算法C++与Java相互加解密Demo

一、AES算法 AES是一种对称加密算法&#xff0c;算法秘钥长度可为128位(16字节)、192位(24字节)、256位(32字节)。加密模式分为ECB、CBC、CTR等&#xff0c;其中ECB模式最简单够用。现给出ECB模式下C和Java的实现&#xff0c;并且可以相互加解密验证。 二、AES_ECB实现DEMO …...

H7-TOOL自制Flash读写保护算法系列,为兆易创新GD32E23X制作使能和解除算法,支持在线烧录和脱机烧录使用(2024-10-29)

说明&#xff1a; 很多IC厂家仅发布了内部Flash算法文件&#xff0c;并没有提供读写保护算法文件&#xff0c;也就是选项字节算法文件&#xff0c;需要我们制作。 实际上当前已经发布的TOOL版本&#xff0c;已经自制很多了。但是依然有些厂家还没自制&#xff0c;所以陆续开始…...

FFmpeg 深度教程音视频处理的终极工具

1. 引言 什么是 FFmpeg&#xff1f; FFmpeg 是一个开源的跨平台多媒体处理工具&#xff0c;广泛应用于音视频的录制、转换、流式传输以及编辑等多个领域。它由 FFmpeg 项目团队开发和维护&#xff0c;支持几乎所有主流的音视频格式和编解码器。FFmpeg 包含了一系列强大的命令…...

Java程序设计:spring boot(13)——全局异常与事务控制

1 Spring Boot 事务支持 在使⽤ Jdbc 作为数据库访问技术时&#xff0c;Spring Boot框架定义了基于jdbc的PlatformTransaction Manager 接⼝的实现 DataSourceTransactionManager&#xff0c;并在 Spring Boot 应⽤ 启动时⾃动进⾏配置。如果使⽤ jpa 的话 Spring Boot 同样提供…...

金和OA-C6 ApproveRemindSetExec.aspx XXE漏洞复现(CNVD-2024-40568)

0x01 产品描述&#xff1a; 金和C6协同管理平台是以"精确管理思想"为灵魂&#xff0c;围绕“企业协同四层次理论”模型&#xff0c;并紧紧抓住现代企业管理的六个核心要素&#xff1a;文化 Culture、 沟通Communication 、 协作Collaboration 、创新 Creation、 控制…...

Redis集群及Redis存储原理

Redis存储原理 Redis将内存划分为16384个区域(类似hash槽) 将数据的key使用CRC16算法计算出一个值,取余16384 得到的结果是0~16383 将这个key保存在计算结果对应的槽位 再次查询这个key时,直接到这个槽位查找,效率很高 实际上这就是"散列表" 提高查询的效率 R…...

基于Springboot的图书个性化推荐系统【源码】+【论文】

图书个性化推荐系统是一个基于Java语言和Springboot框架开发的Web应用系统&#xff0c;主要为管理员和学生提供个性化图书推荐、图书预约和管理功能。系统通过管理员和学生的不同权限设置&#xff0c;实现了图书分类管理、预约管理、退换图书管理、留言板管理等全面的功能&…...

科普 | 子母钟系统是什么?网络时钟同步的重要性?

科普 | 子母钟系统是什么&#xff1f;网络时钟同步的重要性&#xff1f; 科普 | 子母钟系统是什么&#xff1f;网络时钟同步的重要性&#xff1f; 在信息时代的今天&#xff0c;准确统一的时钟系统已广泛的应用在车站、医院、学校、机场等公共服务场所。 因此完善的时钟系统对…...

批量删除redis数据【亲测可用】

文章目录 引言I redis客户端基础操作key的命名规则批量查询keyII 批量删除key使用连接工具进行分组shell脚本示例其他方法III 知识扩展:控制短信验证码获取频率引言 批量删除redis数据的应用: 例如缓存数据使用了新的key存储,需要删除废弃的key。RedisTemplate的key序列化采…...

Vuestic 数据表格 使用demo

<template><br><div class"grid sm:grid-cols-3 gap-6 mb-6"><VaButton click"()>{for(const it in this.selectedItems){console.log(this.selectedItems);}}">参数设置</VaButton><VaButton>参数刷新</VaButt…...

考勤无忧,Zoho People助HR高效

云考勤系统提升数据准确性、无缝对接业务、节省成本、提高员工效率、保障安全。ZohoPeople作为云HRMS&#xff0c;集成考勤管理等功能&#xff0c;支持试用&#xff0c;助力企业高效管理。 一、使用云考勤管理系统&#xff0c;有哪些好处&#xff1f; 1、数据准确性得到保障 …...

已知一个法向量和一个点,求该平面的ModelCoefficients,并使用ProjectInliers将点云投影到该平面

#include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/filters/project_inliers.h> #include <pcl/model_coefficients.h>// 假设法向量和一个点已知 float A 1.0; // 法向量的 x 分量 float B 0.0; // 法向量的 y 分量 floa…...

92.【C语言】数据结构之单向链表的查找,中间插入和删除,销毁

目录 1.链表的查找函数 2.链表的修改函数 3.链表的中间插入函数 1.在pos之前插入:SLTInsertBefore函数 1.借助头指针pphead 示意图 代码示例(写入SList.c) 头文件添加SLTInsertbefore的声明 main.c的部分代码改为 1.测试中间插入 2.测试头部插入 3.测试pos为NULL的…...

WPF+MVVM案例实战(七)- 系统初始化界面字体描边效果实现

文章目录 1、案例效果展示2、项目准备3、功能实现1、资源获取2、界面代码3、后台代码4 源代码获取1、案例效果展示 2、项目准备 打开项目 Wpf_Examples,新建系统初始化界面 WelcomeWindow.xmal,如下所示: 3、功能实现 1、资源获取 案例中使用的CSDN文字为路径文字,从字体…...

基于 C# 的 AI 算法测试方法

基于 C# 的 AI 算法测试方法 在当今人工智能蓬勃发展的时代&#xff0c;AI 算法的质量和可靠性至关重要。对于使用 C# 开发的 AI 算法&#xff0c;我们需要一套有效的测试方法来确保其性能、准确性和稳定性。本文将详细探讨基于 C# 的 AI 算法测试方法&#xff0c;帮助开发者更…...

Find My画框|苹果Find My技术与画框结合,智能防丢,全球定位

画框通常用于保护和固定艺术品&#xff0c;尤其是绘画作品。它是一种可以展示艺术品的框架&#xff0c;用于保护艺术品免受损坏或污染。艺术品被放置在画框内&#xff0c;可以避免受到空气、尘土和其他外部因素的损害。同时&#xff0c;画框还可以增强艺术品的展示效果&#xf…...

布谷语音源码服务器搭建环境及配置流程

布谷语音源码部署环境安装要求&#xff08;只有在相同的环境下才更容易避免一些不必要的麻烦&#xff09;&#xff1a;●安装Center OS 7.9&#xff0c;我们自己的服务器使用的是7.9建议相同系统&#xff0c;非强制●安装宝塔环境&#xff08;强烈推荐使用&#xff09;●安装软…...

智能网站/亚马逊seo是什么意思

本文通过具体的例子阐明如何在 MATLAB系统中进行全维状态观测器和降维状态观测器的设计。MATLAB 为状态空间设计提供了很多有用的函数&#xff0c;方便了矩阵方程的求解&#xff0c;其中的MATLAB 里面提供的库函数对全维状态观测器和降维状态观测器的设计也显得非常地方便。现通…...

网站建设 推荐/百度小说风云榜今天

IOS中图片拉伸技巧与方法总结 一、了解几个图像拉伸的函数和方法 1、直接拉伸法 简单暴力&#xff0c;却是最最常用的方法&#xff0c;直接将图片设置为ImageView的image属性&#xff0c;图片便会随UIImageView对象的大小做自动拉伸。这种拉伸的方法有一个致命的缺陷&#xff0…...

建立一个网站英语/网站链接推广工具

wampserver安装错误 应用程序无法正常启动0xc000007b解决方法参考文章&#xff1a; &#xff08;1&#xff09;wampserver安装错误 应用程序无法正常启动0xc000007b解决方法 &#xff08;2&#xff09;https://www.cnblogs.com/r709651108/p/6432928.html 备忘一下。...

wordpress中文版好还是英文版好/互联网搜索引擎有哪些

我已经审查了围绕HTML在Python中进行JSON解析的可能解决方案&#xff0c;但无法确定正确的解决方案。内容&#xff1a;原型的一部分&#xff1a;我正在使用request.get()方法从第三方组织接收数据。响应为HTML 4.0(loose.dtd格式)。问题陈述&#xff1a;使用Json.dumps和file.w…...

大连网站建设谁家好/免费十大软件大全下载安装

坚守原则&#xff0c;无限关爱 7岁 效用10倍&#xff0c;辛苦7年&#xff0c;享受70年&#xff01; 父母个性化教育&#xff0c;教育机构&#xff0c;平均化教育 关注点&#xff1a;健康、人格、智力。不能只关注当下着急的&#xff0c;要重视未来重要的。 健康&#xff1a;饮食…...

店面门面设计/seo长尾关键词优化

2019独角兽企业重金招聘Python工程师标准>>> 高性能序列化框架FST 博客分类&#xff1a; java fst是完全兼容JDK序列化协议的系列化框架&#xff0c;序列化速度大概是JDK的4-10倍&#xff0c;大小是JDK大小的1/3左右。 首先引入pom Xml代码 <dependency> …...