iOS MT19937随机数生成,结合AES-CBC加密算法实现。
按处理顺序说明:
1. 生成随机数序列字符串函数
生成方法MT19937,初始种子seed,利用C++库方法,生成:
#include <random> //C++ 库头文件引入NSString * JKJMT19937Seed(uint32_t seed) {NSLog(@"MT19937Seed种子:%u",seed);NSMutableArray *ranVlues = [NSMutableArray array];std::mt19937 rngCPluc2(seed);for (int i = 0; i < 64; ++i) {//连续生产64个随机数unsigned long ranValue = rngCPluc2();NSNumber *rngVal = [NSNumber numberWithUnsignedLong:ranValue];[ranVlues addObject:rngVal];}NSString *ranSeriesStr = [ranVlues componentsJoinedByString:@""];NSLog(@"随机数值序列拼接字符串key1 = %@",ranSeriesStr);return ranSeriesStr;
}
2. 对第一部中的随机数序列字符串进行sha256加密,得到64字节的一个数据流函数。
#import <CommonCrypto/CommonCrypto.h>//加密头文件NSString * sha256String(NSString *inputString) {const char *str = inputString.UTF8String;uint8_t buffer[CC_SHA256_DIGEST_LENGTH];CC_SHA256(str, (CC_LONG)strlen(str), buffer);NSMutableString *strM = [NSMutableString string];for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {[strM appendFormat:@"%02x", buffer[i]];}return [strM copy];
}
3. AES-CBC加密解密方法
/*CCCrypt方法提供了CBC 和 ECB 两种AES加密模式,如果不传参数,kCCOptionECBMode,则默认即使CBC模式加密。ECB模式不是一种可靠安全的加密模式,推荐使用CBC模式。另外也可以通过其他的库或者方法实现GCM等方式的AES加密。在后面的代码块中翻入方法。
*/
NSData *aesCBCEncrypt(NSData *inputData, NSData *keyData, NSData *ivData) {NSData *key = keyData;// 准备一个初始化向量(IV),IV 的长度需要与 AES 加密模式匹配// 在 AES-CBC 模式下,IV 的长度通常为 AES 块大小(16 字节)NSData *iv = ivData;// 创建一个用于存储加密后数据的 NSMutableData 对象NSMutableData *encryptedData = [NSMutableData dataWithLength:inputData.length + kCCBlockSizeAES128];size_t encryptedDataLength = 0;// 使用 AES-256 加密(CBC)CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES,kCCOptionPKCS7Padding,key.bytes, key.length,iv.bytes, // 无需传入 IVinputData.bytes, inputData.length,encryptedData.mutableBytes, encryptedData.length,&encryptedDataLength);if (cryptStatus == kCCSuccess) {encryptedData.length = encryptedDataLength;return encryptedData;} else {NSLog(@"AES-256 加密失败");return nil;}
}NSData *aesCBCDecrypt(NSData *encryptedData, NSData *keyData, NSData *ivData) {// 准备一个初始化向量(IV)// 在 AES-CBC 模式下,IV 的长度通常为 AES 块大小(16 字节)NSData *iv = ivData;// 创建一个用于存储解密后数据的 NSMutableData 对象NSMutableData *decryptedData = [NSMutableData dataWithLength:encryptedData.length];size_t decryptedDataLength = 0;// 进行 AES 解密CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES,kCCOptionPKCS7Padding,keyData.bytes, keyData.length,iv.bytes, // 传入 IVencryptedData.bytes, encryptedData.length,decryptedData.mutableBytes, decryptedData.length,&decryptedDataLength);if (cryptStatus == kCCSuccess) {decryptedData.length = decryptedDataLength;return decryptedData;} else {NSLog(@"AES 解密失败");return nil;}
}
/*
CCCrypt方法提供了CBC 和 ECB 两种AES加密模式,
如果不传参数,kCCOptionECBMode,则默认即使CBC模式加密。
ECB模式不是一种可靠安全的加密模式,推荐使用CBC模式。
另外也可以通过其他的库或者方法实现GCM等方式的AES加密。在后面的代码块中翻入方法。
*/
4. 前三个步骤已经完成了加密的基本算法代码,接下来直接示例实现一个传参加密:
- (NSString *)EncryptStringFromtServiceStartMap:(NSDictionary *)json {//补充ts字段。NSMutableDictionary *muJson = [NSMutableDictionary dictionaryWithDictionary:json];NSTimeInterval tsVal = [[NSDate date] timeIntervalSince1970];[muJson setObject:[NSNumber numberWithInteger:(NSUInteger)(1000* tsVal)] forKey:@"ts"];NSString *jsonString = nil;NSError *error;NSData *jsonData = [NSJSONSerialization dataWithJSONObject:muJsonoptions:NSJSONWritingPrettyPrintederror:&error];[self loadAESKeyAndIVData];//获取key和iv。//进行AES加密。NSData *aesData = aesCBCEncrypt(jsonData, self.aesSHA256keyData, self.aesIVData);//base64再次加密AES-CBC加密后的数据流,返回值作为start_service的参数NSString *base64 = [aesData base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];NSString *encryptStr = base64;NSLog(@"jsonContent:%@",muJson);NSLog(@"start_service参数encryptStr = %@",encryptStr);NSData *uncodeAESData = aesCBCDecrypt(aesData, self.aesSHA256keyData, self.aesIVData);if(uncodeAESData){NSError *error = nil;NSDictionary *uncodeDict = [NSJSONSerialization JSONObjectWithData:uncodeAESData options:kNilOptions error:&error];if (error) {NSString *uncodeJSON = [[NSString alloc] initWithData:uncodeAESData encoding:NSUTF8StringEncoding];if(uncodeJSON){NSLog(@"验证解密ServiceMap:%@", uncodeJSON);}else{NSLog(@"验证解密ServiceMap: 解析失败:%@", error);}} else {NSLog(@"验证解密ServiceMap:%@",uncodeDict);}}return encryptStr;
}//初始化AES加密的Key和iv向量数据。
- (void)loadAESKeyAndIVData {// 设置随机种子为 4728423NSString *mt19937Str = JKJMT19937Seed(4728423);NSString *sha256Str = sha256String(mt19937Str);/*密钥,截取sha256的64个字节前面32个字节IV:截取sha256的64个字节前面16个字节*/NSString *keyString = substringWithLength(sha256Str, 32);NSString *ivString = substringWithLength(sha256Str, 16);//获取AES加密Key_aesSHA256keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];//获取AES加密的IV向量_aesIVData = [ivString dataUsingEncoding:NSUTF8StringEncoding];
}
5. 归纳,完成4的操作,用到了一下两个库,a. C++中的random库,实现mt19937随机数;b. CommonCrypto库,实现AES-CBC对称加密,实现SHA256加密。c. 另外Base64是OC中的NSData(NSDataBase64Encoding)分类方法提供。
#include <random>
#import <CommonCrypto/CommonCrypto.h>
6. 补充, AES加密如果需要GCM或者其它(非CBC、非ECB)模式的,可能需要用到如下的方法去实现:a. 使用iOS13之后的库, b. 使用第三方库(libsodium-ios为例)
- a.使用iOS13之后的库代码如下:
//
// AESEncryptor.swift
//
// Created by xw.long on 2024/4/7.
//import Foundation
import CryptoKit
import CommonCrypto@available(iOS 13.0, *)
@objc class AESEncryptor: NSObject {func encryptDataUsingCBCMode(data: Data, key: Data, iv: Data) -> Data? {let bufferSize = data.count + kCCBlockSizeAES128var buffer = [UInt8](repeating: 0, count: bufferSize)var numBytesEncrypted: size_t = 0let cryptStatus = data.withUnsafeBytes { dataBytes inkey.withUnsafeBytes { keyBytes iniv.withUnsafeBytes { ivBytes inCCCrypt(CCOperation(kCCEncrypt),CCAlgorithm(kCCAlgorithmAES),CCOptions(kCCOptionPKCS7Padding),keyBytes.baseAddress, key.count,ivBytes.baseAddress,dataBytes.baseAddress, data.count,&buffer, bufferSize,&numBytesEncrypted)}}}if cryptStatus == kCCSuccess {return Data(buffer.prefix(Int(numBytesEncrypted)))} else {print("Error: \(cryptStatus)")return nil}}// 加密方法@objc func encrypt(content: String, key: String, iv: String) -> Data? {guard let keyData = Data(hexString: key),let ivData = Data(hexString: iv),let contentData = content.data(using: .utf8) else {print("AESEncryptor 无法将输入转换为Data")return nil}do {// 创建AES密钥let aesKey = SymmetricKey(data: keyData)// 使用AES-256-GCM加密let sealedBox = try AES.GCM.seal(contentData, using: aesKey, nonce: AES.GCM.Nonce(data: ivData))// 返回加密后的数据return sealedBox.combined} catch {print("AESEncryptor 加密失败: \(error)")return nil}}// 解密方法@objc func decrypt(encryptedData: Data, key: String, iv: String) -> String? {guard let keyData = Data(hexString: key),let ivData = Data(hexString: iv) else {print("AESEncryptor 无法将输入转换为Data")return nil}do {// 创建AES密钥let aesKey = SymmetricKey(data: keyData)// 解密let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)let decryptedData = try AES.GCM.open(sealedBox, using: aesKey)// 将解密后的数据转换为字符串guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {print("解密后的数据无法转换为字符串")return nil}return decryptedString} catch {print("AESEncryptor 解密失败: \(error)")return nil}}// 测试方法@objc static func test() {let content = "hello world"let key = "3891346e92151849d58e70de02a05c596b48afe1ae2bdeedf3e69c661c2ea2ae"let iv = "3891346e9215"if let encryptedData = AESEncryptor().encrypt(content: content, key: key, iv: iv) {print("AESEncryptor 加密后的数据: \(encryptedData.base64EncodedString())")if let decryptedString = AESEncryptor().decrypt(encryptedData: encryptedData, key: key, iv: iv) {print("AESEncryptor 解密后的字符串: \(decryptedString)")}}}}// 十六进制字符串转换为Data扩展
extension Data {init?(hexString: String) {var hexString = hexStringvar data = Data()while hexString.count > 0 {let subIndex = hexString.index(hexString.startIndex, offsetBy: 2)let hexChar = String(hexString[..<subIndex])hexString = String(hexString[subIndex...])guard let byte = UInt8(hexChar, radix: 16) else {return nil}data.append(byte)}self = data}
}+ (void)swiftTest {NSString *originalString = @"3891346e92151849d89070de02a05c596b48a123ae2bdeedf3e69c661c2ea2ae";// 截取新的key和ivNSString *key = [originalString substringToIndex:32];NSString *iv = [originalString substringToIndex:12];// 待加密的内容NSString *content = @"hello world";// 调用加密方法if (@available(iOS 13.0, *)) {NSData *encryptedData = [[AESEncryptor new] encryptWithContent:content key:key iv:iv];// 将加密后的数据转换为Base64字符串NSString *encryptedString = [encryptedData base64EncodedStringWithOptions:0];NSLog(@"加密后的字符串: %@", encryptedString);} else {// Fallback on earlier versions}
}
- b. 使用第三方库(libsodium-ios为例)
库引用可以通过cocoa-pod方法,在Podfile文件中加入如下代码,并在对应文件目录下执行【pod install】。
platform :ios, '12.0'
也可以通过github 下载:https://github.com/mochtu/libsodium-ios
# Uncomment the next line to define a global platform for your project
platform :ios, '12.0'
target 'MYPROJECT' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for MYPROJECT
pod 'libsodium-ios'
end
post_install do |pi|
pi.pods_project.targets.each do |t|
t.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
下面是库引入之后得代码示例:
#include <sodium.h>NSData *encryptStringWithAES256EStream(NSString *inputString, const unsigned char *nonce, const unsigned char *key) {// 转换输入字符串为NSDataNSData *inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];// 获取输入数据的长度NSUInteger inputLength = inputData.length;// 准备输出缓冲区NSMutableData *encryptedData = [NSMutableData dataWithLength:inputLength];// 产生AES-256-ESTREAM流密码unsigned char stream[inputLength];crypto_stream_aes256estream(stream, inputLength, nonce, key);// 对输入数据进行异或运算,实现加密unsigned char *inputBytes = (unsigned char *)inputData.bytes;unsigned char *encryptedBytes = (unsigned char *)encryptedData.mutableBytes;for (NSUInteger i = 0; i < inputLength; i++) {encryptedBytes[i] = inputBytes[i] ^ stream[i];}return encryptedData;
}+ (void)cryptoTest {// 初始化libsodium库if (sodium_init() == -1) {NSLog(@"libsodium初始化失败");return;}// 长度为16字节的nonceunsigned char nonce[crypto_stream_aes256estream_NONCEBYTES];randombytes_buf(nonce, sizeof(nonce));// 长度为32字节的密钥unsigned char key[crypto_stream_aes256estream_KEYBYTES];randombytes_buf(key, sizeof(key));// 要加密的字符串NSString *inputString = @"hello world";// 使用AES-256-ESTREAM密钥流对字符串进行加密NSData *encryptedData = encryptStringWithAES256EStream(inputString, nonce, key);// 打印加密后的数据NSLog(@"加密后的数据:%@", encryptedData);}
相关文章:
iOS MT19937随机数生成,结合AES-CBC加密算法实现。
按处理顺序说明: 1. 生成随机数序列字符串函数 生成方法MT19937,初始种子seed,利用C库方法,生成: #include <random> //C 库头文件引入NSString * JKJMT19937Seed(uint32_t seed) {NSLog("MT19937Seed种…...

阿里云2024年优惠券获取方法及使用教程详解
阿里云是阿里巴巴集团旗下的云计算服务提供商,是全球领先的云计算及人工智能科技公司之一。提供免费试用、云服务器、云数据库、云安全、云企业应用等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。 阿里云2024年优惠券的获取方…...
hadoop中hdfs的fsimage文件与edits文件
hadoop中hdfs的fsimage文件与edits文件的作用 首先,我们抛出fsimage和edits文件的功能描述。 Fsimage文件: HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的 所有目录和文件inode的序列化信息。 Edits文件:存放HDFS文件系统的所有更…...

最新版两款不同版SEO超级外链工具PHP源码
可根据个人感觉喜好自行任意选择不同版本使用(版V1或版V2) 请将zip文件全部解压缩即可访问! 源码全部开源,支持上传二级目录访问 已更新增加大量高质量外链(若需要增加修改其他外链请打开txt文件)修复优…...

.net框架和c#程序设计第二次测试
一、实验内容 1、设计一个用户登录页面webform1.aspx,效果如下图所示: 2、点击webform1.aspx中“还未注册”连接进入register.aspx,注册页面效果如下图所示:点击用户注册信息到usershow.aspx页面,并显示注册的用户信息…...

芒果YOLOv8改进组合157:动态标签分配ATSS+新颖高效AsDDet检测头组合改进,共同助力VisDrone涨点1.8%,小目标高效涨点
💡本篇内容:【芒果YOLOv8改进ATSS标签分配策略|第三集】芒果YOLOv8改进组合157:动态标签分配ATSS+新颖高效AsDDet检测头组合改进,共同助力VisDrone涨点1.8%,小目标高效涨点 💡🚀🚀🚀本博客 标签分配策略ATSS改进+ 新颖高效AsDDet检测头组合改进,适用于 YOLOv8 …...

自媒体内容创作助手:7款必备ai写作工具一览! #学习方法#科技#其他
这些工具不仅可以快速生成高质量的文本内容,还可以根据用户的需求进行个性化定制。它们可以帮助我们节省大量的时间和精力,让我们更加专注于创意和细节的打磨。本文将为大家详细介绍几个AI写作工具,让你在写作领域更上一层楼。 1.七燕写作 这…...
文心一言 vs GPT-4 -- 全面横向比较
文心一言和GPT-4都是当前非常先进的自然语言处理模型,它们在语言理解、生成和翻译等方面都展现出了出色的能力。以下是对这两个模型的全面横向比较: 核心技术基础: 文心一言:是基于BERT(Bidirectional Encoder Repre…...

Leetcode C语言习题
Leetcode习题27:移除元素 题目: 说明: 示例: 题解: 方法一:(开辟额外的数组空间) 我们可以创建一个新的数组,然后用循环来遍历原数组,将原数组中不为 val…...

比 Nest.js 更优雅的 TS 控制反转策略 - 依赖查找
一、Cabloy5.0 内测预告 Cabloy5.0 采用 TS 对整个全栈框架进行了脱胎换骨般的大重构,并且提供了更加优雅的 ts 控制反转策略,让我们的业务开发更加快捷顺畅 1. 新旧技术栈对比: 后端前端旧版js、egg2.0、mysqljs、vue2、framework7新版ts…...

java算法day43 | 动态规划part05 ● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零
1049. 最后一块石头的重量 II 核心思想: 尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。 是不是感觉和昨天讲解的416. 分割等和子集 (opens new window)非常像了。那么分成两堆石头,一堆石头的…...

STM32无刷电机全套开发资料(源码、原理图、PCB工程及说明文档)
目录 1、原理图、PCB、BOOM表 2、设计描述 2.1 前言 2.2 设计电路规范 3、代码 4、资料清单 资料下载地址:STM32无刷电机全套开发资料(源码、原理图、PCB工程及说明文档) 1、原理图、PCB、BOOM表 2、设计描述 2.1 前言 经过一个星期的画PCB,今…...

工地安全监测识别摄像机
工地安全监测识别摄像机是一种在建筑工地和施工现场广泛使用的智能监控设备,主要用于监测施工过程中可能出现的安全隐患和违规行为,以确保工地人员和设备的安全。通过高清摄像头、智能算法和远程监控系统的结合,该摄像机可以实时监测工地各个…...

【零基础学数据结构】顺序表实现书籍存储
目录 书籍存储的实现规划 编辑 前置准备: 书籍结构体: 书籍展示的初始化和文件加载 书籍展示的销毁和文件保存 书籍展示的容量检查 书籍展示的尾插实现 书籍展示的书籍增加 书籍展示的书籍打印 书籍删除展示数据 书籍展示修改数据 在指定位置之前…...

【智能算法】黑寡妇优化算法(BWO)原理及实现
目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2020年,V Hayyolalam等人受到自然界黑寡妇交配行为启发,提出了黑寡妇优化算法(Black Widow Optimization Agorithm, BWO)。 2.算法原理 2.1算法思想…...
C#-非托管代码
非托管代码是指不受.NET运行时(CLR)的管理和控制,而是直接由操作系统或其他本机执行环境(如C/C编译的代码)所执行的代码。以下是一些常见的非托管代码的例子: C/C代码:通过使用C或C等编程语言编…...

计算机视觉之三维重建(7)---多视图几何(下)
文章目录 一、透视结构恢复问题1.1 概述1.2 透视结构恢复歧义1.3 代数方法1.4 捆绑调整 二、P3P问题三、随机采样一致性 一、透视结构恢复问题 1.1 概述 1. 透视结构恢复问题:摄像机为透视相机,内外参数均未知。 2. 问题:已知 n n n 个三维…...

AUTOSAR配置工具开发教程 - 开篇
简介 本系列的教程,主要讲述如何自己开发一套简单的AUTOSAR ECU配置工具。适用于有C# WPF基础的人员。 简易介绍见:如何打造AUTOSAR工具_autosar_mod_ecuconfigurationparameters-CSDN博客 实现版本 AUTOSAR 4.0.3AUTOSAR 4.2.2AUTOSAR 4.4.0 效果 …...

配置VM开机自启动
1. 在此电脑-右键选择“管理”-服务和应用程序-服务中找到VMware Workstation Server服务(新版名称也可能是VMware自启动服务,自己找一下,服务属性里有描述信息的),将其启用并选择开机自动启动 新版参考官方文档&…...

工作的第四天
推荐一个软件分配软件 我们看一下如何使用 连接信息 AOC中国官方网站 发现打开 还是这个页面信息,发现最后出现了页面重新定向的问题 我服了 我的码 怎么解决 我想用这个软件 来看看这个软件下载就可以使用 一听到钱我使用的情绪不是很高了 算了不使用 使用…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...

【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理:检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目:RankRAG:Unifying Context Ranking…...

EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...