iOS中宿主APP与录屏扩展进程数据传递方式
背景
在iOS生态系统中,应用程序的功能不再局限于单一的宿主应用,而是可以通过扩展进程实现更丰富的用户体验和功能。其中一种引人注目的扩展是录屏功能,它使用户能够捕捉设备屏幕上的活动,无论是游戏过程、教育演示还是其他应用场景。
然而,实现这一功能并非只涉及宿主应用的单一努力,更需要与扩展进程之间的高效数据传递。这种数据传递是确保录屏功能顺利运作的关键,也是开发者需要深入了解和灵活应用的技术之一。
在本篇博客中,我们将深入探讨iOS宿主APP与扩展进程之间的数据传递方式,了解它们如何协同工作,以实现无缝的录屏体验。
实现方案
方案一:通知
关于Fondation框架中的NSNotificationCenter应该都不陌生,但这里要使用的是Core Fondation框架中的CFNotificationCenterRef,是NSNotificationCenter更底层的实现。使用CFNotificationCenterRef进行数据传递的前提是,主应用和扩展应用需要设置相同的APP Group.
另外需要注意的是,通知只适合传递少量数据,比如一些重连,断开,或者其它少量信息的情况,大量数据的传递不适用,和我们使用NSNotificationCenter一样,应该也没有人使用它来连续不断地传递数据。
以主应用监听扩展应用为例,主应用中需要有下面三个需要实现方法:
- 声明回调block
static NSString * const notificationName = @"notificationName";
void notificationCallback(CFNotificationCenterRef center,void * observer,CFStringRef name,void const * object,CFDictionaryRef userInfo) {NSString *identifier = (__bridge NSString *)name;NSObject *sender = (__bridge NSObject *)observer;NSDictionary *notiUserInfo = @{@"identifier":identifier};//为了简化处理过程,使用常规通知承接一下。[[NSNotificationCenter defaultCenter] postNotificationName:notificationNameobject:senderuserInfo:notiUserInfo];
}
- 注册通知
//参数1:通知中心
//参数2:接收监听的对象。
//参数3:监听的回调
//参数4:通知给观察者的名称
//参数5:对于非Darwin通知中心,要观察的对象。
//参数6:决定应用程序在后台时如何处理通知。- (void)registerNotificationsWithIdentifier:(nullable NSString *)identifier {[self unregisterNotificationsWithIdentifier:identifier];CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();CFStringRef str = (__bridge CFStringRef)identifier;CFNotificationCenterAddObserver(center,(__bridge const void *)(self),notificationCallback,str,NULL,CFNotificationSuspensionBehaviorDeliverImmediately);
}
- 注销通知
- (void)unregisterForNotificationsWithIdentifier:(nullable NSString *)identifier {CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();CFStringRef str = (__bridge CFStringRef)identifier;CFNotificationCenterRemoveObserver(center,(__bridge const void *)(self),str,NULL);
}
而在使用过程中和NSNotificationCenter十分相似:
- 开始注册监听
//MARK: 注册监听插件消息通知
- (void)addUploaderEventMonitor {[self registerForNotificationsWithIdentifier:@"setupSocket"];//这里同时注册了纷发消息的通知,在宿主App中使用[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(broadcastInfo:) name:notificationName object:nil];
}
- 实现监听到通知的回调方法
- (void)broadcastInfo:(NSNotification *)noti {NSDictionary *userInfo = noti.userInfo;NSString *identifier = userInfo[@"identifier"];if ([identifier isEqualToString:@"setupSocket"]) {}
}
- dealloc方法中移除监听
- (void)removeUploaderEventMonitor {[self unregisterForNotificationsWithIdentifier:@"setupSocket"];[[NSNotificationCenter defaultCenter] removeObserver:self name:ScreenHoleNotificationName object:nil];
}- (void)dealloc{[self removeUploaderEventMonitor];
}
而扩展进程只需要负责在合适的时候发送通知:
//MARK: 发送通知
- (void)sendNotificationForMessageWithIdentifier:(nullable NSString *)identifier userInfo:(NSDictionary *)info {CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();CFDictionaryRef userInfo = (__bridge CFDictionaryRef)info;BOOL const deliverImmediately = YES;CFStringRef identifierRef = (__bridge CFStringRef)identifier;CFNotificationCenterPostNotification(center, identifierRef, NULL, userInfo, deliverImmediately);
}- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.[self sendNotificationForMessageWithIdentifier:@"setupSocket" userInfo:@{}];
}
我们可以把数据放到userInfo中传递给主应用。
方案二:KVO和KVC
iOS 7引入了应用群组功能允许创建一个共享沙盒,应用和应用扩展都可以访问它。还可以支持多个应用之间共享数据,但前提是应用必须使用相同的证书进行签名。
该方案同样也离不开应用群组。同时需要借助NSUserDefauls及KVO和KVC。
仍然是以扩展进程向主应用传递数据为例,主应用需要实现以下几个步骤:
- 创建NSUserDefaults对象,并进行监听(注意该对象需要显式的强引用)。
self.userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.m10v.hperf"];
[self.userDefaulst addObserver:self forKeyPath:@"didReceiveVideo" options:NSKeyValueObservingOptionNew context:nil];
- 实现监听的回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"didReceiveVideo"]){NSData *data = [userDefaulst objectForKey:@"didReceiveVideo”];}
}
而对于扩展进程同样也只需要负责发送数据:
- 创建NSUserDefaults对象,并使用KVC给didReceiveVideo进行赋值。
NSUserDefaults * userDefaulst = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.m10v.hperf"];
NSData *videoData = [NSData new];
[userDefaulst setObject:videoData forKey:@"didReceiveVideo"];
方案三:使用NSFileManager
该方案也需要使用到应用群组,核心思想还是通过应用群组创建共享容器来帮我祝我们在两个进程间共享数据。
以扩展进程向主APP传递数据为例,与上面方案的不同之处在于,此方案的侧重点在扩展进程。
扩展进程:
- 创建NSFileManager将数据写入文件
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"your.app.group.identifier"];
NSURL *dataURL = [containerURL URLByAppendingPathComponent:@"recordedData.bin"];// 将 CMSampleBuffer 数据写入文件
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
NSData *data = [NSData dataWithContentsOfURL:dataURL];
[data writeToURL:dataURL atomically:YES];
主应用:
- 读取共享文件中的内容
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"your.app.group.identifier"];
NSURL *dataURL = [containerURL URLByAppendingPathComponent:@"recordedData.bin"];// 从文件读取 CMSampleBuffer 数据
NSData *data = [NSData dataWithContentsOfURL:dataURL];
方案四:使用Socket
使用这种方式进行数据传递比较灵活,但实现起来也比较繁琐,首先需要两个进程之间建立好Socket链接,之后才能进行数据传递的操作。以GCDAysncSocket为例。
以扩展进程发送数据到主应用为例
主应用:
- 属性声明
#import "GCDAsyncSocket.h"
@interface ViewController ()<GCDAsyncSocketDelegate>
@property (nonatomic, strong) GCDAsyncSocket * socket;
@property (nonatomic, strong) NSMutableArray * socketArrayM;
//消息接口队列(串行)
@property (nonatomic, strong) dispatch_queue_t queue;
@end
- 数据初始化,创建队列,初始化socket
- (void)viewDidLoad {[super viewDidLoad];[self initData];[self setupSocket];
}- (void)initData{self.socketArrayM = [NSMutableArray array];self.queue = dispatch_queue_create("com.joyme.panghu.socket", DISPATCH_QUEUE_SERIAL);
}//MARK: 初始化socket
- (void)setupSocket{self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.queue];self.socket.IPv6Enabled = NO;NSError * error;//设置端口号[self.socket acceptOnPort:8999 error:&error];//读取数据[self.socket readDataWithTimeout:-1 tag:0];}
- 实现Socket的代理方法
//MARK: GCDAsyncSocketDelegate//链接断开
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{[self.socketArrayM removeObject:sock];
}//读取流通道关闭
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock{[self.socketArrayM removeObject:sock];
}//接收到新的链接
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{[self.socketArrayM addObject:newSocket];[newSocket readDataWithTimeout:-1 tag:0];
}//读取到数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{NSLog(@"收到:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
扩展进程:
扩展进程同样也需要进行类似的步骤,来创建Socket以进行连接。
- 属性声明
#import "SampleHandler.h"
#import "GCDAsyncSocket.h"@interface SampleHandler ()<GCDAsyncSocketDelegate>@property (nonatomic, strong) GCDAsyncSocket * socket;
///消息接收队列
@property (nonatomic, strong) dispatch_queue_t queue;@end
- 初始化数据
(void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {[self initData];[self setupSocket];
}- (void)initData{self.queue = dispatch_queue_create("com.joyme.panghu.socket_2", DISPATCH_QUEUE_SERIAL);
}- (void)setupSocket{self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.queue];NSError * error;[self.socket connectToHost:@"127.0.0.1" onPort:8999 error:&error];[self.socket readDataWithTimeout:-1 tag:0];
}
- Socket代理方法
//MARK: GCDAsyncSocketDelegate
//链接成功
- (void)socket:(GCDAsyncSocket *)sock didConnectToUrl:(NSURL *)url{}- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{[self.socket readDataWithTimeout:-1 tag:0];[self sendReadData];
}//写入数据成功
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{}//读取到数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{}- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{}
- 发送数据
//MARK: 发送数据
- (void)sendReadData{NSString * ready = @"ready";NSData * data = [ready dataUsingEncoding:NSUTF8StringEncoding];[self.socket writeData:data withTimeout:-1 tag:0];
}
建立连接成功之后,我们会在APP接收到扩展进程传递来的数据
2023-02-10 11:49:50.143508+0800 RecordDemo[7575:1395544] 收到:ready
使用Socket与上面的方法不同之处,首先不需要创建应用群组;数据传递是双向的,建立好链接后,主APP可以向扩展进程发送数据,扩展进程也可以向宿主APP发送数据。
但上面的只是一个基础的数据传递方案,想要相对完整的建立链接流程,我们还需要考虑到端口冲突的问题。实现大量数据的传递还需要其它工具来实现比如(NTESSocketPacket,数据打包,NTESTPCircularBuffer数据解包)。
结语
当涉及主APP与扩展进程之间的数据传递方案时,我们面临着多种选择,并没有一种绝对优越的方案,只有最适合特定场景的方案。在实践中,我们可能需要结合多个方案以满足不同需求。这种灵活性使得我们能够根据具体情境选择最合适的方式,从而更好地满足项目的要求。在选择方案时,务必考虑到实际需求和性能优化,以确保系统的稳定性和效率。综上所述,灵活运用不同的数据传递方案,是构建强大应用的重要一环。
相关文章:
iOS中宿主APP与录屏扩展进程数据传递方式
背景 在iOS生态系统中,应用程序的功能不再局限于单一的宿主应用,而是可以通过扩展进程实现更丰富的用户体验和功能。其中一种引人注目的扩展是录屏功能,它使用户能够捕捉设备屏幕上的活动,无论是游戏过程、教育演示还是其他应用场…...
Windows系统下的可用RADIUS软件-[资源]
RADIUS协议相关原理介绍,可参考博客RADIUS协议原理介绍报文分析配置指导-RFC2865/RFC2866。 本文用于提供和介绍Window系统下几种可用的RADIUS软件。主要涉及软件有radius_ping(绿色免安装版)和WinRadius(绿色免安装版)…...
基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十五:基础数据模块相关功能实现
一、本章内容 本章使用已实现的公共组件实现系统管理中的基础数据中的验证码管理、消息管理等功能。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址: 基于VUE3+Layui从头搭建通用后台管理系统合集-验证码功能实现 3.2 西瓜…...
MAC苹果笔记本电脑如何彻底清理垃圾文件软件?
苹果电脑以其流畅的操作系统和卓越的性能而备受用户喜爱。然而,随着时间的推移,系统可能会积累大量垃圾文件,影响性能。本文将介绍苹果电脑怎么清理垃圾文件的各种方法,以提升系统运行效率。 CleanMyMac X是一款专业的Mac清理软件…...
【Linux C | 文件I/O】文件的打开关闭 | open、creat、colse 函数
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...
【BEV感知】BEVFormer 融合多视角图形的空间特征和时序特征 ECCV 2022
前言 本文分享BEV感知方案中,具有代表性的方法:BEVFormer。 它基于Deformable Attention,实现了一种融合多视角相机空间特征和时序特征的端到端框架,适用于多种自动驾驶感知任务。 主要由3个关键模块组成: BEV Que…...
Amazon Toolkit — CodeWhisperer 使用
tFragment--> 官网:https://aws.amazon.com/cn/codewhisperer/?trkcndc-detail 最近学习了亚马逊云科技的 代码工具,感慨颇多。下面是安装 和使用的分享。 CodeWhisperer,亚马逊推出的实时 AI 编程助手,是一项基于机器学习…...
Flink SQL填坑记2:Flink和MySQL的Bigdata类型不同导致ClassCastException报错
最近在开发Flink SQL的时候,需要关联Kafka事实表和MySQL维表,得到的数据写入Phoenix表中,但是其中有个字段,Kafka表、MySQL表和Phoenix表都是BigData类型,但是在实现的时候却报“java.math.BigInteger cannot be cast to java.lang.Long”异常,从报错信息来看,是由于Big…...
本地MinIO存储服务如何创建Buckets并实现公网访问上传文件
文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统,它可以100%的运行在标准硬件上,即X86等…...
通过https协议访问Tomcat部署并使用Shiro认证的应用跳转登到录页时协议变为http的问题
问题描述: 在最近的一个项目中,有一个存在较久,并且只在内部城域网可访问的一个使用Shiro框架进行安全管理的Java应用,该应用部署在Tomcat服务器上。起初,应用程序可以通过HTTP协议访问,一切运行都没…...
Backend - Django 项目创建 运行
目录 一、配置环境 二、创建 Django 项目 (一)新建文件夹 (二)打开文件夹 (三)打开运行终端 (四)创建基础项目 (五)创建app 1. 安装Django …...
C# .Net学习笔记—— Expression 表达式目录树
一、什么是表达式目录树 (1)Expression我们称为是表达式树,是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算。通常表达式目录树是配合Lambda一起来使用的…...
《论文阅读28》Unsupervised 3D Shape Completion through GAN Inversion
GAN,全称GenerativeAdversarialNetworks,中文叫生成式对抗网络。顾名思义GAN分为两个模块,生成网络以及判别网络,其中 生成网络负责根据随机向量产生图片、语音等内容,产生的内容是数据集中没有见过的,也可…...
一个正则快速找到在ES中使用profile的时产生慢查询的分片
在es中使用profile分析慢查询的时候,往往因为分片过多,或者因为查询条件太复杂,分析的结果几十万行。在kibana上点半天,也找不到一个耗时长的分片。 kibana上可以通过正则来匹配。其实我们只需要匹配到耗时大于10秒的请求。 检索语…...
链接未来:深入理解链表数据结构(一.c语言实现无头单向非循环链表)
在上一篇文章中,我们探索了顺序表这一基础的数据结构,它提供了一种有序存储数据的方法,使得数据的访 问和操作变得更加高效。想要进一步了解,大家可以移步于上一篇文章:探索顺序表:数据结构中的秩序之美 今…...
Python tkinter控件全集之组合选择框 ttk.ComboBox
Tkinter标准库 Tkinter是Python的标准GUI库,也是最常用的Python GUI库之一,提供了丰富的组件和功能,包括窗口、按钮、标签、文本框、列表框、滚动条、画布、菜单等,方便开发者进行图形界面的开发。Tkinter库基于Tk for Unix/Wind…...
Axure之中继器的使用(交互动作reperter属性Item属性)
目录 一.中继器的基本使用 二.中继器的动作(增删改查) 2.1 新增 2.2 删除 2.3 更新行 2.4 效果展示 2.5 模糊查询 三.reperter属性 在Axure中,中继器(Repeater)是一种功能强大的组件,用于创建重复…...
数字化医疗新篇章:构建智能医保支付购药系统
在迎接数字化医疗时代的挑战和机遇中,智能医保支付购药系统的建设显得尤为重要。本文将深入介绍如何通过先进的技术实现,构建一套智能、高效的医保支付购药系统,为全面建设健康中国贡献力量。 1. 引言 随着医疗科技的飞速发展,…...
11_12-Golang中的运算符
**Golang **中的运算符 主讲教师:(大地) 合作网站:www.itying.com** **(IT 营) 我的专栏:https://www.itying.com/category-79-b0.html 1、Golang 内置的运算符 算术运算符关系运算符逻辑运…...
k8s-ingress特性 9
TLS加密 创建证书 测试访问 auth认证 创建认证文件 rewrite重定向 进入域名时,会自动重定向到hostname.html 示例: 测试 版本的升级迭代,之前利用控制器进行滚动更新,在升级过程中无法做到快速回滚 更加平滑的升级࿱…...
【redis】redis系统实现发布订阅的标准模板
目录 简介参数配置代码模板 简介 Redis发布订阅功能是Redis的一种消息传递模式,允许多个客户端之间通过消息通道进行实时的消息传递。在发布订阅模式下,消息的发送者被称为发布者(publisher),而接收消息的客户端被称为…...
Python 时间日期处理库函数
标准库 datetime >>> import datetime >>> date datetime.date(2023, 12, 20) >>> print(date) 2023-12-20 >>> date datetime.datetime(2023, 12, 20) >>> print(date) 2023-12-20 00:00:00 >>> print(date.strfti…...
第二十二章 : Spring Boot 集成定时任务(一)
第二十二章 : Spring Boot 集成定时任务(一) 前言 本章知识点: 介绍使用Spring Boot内置的Scheduled注解来实现定时任务-单线程和多线程;以及介绍Quartz定时任务调度框架:简单定时调度器(Simp…...
关于“Python”的核心知识点整理大全32
目录 12.6.4 调整飞船的速度 settings.py ship.py alien_invasion.py 12.6.5 限制飞船的活动范围 ship.py 12.6.6 重构 check_events() game_functions.py 12.7 简单回顾 12.7.1 alien_invasion.py 12.7.2 settings.py 12.7.3 game_functions.py 12.7.4 ship.py …...
【krita】实时绘画 入门到精通 海报+电商+装修+人物
安装插件 首先打开comfyUI,再打开krita,出现问题提示, 打开 cd custom_nodes 输入命令 安装控件 git clone https://github.com/Acly/comfyui-tooling-nodes.git krita基础设置 设置模型 设置lora (可设置lora强度 增加更多…...
云原生系列2-CICD持续集成部署-GitLab和Jenkins
1、CICD持续集成部署 传统软件开发流程: 1、项目经理分配模块开发任务给开发人员(项目经理-开发) 2、每个模块单独开发完毕(开发),单元测试(测试) 3、开发完毕后,集成部…...
50ms时延工业相机
华睿工业相机A3504CG000 参数配置: 相机端到端理论时延:80ms 厂家同步信息,此款设备帧率上线23fps,单帧时延:43.48ms,按照一图缓存加上传输显示的话,厂家预估时延在:80ms 厂家还有…...
CPU缓存一致性问题
什么是可见性问题? Further Reading :什么是可见性问题? 缓存一致性 内存一致性 内存可见性 顺序一致性区别 CPU缓存一致性问题 由于CPU缓存的出现,很好地解决了处理器与内存速度之间的矛盾,极大地提高了CPU的吞吐能…...
35道HTML高频题整理(附答案背诵版)
1、简述 HTML5 新特性 ? HTML5 是 HTML 的最新版本,它引入了很多新的特性和元素,以提供更丰富的网页内容和更好的用户体验。以下是一些主要的新特性: 语义元素:HTML5 引入了新的语义元素,像 <article&g…...
【powershell】Windows环境powershell 运维之历史文件压缩清理
🦄 个人主页——🎐开着拖拉机回家_Linux,大数据运维-CSDN博客 🎐✨🍁 🪁🍁🪁🍁🪁🍁🪁🍁 🪁🍁🪁&am…...
一级a做爰视频安全网站/网络推广外包要多少钱
ALLEXCEPT函数ALLEXCEPT函数属于“筛选”类函数,隶属于“表函数”,在ALL函数系列家族中,其地位是不可或缺的。EXCEPT翻译成中文是什么意思?表示:除了的意思。因此,这个函数所表达的意思顾名思义,…...
北京建站公司兴田德润很好/云南疫情最新情况
前言 阿里巴巴,作为国内互联网公司的Top,算是业界的标杆,有阿里背景的程序员,也更具有权威性。作为程序员,都清楚阿里对于员工要求有多高,技术人员掌握的技术水平更是望尘莫及。所以,大厂程序员…...
顺义建站设计/个人网站设计毕业论文
牙齿是我们身体的重要组成部分,也是我们咀嚼食物的主要方式。我们必须保护自己的牙齿。当我们发现我们的牙齿有蛀牙或牙齿不规则时,想要补牙或者矫正牙齿的时候。我们需要牙套来固定牙齿。当牙套钢丝断了的时候。我们应该及时更换它们,否则会…...
独山县哪里有做网站的/百度收录api怎么提交
思路:题意抽象出来就是,找到一个最小边权值为d,删除大于d的边,使得剩下的联通块的个数不大于k个。这个题我们可以发现d越大连通块越少,d与连通块个数成单调函数。 那么可以二分d,然后dfs/bfs求联通块个数判…...
网站模板后台怎么做/营销咨询服务
10.3去看了穗港澳动漫展,08年广州难得的一次动漫展......比较近距离地看了下川みくに,气氛很好。贴个相册地址,有兴趣的可以去看下哇。[url]http://photo.163.com/photos/squareram/160820460/[/url]转载于:https://blog.51cto.com/zeroyang/…...
沧州做网站的/推广普通话图片
ansible的copy模块批量下发文件copy模块的参数,ansible 主机组 -m copy -a src: 指定源文件或目录dest: 指定目标服务器的文件或目录backup: 是否要备份owner: 拷贝到目标服务器后,文件或目录的所属用户group: 拷贝到目标服务器后,文件或目录…...