【聊天室后端服务器开发】语音转换子服务

概述 

实现逻辑

服务器搭建流程分析

  • 基于gflags模块进行参数解析
    • RPC信息:当前服务器的地址端口,主要用于搭建RPC服务器的监听地址信息
    • 服务注册信息
      • 注册中心的地址端口:向服务器中心进行服务注册
      • 外部访问地址端口:告知注册中心的访问地址信息
    • 语音识别平台所需信息:ID、密码等
    • 日志模块信息:运行模式、日志文件名称、日志输出级别
  • 初始化日志模块
  • 搭建RPC服务器,实现各个接口功能
  • 向服务中心进行服务注册 

具体实现

Protobuf接口

message SpeechRecognitionReq {
    string request_id = 1;              //请求ID
    bytes speech_content = 2;           //语音数据
    optional string user_id = 3;        //用户ID
    optional string session_id = 4;     //登录会话ID -- 网关进行身份鉴权
}

message SpeechRecognitionRsp {
    string request_id = 1;              //请求ID
    bool success = 2;                   //请求处理结果标志
    optional string errmsg = 3;         //失败原因
    optional string recognition_result = 4;      //识别后的文字数据
}

//语音识别Rpc服务及接口的定义,传入语音识别的请求,然后返回语音识别的响应
service SpeechService {
    rpc SpeechRecognition(SpeechRecognitionReq) returns (SpeechRecognitionRsp);
}

speech_server.hpp

服务端的头文件中主要构建了三个类

  • SpeechServiceImpl 主要用于实际的语音识别逻辑,具体是通过调用ASRClient来进行语音识别
  • SpeechServer 主要启动和管理RPC服务,提供start方法让服务进入运行状态
  • SpeechServerBuilder 通过构建者,逐步配置语言识别服务所需要的对象,其中包括客户端、服务注册和RPC服务器

SpeechServiceImpl

核心就是SpeechRecognition方法

  • 取出请求中的语音数据
  • 调用ASRClient来进行语音识别
  • 识别成功就返回结果,识别失败就将错误信息写入响应
class SpeechServiceImpl : public mag::SpeechService {
    public:
        // 构造函数,传入 ASRClient 对象,初始化语音识别客户端
        SpeechServiceImpl(const ASRClient::ptr &asr_client)
            : _asr_client(asr_client) {}

        // 析构函数,释放资源
        ~SpeechServiceImpl() {}

        // SpeechRecognition 方法是实际的服务实现,处理语音识别请求
        // controller:用于控制 RPC 调用的执行
        // request:客户端请求的数据,包括语音内容
        // response:服务端返回的响应数据
        // done:调用完成后的回调,用于通知客户端
        void SpeechRecognition(google::protobuf::RpcController* controller,
                               const ::mag::SpeechRecognitionReq* request,
                               ::mag::SpeechRecognitionRsp* response,
                               ::google::protobuf::Closure* done) {
            LOG_DEBUG("收到语音转文字请求!");

            // 使用 brpc::ClosureGuard 确保 RPC 调用完成后会自动执行 done 回调
            brpc::ClosureGuard rpc_guard(done);

            // 1. 提取请求中的语音内容
            // 2. 调用语音识别客户端进行语音识别,获取识别结果
            std::string err;
            std::string res = _asr_client->recognize(request->speech_content(), err);

            // 3. 如果识别失败,设置响应失败信息
            if (res.empty()) {
                LOG_ERROR("{} 语音识别失败!", request->request_id());
                response->set_request_id(request->request_id());
                response->set_success(false);
                response->set_errmsg("语音识别失败:" + err);
                return;
            }

            // 4. 如果识别成功,设置响应内容
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_recognition_result(res);
        }

    private:
        ASRClient::ptr _asr_client; // 语音识别客户端
    };

SpeechServer

主要就是提供接口来启动服务器

class SpeechServer {
    public:
        using ptr = std::shared_ptr<SpeechServer>;

        // 构造函数,初始化语音识别客户端、服务注册客户端和 RPC 服务器
        SpeechServer(const ASRClient::ptr asr_client,
                     const Registry::ptr &reg_client,
                     const std::shared_ptr<brpc::Server> &server)
            : _asr_client(asr_client), _reg_client(reg_client), _rpc_server(server) {}

        // 析构函数,释放资源
        ~SpeechServer() {}

        // 启动 RPC 服务器,进入事件循环,直到请求停止
        void start() {
            _rpc_server->RunUntilAskedToQuit();
        }

    private:
        ASRClient::ptr _asr_client;        // 语音识别客户端
        Registry::ptr _reg_client;         // 服务注册客户端
        std::shared_ptr<brpc::Server> _rpc_server; // RPC 服务器
    };

SpeechServerBuilder

通过brpc框架,构建一个RPC的语音识别服务器,该处使用了构建者模式从而实现灵活构建

    class SpeechServerBuilder {
    public:
        // 构造语音识别客户端对象
        // app_id、api_key、secret_key:用于初始化语音识别客户端的参数
        void make_asr_object(const std::string &app_id,
                              const std::string &api_key,
                              const std::string &secret_key) {
            _asr_client = std::make_shared<ASRClient>(app_id, api_key, secret_key);
        }

        // 构造服务注册客户端对象
        // reg_host:注册中心的地址
        // service_name:服务名称
        // access_host:服务访问地址
        void make_reg_object(const std::string &reg_host,
                             const std::string &service_name,
                             const std::string &access_host) {
            _reg_client = std::make_shared<Registry>(reg_host);
            _reg_client->registry(service_name, access_host);
        }

        // 构造 RPC 服务器对象并启动服务器
        // port:服务端口,timeout:超时时间,num_threads:线程数
        void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads) {
            // 确保语音识别模块已经初始化
            if (!_asr_client) {
                LOG_ERROR("还未初始化语音识别模块!");
                abort();
            }

            // 创建 brpc::Server 对象
            _rpc_server = std::make_shared<brpc::Server>();

            // 创建 SpeechServiceImpl 对象,并将其添加到 RPC 服务器
            // SpeechServiceImpl 类实现了语音识别服务的具体业务逻辑
            SpeechServiceImpl *speech_service = new SpeechServiceImpl(_asr_client);
            int ret = _rpc_server->AddService(speech_service,
                                              brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
            if (ret == -1) {
                LOG_ERROR("添加Rpc服务失败!");
                abort();
            }

            // 配置 RPC 服务器的选项
            brpc::ServerOptions options;
            options.idle_timeout_sec = timeout;  // 设置空闲超时时间
            options.num_threads = num_threads;   // 设置工作线程数

            // 启动服务器,监听指定端口
            ret = _rpc_server->Start(port, &options);
            if (ret == -1) {
                LOG_ERROR("服务启动失败!");
                abort();
            }
        }

        // 构建并返回 SpeechServer 对象
        // 返回一个包含语音识别客户端、服务注册客户端和 RPC 服务器的完整服务对象
        SpeechServer::ptr build() {
            if (!_asr_client) {
                LOG_ERROR("还未初始化语音识别模块!");
                abort();
            }
            if (!_reg_client) {
                LOG_ERROR("还未初始化服务注册模块!");
                abort();
            }
            if (!_rpc_server) {
                LOG_ERROR("还未初始化RPC服务器模块!");
                abort();
            }
            // 返回构建好的 SpeechServer 对象
            SpeechServer::ptr server = std::make_shared<SpeechServer>(_asr_client, _reg_client, _rpc_server);
            return server;
        }

    private:
        ASRClient::ptr _asr_client;  // 语音识别客户端
        Registry::ptr _reg_client;   // 服务注册客户端
        std::shared_ptr<brpc::Server> _rpc_server; // RPC 服务器
    };

speech_server.cc

int main(int argc, char *argv[])
{
    google::ParseCommandLineFlags(&argc, &argv, true);
    init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);


    mag::SpeechServerBuilder ssb;
    ssb.make_asr_object(FLAGS_app_id, FLAGS_api_key, FLAGS_secret_key);
    ssb.make_rpc_server(FLAGS_listen_port, FLAGS_rpc_timeout, FLAGS_rpc_threads);
    ssb.make_reg_object(FLAGS_registry_host, FLAGS_base_service + FLAGS_instance_name, FLAGS_access_host);
    auto server = ssb.build();
    server->start();

    return 0;
}

test.cc

  • 通过etcd服务发现机制找到语音识别服务的RPC通信信道
  • 读取语音文件的内容
  • 构造语音识别请求并通过RPC调用发送给语音服务
  • 接收并处理语音识别结果,输出识别成功或者失败的消息
int main(int argc, char *argv[])
{
    // 解析命令行参数
    google::ParseCommandLineFlags(&argc, &argv, true);
    init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);

    // 1. 构造 Rpc 信道管理对象,用于选择合适的 RPC 信道
    auto sm = std::make_shared<mag::ServiceManager>();

    // 注册服务发现时回调函数
    sm->declared(FLAGS_speech_service);
    auto put_cb = std::bind(&mag::ServiceManager::onServiceOnline, sm.get(), std::placeholders::_1, std::placeholders::_2);
    auto del_cb = std::bind(&mag::ServiceManager::onServiceOffline, sm.get(), std::placeholders::_1, std::placeholders::_2);

    // 2. 构造服务发现对象,连接到 Etcd 服务注册中心
    mag::Discovery::ptr dclient = std::make_shared<mag::Discovery>(FLAGS_etcd_host, FLAGS_base_service, put_cb, del_cb);

    // 3. 通过 Rpc 信道管理对象,获取提供语音识别服务的信道
    auto channel = sm->choose(FLAGS_speech_service);
    if (!channel) {
        // 如果没有获取到可用的信道,等待 1 秒后退出
        std::this_thread::sleep_for(std::chrono::seconds(1));
        return -1;
    }

    // 4. 读取语音文件数据
    std::string file_content;
    aip::get_file_content("16k.pcm", &file_content);  // 从文件中读取 PCM 格式的语音数据
    std::cout << "语音文件大小: " << file_content.size() << " 字节" << std::endl;

    // 5. 构造并发起语音识别的 RPC 调用
    mag::SpeechService_Stub stub(channel.get());  // 创建服务存根对象
    mag::SpeechRecognitionReq req;  // 创建语音识别请求对象
    req.set_speech_content(file_content);  // 设置请求中的语音数据
    req.set_request_id("111111");  // 设置请求 ID

    brpc::Controller *cntl = new brpc::Controller();  // 创建 RPC 控制器,用于管理请求的生命周期
    mag::SpeechRecognitionRsp *rsp = new mag::SpeechRecognitionRsp();  // 创建响应对象,用于接收服务端返回的数据

    // 发起 RPC 调用,调用语音识别服务
    stub.SpeechRecognition(cntl, &req, rsp, nullptr);

    // 检查 RPC 调用是否失败
    if (cntl->Failed()) {
        std::cout << "Rpc 调用失败:" << cntl->ErrorText() << std::endl;
        delete cntl;
        delete rsp;
        std::this_thread::sleep_for(std::chrono::seconds(1));  // 等待一秒后退出
        return -1;
    }

    // 检查语音识别的结果是否成功
    if (!rsp->success()) {
        std::cout << "语音识别失败: " << rsp->errmsg() << std::endl;
        delete cntl;
        delete rsp;
        return -1;
    }

    // 输出识别结果
    std::cout << "收到响应, 请求 ID: " << rsp->request_id() << std::endl;
    std::cout << "语音识别结果: " << rsp->recognition_result() << std::endl;

    // 清理资源
    delete cntl;
    delete rsp;

    return 0;
}

CMake

# 1. 添加cmake版本说明
cmake_minimum_required(VERSION 3.1.3)

# 2. 声明工程名称
project(speech_server)

set(target "speech_server")
set(test_client "speech_client")

# 3. 检测并生成ODB框架代码
set(proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../proto)
set(proto_files speech.proto)

set(proto_srcs "")
set(proto_cxx "")
set(proto_srcs "")
foreach(proto_file ${proto_files})
    string(REPLACE ".proto" ".pb.cc" proto_cc ${proto_file})
    string(REPLACE ".proto" ".pb.h" proto_hh  ${proto_file})

    # 如果没有生成,则预定义生成指令 -- 用于在构建项目之间先生成框架代码
    if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
        add_custom_command(
            PRE_BUILD
            COMMAND protoc
            ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I${proto_path} --experimental_allow_proto3_optional  ${proto_path}/${proto_file}
            DEPENDS ${proto_path}/${proto_file}
            OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
            COMMENT "生成Protobuf框架代码文件:" ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc}
        )
    endif()
    list(APPEND proto_srcs ${CMAKE_CURRENT_BINARY_DIR}/${proto_cc})
endforeach()

# 4. 获取源码目录下的所有源码文件
set(src_files "")
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source src_files)

# 5. 声明目标及依赖
add_executable(${target} ${src_files} ${proto_srcs})

set(test_files "")
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/test test_files)
add_executable(${test_client} ${test_files} ${proto_srcs})
target_link_libraries(${test_client} -lgflags -lspdlog -lfmt -lbrpc -lssl -lcrypto -lprotobuf -lleveldb -letcd-cpp-api -lcpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)

# 7. 设置需要连接的库
find_package(gflags REQUIRED) # 确保 gflags 被正确找到
target_link_libraries(${target} gflags spdlog fmt brpc ssl crypto protobuf leveldb etcd-cpp-api cpprest -lcurl /usr/lib/x86_64-linux-gnu/libjsoncpp.so.19)

# 6. 设置头文件默认搜索路径
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../third/include)

# 8. 设置安装路径
INSTALL(TARGETS ${target} ${test_client} RUNTIME DESTINATION bin)

测试

问题总结

aip-cpp-sdk 问题

经排查,该库函数中std::transform使用不正确导致的

因为toupper和tolower是C库中的函数,接收的是一个字符,而不是一个字符串,所以最终导致类型推导失败

    inline std::string to_upper(std::string src)
    {
        //11-30修改
        std::transform(src.begin(), src.end(), src.begin(), [](unsigned char c) { return std::toupper(c); });
        // std::transform(src.begin(), src.end(), src.begin(), toupper);
        return src;
    }
    
    
    inline std::string to_lower(std::string src)
    {
        //11-30修改
        // std::transform(src.begin(), src.end(), src.begin(), tolower);
        std::transform(src.begin(), src.end(), src.begin(), [](unsigned char c) { return std::tolower(c); });
        return src;
    }

客户端调用RPC服务失败

root@hcss-ecs-b4a9:/home/chatServer/chatServer/src/server/speech/build# ./speech_client
[default-logger][21:38:01][414488][debug   ][/home/chatServer/chatServer/src/server/speech/../common/channel.hpp:118] /service-127.0.0.1:9090 服务上线了,但是当前并不关心!
[default-logger][21:38:01][414488][debug   ][/home/chatServer/chatServer/src/server/speech/../common/channel.hpp:139] /service/speech_service-127.0.0.1:10001 服务上线新节点,进行添加管理!
0
Rpc调用失败:[E1014]Got EOF of Socket{id=0fd=10 addr=127.0.0.1:10001:39996} (0x0x55753db88940) [R1][E112]Not connected to 127.0.0.1:10001 yet, server_id=0 [R2][E112]Not connected to 127.0.0.1:10001 yet, server_id=0 [R3][E112]Not connected to 127.0.0.1:10001 yet, server_id=0
I1130 21:38:01.654227 414538 4294969856 /home/chatServer/chatServer/Test/brpc/brpc/src/brpc/socket.cpp:2570] Checking Socket{id=0 addr=127.0.0.1:10001} (0x55753db88940)
[warn] watcher does't exit normally

生命周期问题,通过智能指针或者堆上建立即可,后期统一解决

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/926651.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

w064基于springboot的高校学科竞赛平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…

UCOS-II 自学笔记

摘抄于大学期间记录在QQ空间的一篇自学笔记&#xff0c;当前清理空间&#xff0c;本来想直接删除掉的&#xff0c;但是感觉有些舍不得&#xff0c;因此先搬移过来。 一、UC/OS_II体系结构 二、UC/OS_II中的任务 1、任务的基本概念 在UCOS-II中&#xff0c;通常把一个大型任…

《Python基础》之Python中可以转换成json数据类型的数据

目录 一、JSON简介 JSON有两种基本结构 1、对象&#xff08;Object&#xff09; 2、数组&#xff08;Array&#xff09; 二、将数据装换成json数据类型方法 三、在Python中&#xff0c;以下数据类型可以直接转换为JSON数据类型 1、字典&#xff08;Dictionary&#xff09…

Android -- 简易音乐播放器

Android – 简易音乐播放器 播放器功能&#xff1a;* 1. 播放模式&#xff1a;单曲、列表循环、列表随机&#xff1b;* 2. 后台播放&#xff08;单例模式&#xff09;&#xff1b;* 3. 多位置同步状态回调&#xff1b;处理模块&#xff1a;* 1. 提取文件信息&#xff1a;音频文…

纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架

前言​ 开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C 库&#xff0c;如 ​​OpenCV​​ 或 ​​​dlib​​​&#xff0c;但通过 ​​​cgo​​​ 调用 C 程序会引入巨大的延迟&#xff0c;并在性能方面产生显著的权衡。…

uniapp中父组件数组更新后与页面渲染数组不一致实战记录

简单描述一下业务场景方便理解: 商品设置功能,支持添加多组商品(点击添加按钮进行增加).可以对任意商品进行删除(点击减少按钮对选中的商品设置进行删除). 问题: 正常添加操作后,对已添加的任意商品删除后,控制台打印数组正常.但是与页面显示不一致.已上图为例,选中尾…

【Figma】中文版安装

一、软件安装包下载 打开官网链接https://www.figma.com/downloads/下载相应安装包 或使用我已下载好的链接&#xff1a; FigmaSetup.exe 链接: https://pan.baidu.com/s/113eQ8JRETdeOwUp2B3uieA?pwd4vep 二、安装流程 1.点击安装包 2.选择在浏览器登录 3.输入账号密码&a…

SpringBoot框架在宠物领养系统中的应用

摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现有问题而产生的。针对于宠物领养…

SpringAi整合大模型(进阶版)

进阶版是在基础的对话版之上进行新增功能。 如果还没弄出基础版的&#xff0c;请参考 https://blog.csdn.net/weixin_54925172/article/details/144143523?sharetypeblogdetail&sharerId144143523&sharereferPC&sharesourceweixin_54925172&spm1011.2480.30…

redis快速进门

、数据库类型认识 关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言&#xff0c;用于执行…

爬虫笔记24——纷玩岛自动抢票脚本笔记

纷玩岛自动抢票&#xff0c;协议抢票思路实现 一、获取Authorization凭证二、几个关键的参数三、几个关键的接口获取参数v&#xff0c;这个参数其实可以写死&#xff0c;可忽略通过价位获取演出的参数信息获取观演人信息&#xff0c;账号提前录入即可提交订单接口 先看实现图&a…

Netty的心跳机制怎么实现的?

大家好&#xff0c;我是锋哥。今天分享关于【Netty的心跳机制怎么实现的&#xff1f;】面试题。希望对大家有帮助&#xff1b; Netty的心跳机制怎么实现的&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Netty 的心跳机制用于维持客户端和服务器之间的…

RHEL7+Oracle11.2 RAC集群-多路径(multipath+udev)安装步骤

RHEL7Oracle11.2RAC集群-多路径&#xff08;multipathudev&#xff09;安装 配置虚拟存储 使用StarWind Management Console软件&#xff0c;配置存储 dggrid1: 1g*3 Dggrid2: 1g*3 Dgsystem: 5g*1 系统表空间&#xff0c;临时表空间&#xff0c;UNDO&#xff0c;参数文件…

Sybase数据恢复—Sybase数据库无法启动,Sybase Central连接报错的处理案例

Sybase数据库数据恢复环境&#xff1a; Sybase数据库版本&#xff1a;SQL Anywhere 8.0。 Sybase数据库故障&分析&#xff1a; Sybase数据库无法启动。 错误提示&#xff1a; 使用Sybase Central连接报错。 数据库数据恢复工程师经过检测&#xff0c;发现Sybase数据库出现…

数学题转excel;数学题库;数学试卷转excel;大风车excel

一、数学试卷转excel 有些需要刷题的朋友&#xff0c;需要将题库数学题转为excel格式&#xff0c;便于管理 前端时间帮一位朋友实现了数学题转excel&#xff0c;包括选择题、填空题、分析题 示例&#xff1a; 二、问题 数学题是最难以处理的试题&#xff0c;理由如下 1、有…

开源项目:纯Python构建的中后台管理系统

来源&#xff1a;Python大数据分析 费弗里 大家好我是费老师&#xff0c;目前市面上有很多开源的「中后台管理系统」解决方案&#xff0c;复杂如「若依」那种前端基于Vue&#xff0c;后端基于Java的框架&#xff0c;虽然其提供了较为完善的一整套前后端分离权限管理系统解决方…

PS的功能学习

背景差色较大&#xff0c;就魔棒 魔棒的连续就是倒水点的跨越问题 魔棒的容差的选择就有点看经验了&#xff0c;看颜色的统一程度选择 Ctrl D 取消当前所有的选区 至于快速选择工具&#xff0c;和对象选择工具也差不多&#xff0c;只不过控制范围变成了一块一块的&#x…

linux一键部署apache脚本

分享一下自己制作的一键部署apache脚本&#xff1a; 脚本已和当前文章绑定&#xff0c;请移步下载&#xff08;免费&#xff01;免费&#xff01;免费&#xff01;&#xff09; &#xff08;单纯的分享&#xff01;&#xff09; 步骤&#xff1a; 将文件/内容上传到终端中 …

DataWhale—PumpkinBook(TASK07支持向量机)

课程开源地址及相关视频链接&#xff1a;&#xff08;当然这里也希望大家支持一下正版西瓜书和南瓜书图书&#xff0c;支持文睿、秦州等等致力于开源生态建设的大佬✿✿ヽ(▽)ノ✿&#xff09; Datawhale-学用 AI,从此开始 【吃瓜教程】《机器学习公式详解》&#xff08;南瓜…

排序算法之插入排序篇

插入排序 思路&#xff1a; 就是将没有排序的元素逐步地插入到已经排好序的元素后面&#xff0c;保持元素的有序 视频的实现过程如下&#xff1a; 插入排序全过程 代码实现过程如下&#xff1a; public static void Insertion(int[] arr) { for (int i 1; i < arr.length…