1# IPC/RPC组件<a name="ZH-CN_TOPIC_0000001103602398"></a> 2 3- [简介](#section11660541593) 4- [系统架构](#section1950291414611) 5- [目录](#section161941989596) 6- [约束](#section119744591305) 7- [编译构建](#section137768191623) 8- [说明](#section1312121216216) 9 - [接口说明](#section1551164914237) 10 - [使用说明](#section129654513264) 11 12- [相关仓](#section1371113476307) 13 14## 简介<a name="section11660541593"></a> 15 16IPC(Inter-Process Communication)与RPC(Remote Procedure Call)机制用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,而后者使用软总线驱动,用于跨设备跨进程通信。IPC和RPC通常采用客户端-服务器(Client-Server)模型,服务请求方(Client)可获取提供服务提供方(Server)的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。通常,系统能力(System Ability)Server侧会先注册到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。三方应用可以使用FA提供的接口绑定服务提供方的Ability,获取代理,进行通信。下文使用Proxy表示服务请求方,Stub表示服务提供方。 17 18## 系统架构<a name="section1950291414611"></a> 19 20**图 1** IPC通信机制架构图<a name="fig312319321710"></a> 21 22 23## 目录<a name="section161941989596"></a> 24 25``` 26/foundation/communication/ipc 27├── interfaces # 对外接口存放目录 28│ └── innerkits # 对内部子系统暴露的头文件存放目录 29│ ├── ipc_core # ipc 接口存放目录 30│ └── libdbinder # dbinder 接口存放目录 31├── ipc # ipc 框架代码 32│ ├── native # ipc native 实现存放目录 33│ ├── src # ipc native 源代码存放目录 34│ └── test # ipc native 单元测试用例存放目录 35│ └── test # ipc native 模块测试用例存放目录 36├── service # dbinder 实现存放目录 37│ └── dbinder # dbinder 源代码存放目录 38``` 39 40## 约束<a name="section119744591305"></a> 41 421. 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存。 432. 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备。 44 45## 编译构建<a name="section137768191623"></a> 46 47**JS侧依赖** 48 49``` 50import rpc from "@ohos.rpc" 51``` 52 53**Native侧编译依赖** 54 55sdk依赖: 56 57``` 58external_deps = [ 59 "ipc:ipc_core", 60] 61``` 62 63此外, IPC/RPC依赖的refbase实现在公共基础库下,请增加对utils的依赖: 64 65``` 66external_deps = [ 67 "c_utils:utils", 68] 69``` 70 71**Rust侧编译依赖** 72 73``` 74external_deps = [ "ipc:ipc_rust" ] 75``` 76 77## 说明<a name="section1312121216216"></a> 78 79**JS侧实现跨进程通信基本步骤:** 80 811. 获取代理 82 83 使用ohos.app.ability.UIAbility提供的globalThis.context.connectServiceExtensionAbility方法绑定Ability,在参数里指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的情况,还需要指定所在设备的NetworkId。用户需要在服务端的onConnect方法里返回一个继承自ohos.rpc.RemoteObject的对象,此对象会在其onRemoteMessageRequest方法里接收到请求。 84 852. 发送请求 86 87 客户端在globalThis.context.connectServiceExtensionAbility参数指定的回调函数接收到代理对象后,使用ohos.rpc模块提供的方法完成RPC通信,其中MessageParcel提供了读写各种类型数据的方法,IRemoteObject提供了发送请求的方法,RemoteObject提供了处理请求的方法onRemoteRequest,用户需要重写。 88 89**Native侧实现跨进程通信的基本步骤:** 90 911. 定义接口类 92 93 接口类继承IRemoteBroker,定义描述符、业务函数和消息码。 94 952. 实现服务提供端\(Stub\) 96 97 Stub继承IRemoteStub\(Native\),除了接口类中未实现方法外,还需要实现AsObject方法及OnRemoteRequest方法。 98 993. 实现服务请求端\(Proxy\) 100 101 Proxy继承IRemoteProxy\(Native\),封装业务函数,调用SendRequest将请求发送到Stub。 102 1034. 注册SA 104 105 服务提供方所在进程启动后,申请SA的唯一标识,将Stub注册到SAMgr。 106 1075. 获取SA 108 1096. 通过SA的标识和设备NetworkId,从SAMgr获取Proxy,通过Proxy实现与Stub的跨进程通信。 110 111**Rust侧实现跨进程通信的基本步骤:** 112 1131. 定义接口 114 115 继承IPC框架的IRemoteBroker特征,定义一个业务自己的trait,在此trait中定义proxy和stub之间的IPC方法。 116 1172. 定义服务 118 119 和c++ 定义的服务类似,Rust服务相关的类型有两个; 120 121 1)由业务提供名字,通过宏define_remote_object定义。 122 123 2)由业务定义,框架不关心其内容,只要求其必须实现步骤1中定义的接口trait。 124 1253. 定义代理 126 127 代理的定义由业务提供名字,通过宏define_remote_object定义代理的类型。 128 1294. 创建并注册服务 130 131 服务定义完成后,只有注册到samgr后,其他进程才能获取该服务的代理,完成和该服务的通信。 132 1335. 获取代理 134 135 通过向samgr发起请求,可以获取到指定服务的代理对象,之后便可以调用该代理对象的IPC方法实现和服务的通信。 136 1376. 测试服务能力 138 139### 接口说明<a name="section1551164914237"></a> 140 141**表 1** JS侧IPC关键API 142 143| 模块 | 方法 | 功能说明 | 144| -------------------------- | ------------------------------------------------------------ | ------------------------------------------- | 145| ohos.app.ability.UIAbility | globalThis.context.connectServiceExtensionAbility(want: Want, options:ConnectOptions ): number | 绑定指定的Ability,在回调函数里接收代理对象 | 146| ohos.rpc.RemoteObject | onRemoteMessageRequest(code: number, data: MessageParcel, reply: MessageParcel, options: MessageOption): boolean \| Promise<boolean> | 服务端处理请求,返回结果 | 147| ohos.rpc.IRemoteObject | sendRequest(code: number, data: MessageParcel, reply: MessageParcel, options: MessageOption): Promise<SendRequestResult> | 发送请求,在期约里接收结果 | 148| ohos.rpc.IRemoteObject | sendRequest(code: number, data: MessageParcel, reply: MessageParcel, options: MessageOption, callback: AsyncCallback<SendRequestResult>): void | 发送请求,在回调函数里接收结果 | 149| ohos.rpc.MessageParcel | writeRemoteObject(object: IRemoteObject): boolean | 序列化IRemoteObject对象 | 150| ohos.rpc.MessageParcel | readRemoteObject(): IRemoteObject | 反序列化IRemoteObject对象 | 151 152 153 154**表 2** Native侧IPC接口 155 156<a name="table178849240013"></a> 157 158<table><thead align="left"><tr id="row6884924608"><th class="cellrowborder" valign="top" width="14.12141214121412%" id="mcps1.2.4.1.1"><p id="p98846241706"><a name="p98846241706"></a><a name="p98846241706"></a>类/接口</p> 159</th> 160<th class="cellrowborder" valign="top" width="52.54525452545254%" id="mcps1.2.4.1.2"><p id="p1488482414020"><a name="p1488482414020"></a><a name="p1488482414020"></a>方法</p> 161</th> 162<th class="cellrowborder" valign="top" width="33.33333333333333%" id="mcps1.2.4.1.3"><p id="p388516244016"><a name="p388516244016"></a><a name="p388516244016"></a>功能说明</p> 163</th> 164</tr> 165</thead> 166<tbody><tr id="row15885824402"><td class="cellrowborder" valign="top" width="14.12141214121412%" headers="mcps1.2.4.1.1 "><p id="p08859241008"><a name="p08859241008"></a><a name="p08859241008"></a>IRemoteBroker</p> 167</td> 168<td class="cellrowborder" valign="top" width="52.54525452545254%" headers="mcps1.2.4.1.2 "><p id="p388572412010"><a name="p388572412010"></a><a name="p388572412010"></a>sptr<IRemoteObject> AsObject()</p> 169</td> 170<td class="cellrowborder" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.3 "><p id="p13885724405"><a name="p13885724405"></a><a name="p13885724405"></a>返回通信对象。派生类需要实现,Stub端返回RemoteObject对象本身,Proxy端返回代理对象。</p> 171</td> 172</tr> 173<tr id="row138859241808"><td class="cellrowborder" valign="top" width="14.12141214121412%" headers="mcps1.2.4.1.1 "><p id="p1888515245012"><a name="p1888515245012"></a><a name="p1888515245012"></a>IRemoteStub</p> 174</td> 175<td class="cellrowborder" valign="top" width="52.54525452545254%" headers="mcps1.2.4.1.2 "><p id="p1388516240011"><a name="p1388516240011"></a><a name="p1388516240011"></a>virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)</p> 176</td> 177<td class="cellrowborder" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.3 "><p id="p1188582414016"><a name="p1188582414016"></a><a name="p1188582414016"></a>请求处理方法,派生类需要重写,处理Proxy的请求并返回结果。</p> 178</td> 179</tr> 180<tr id="row108856241904"><td class="cellrowborder" valign="top" width="14.12141214121412%" headers="mcps1.2.4.1.1 "><p id="p6885924609"><a name="p6885924609"></a><a name="p6885924609"></a>IRemoteProxy</p> 181</td> 182<td class="cellrowborder" valign="top" width="52.54525452545254%" headers="mcps1.2.4.1.2 "> </td> 183<td class="cellrowborder" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.3 "><p id="p688592413018"><a name="p688592413018"></a><a name="p688592413018"></a>业务Proxy类派生自IRemoteProxy类。</p> 184</td> 185</tr> 186</tbody> 187</table> 188 189### 使用说明<a name="section129654513264"></a> 190 191**JS侧使用说明** 192 1931. 客户端构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要目标设备NetworkId。构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数。使用UIAbility提供的接口绑定Ability。 194 195 ``` 196 import rpc from "@ohos.rpc" 197 198 let proxy = null 199 let connectId = null 200 201 // 单个设备 202 let want = { 203 // 包名和组件名写实际的值 204 "bundleName": "ohos.rpc.test.server", 205 "abilityName": "ohos.rpc.test.server.ServiceAbility", 206 } 207 let connect = { 208 onConnect:function(elementName, remote) { 209 proxy = remote 210 }, 211 onDisconnect:function(elementName) { 212 }, 213 onFailed:function() { 214 proxy = null 215 } 216 } 217 connectId = globalThis.context.connectServiceExtensionAbility(want, connect) 218 219 // 如果是跨设备绑定,可以使用deviceManager获取目标设备NetworkId 220 import deviceManager from '@ohos.distributedHardware.deviceManager' 221 function deviceManagerCallback(deviceManager) { 222 let deviceList = deviceManager.getTrustedDeviceListSync() 223 let deviceId = deviceList[0].deviceId 224 let want = { 225 "bundleName": "ohos.rpc.test.server", 226 "abilityName": "ohos.rpc.test.service.ServiceAbility", 227 "deviceId": deviceId, 228 "flags": 256 229 } 230 connectId = globalThis.context.connectServiceExtensionAbility(want, connect) 231 } 232 // 第一个参数是本应用的包名,第二个参数是接收deviceManager的回调函数 233 deviceManager.createDeviceManager("ohos.rpc.test", deviceManagerCallback) 234 ``` 235 236 237 2382. 服务端被绑定的Ability在onConnect方法里返回继承自rpc.RemoteObject的对象,该对象需要实现onRemoteMessageRequest方法,处理客户端的请求。 239 240 ``` 241 import rpc from "@ohos.rpc" 242 onConnect(want: Want) { 243 var robj:rpc.RemoteObject = new Stub("rpcTestAbility") 244 return robj 245 } 246 class Stub extends rpc.RemoteObject { 247 constructor(descriptor) { 248 super(descriptor) 249 } 250 onRemoteMessageRequest(code, data, reply, option) { 251 // 根据code处理客户端的请求 252 return true 253 } 254 } 255 ``` 256 257 258 2593. 客户端在onConnect回调里接收到代理对象,调用sendRequest方法发起请求,在期约或者回调函数里接收结果。 260 261 ``` 262 import rpc from "@ohos.rpc" 263 // 使用期约 264 let option = new rpc.MessageOption() 265 let data = rpc.MessageParcel.create() 266 let reply = rpc.MessageParcel.create() 267 // 往data里写入参数 268 proxy.sendRequest(1, data, reply, option) 269 .then(function(result) { 270 if (result.errCode != 0) { 271 console.error("send request failed, errCode: " + result.errCode) 272 return 273 } 274 // 从result.reply里读取结果 275 }) 276 .catch(function(e) { 277 console.error("send request got exception: " + e) 278 } 279 .finally(() => { 280 data.reclaim() 281 reply.reclaim() 282 }) 283 284 // 使用回调函数 285 function sendRequestCallback(result) { 286 try { 287 if (result.errCode != 0) { 288 console.error("send request failed, errCode: " + result.errCode) 289 return 290 } 291 // 从result.reply里读取结果 292 } finally { 293 result.data.reclaim() 294 result.reply.reclaim() 295 } 296 } 297 let option = new rpc.MessageOption() 298 let data = rpc.MessageParcel.create() 299 let reply = rpc.MessageParcel.create() 300 // 往data里写入参数 301 proxy.sendRequest(1, data, reply, option, sendRequestCallback) 302 ``` 303 304 305 3064. IPC通信结束后,使用UIAbility的接口断开连接。 307 308 ``` 309 import rpc from "@ohos.rpc" 310 globalThis.context.disconnectServiceExtensionAbility(connectionId).then((data) => { 311 console.info('disconnectServiceExtensionAbility success'); 312 }).catch((error) => { 313 console.error('disconnectServiceExtensionAbility failed'); 314 }) 315 ``` 316 317 318 319**Native侧使用说明** 320 3211. 定义IPC接口ITestAbility 322 323 IPC接口继承IPC基类接口IRemoteBroker,接口里定义描述符、业务函数和消息码,其中业务函数在Proxy端和Stub端都需要实现。 324 325 ``` 326 class ITestAbility : public IRemoteBroker { 327 public: 328 // DECLARE_INTERFACE_DESCRIPTOR是必须的, 入参需使用std::u16string; 329 DECLARE_INTERFACE_DESCRIPTOR(u"test.ITestAbility"); // DESCRIPTOR接口描述符建议使用"组件名.类名"的格式 330 int TRANS_ID_PING_ABILITY = 1; // 定义消息码 331 virtual int TestPingAbility(const std::u16string &dummy) = 0; // 定义业务函数 332 }; 333 ``` 334 335 336 3372. 定义和实现服务端TestAbilityStub 338 339 该类是和IPC框架相关的实现,需要继承自IRemoteStub<ITestAbility\>。Stub端作为接收请求的一端,需重写OnRemoteRequest方法用于接收客户端调用。 340 341 ``` 342 class TestAbilityStub : public IRemoteStub<ITestAbility> { 343 public: 344 virtual int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; 345 int TestPingAbility(const std::u16string &dummy) override; 346 }; 347 348 int TestAbilityStub::OnRemoteRequest(uint32_t code, 349 MessageParcel &data, MessageParcel &reply, MessageOption &option) 350 { 351 if (data.ReadInterfaceToken() != GetDescriptor()) { // 校验是否为本服务的接口描述符,避免中继攻击 352 return -1; 353 } 354 switch (code) { 355 case TRANS_ID_PING_ABILITY: { 356 std::u16string dummy = data.ReadString16(); 357 int result = TestPingAbility(dummy); 358 reply.WriteInt32(result); 359 return 0; 360 } 361 default: 362 return IPCObjectStub::OnRemoteRequest(code, data, reply, option); 363 } 364 } 365 ``` 366 367 368 3693. 定义服务端业务函数具体实现类TestAbility 370 371 ``` 372 class TestAbility : public TestAbilityStub { 373 public: 374 int TestPingAbility(const std::u16string &dummy); 375 } 376 377 int TestAbility::TestPingAbility(const std::u16string &dummy) { 378 return 0; 379 } 380 ``` 381 382 383 3844. 定义和实现客户端TestAbilityProxy 385 386 该类是Proxy端实现,继承自IRemoteProxy<ITestAbility\>,调用SendRequest接口向Stub端发送请求,对外暴露服务端提供的能力。 387 388 ``` 389 class TestAbilityProxy : public IRemoteProxy<ITestAbility> { 390 public: 391 explicit TestAbilityProxy(const sptr<IRemoteObject> &impl); 392 int TestPingService(const std::u16string &dummy) override; 393 private: 394 static inline BrokerDelegator<TestAbilityProxy> delegator_; // 方便使用iface_cast宏 395 } 396 397 TestAbilityProxy::TestAbilityProxy(const sptr<IRemoteObject> &impl) 398 : IRemoteProxy<ITestAbility>(impl) 399 { 400 } 401 402 int TestAbilityProxy::TestPingService(const std::u16string &dummy) { 403 MessageOption option; 404 MessageParcel dataParcel, replyParcel; 405 if(!dataParcel.WriteInterfaceToken(GetDescriptor())) { // 所有对外接口的proxy实现都要写入接口描述符,用于stub端检验 406 return -1; 407 } 408 if(!dataParcel.WriteString16(dummy)) { 409 return -1; 410 } 411 int error = Remote()->SendRequest(TRANS_ID_PING_ABILITY, dataParcel, replyParcel, option); 412 int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1; 413 return result; 414 } 415 ``` 416 417 418 4195. 同步调用与异步调用 420 421 MessageOption作为发送接口(原型如下)的入参,可设定同步(TF\_SYNC)、异步(TF\_ASYNC),默认情况下设定为同步,其余可通过MessageOption构造方法或void SetFlags\(int flags\)设定。 422 423 ``` 424 int SendRequest(uint32_t code, MessageParcel &data, 425 MessageParcel &reply, MessageOption &option) override; 426 MessageOption option; 427 option.setFlags(option.TF_ASYNC); 428 ``` 429 430 431 4326. SA注册与启动 433 434 SA需要将自己的TestAbilityStub实例通过AddSystemAbility接口注册到SystemAbilityManager,设备内与分布式的注册参数不同。 435 436 ``` 437 // 注册到本设备内 438 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 439 samgr->AddSystemAbility(said, new TestAbility()); 440 441 // 在组网场景下,会被同步到其他设备上 442 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 443 ISystemAbilityManager::SAExtraProp saExtra; 444 saExtra.isDistributed = true; // 设置为分布式SA 445 int result = samgr->AddSystemAbility(said, new TestAbility(), saExtra); 446 ``` 447 448 449 4507. SA获取与调用 451 452 通过SystemAbilityManager的GetSystemAbility方法可获取到对应SA的代理IRemoteObject,然后构造TestAbilityProxy即可。 453 454 ``` 455 // 获取本设备内注册的SA的proxy 456 sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 457 sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(said); 458 sptr<ITestAbility> testAbility = iface_cast<ITestAbility>(remoteObject); // 使用iface_cast宏转换成具体类型 459 460 // 获取其他设备注册的SA的Proxy 461 sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); 462 sptr<IRemoteObject> remoteObject = samgr->GetSystemAbility(sdid, deviceId); // deviceId是指定设备的标识符 463 sptr<TestAbilityProxy> proxy(new TestAbilityProxy(remoteObject)); // 直接构造具体Proxy 464 ``` 465 466**Rust侧使用说明** 467 468以下为CALCULATOR服务的完整开发步骤。 469 4701. 定义接口 471 472 继承IPC框架IRemoteBroker特征,定义一个业务自己的trait,该trait中定义proxy和stub之间的IPC方法。示例如下定义了ICalc trait: 473 474 ``` 475 /// Function between proxy and stub of ICalcService 476 pub trait ICalc: IRemoteBroker { 477 /// Calc add num1 + num2 478 fn add(&self, num1: i32, num2: i32) -> IpcResult<i32>; 479 /// Calc sub num1 + num2 480 fn sub(&self, num1: i32, num2: i32) -> IpcResult<i32>; 481 /// Calc mul num1 + num2 482 fn mul(&self, num1: i32, num2: i32) -> IpcResult<i32>; 483 /// Calc div num1 + num2 484 fn div(&self, num1: i32, num2: i32) -> IpcResult<i32>; 485 } 486 ``` 487 488 1.1 定义枚举ICalcCode 489 490 ICalcCode枚举中的变体表示calculator服务的不同功能。当然这一步不是必须的,但是为了提高代码的可读性,建议按照如下方法为每一个IPC方法定义code,示例如下: 491 492 ``` 493 /// Function code of ICalcService 494 pub enum ICalcCode { 495 /// add 496 CodeAdd = FIRST_CALL_TRANSACTION, // 由IPC框架定义,值为1,建议业务使用该值作为第一个IPC方法的code 497 /// sub 498 CodeSub, 499 /// mul 500 CodeMul, 501 /// div 502 CodeDiv, 503 } 504 ``` 505 506 1.2 ICalCode转换 507 508 ICalCode实现TryFrom trait,可以实现u32类型到CalCode枚举类型的转换。 509 510 ``` 511 impl TryFrom<u32> for ICalcCode { 512 type Error = IpcStatusCode; 513 fn try_from(code: u32) -> IpcResult<Self> { 514 match code { 515 _ if code == ICalcCode::CodeAdd as u32 => Ok(ICalcCode::CodeAdd), 516 _ if code == ICalcCode::CodeSub as u32 => Ok(ICalcCode::CodeSub), 517 _ if code == ICalcCode::CodeMul as u32 => Ok(ICalcCode::CodeMul), 518 _ if code == ICalcCode::CodeDiv as u32 => Ok(ICalcCode::CodeDiv), 519 _ => Err(IpcStatusCode::Failed), 520 } 521 } 522 } 523 ``` 524 5252. 定义服务 526 527 和c++ 定义的服务类似,Rust服务相关的类型有两个: 528 529 1)由业务提供名字,通过宏define_remote_object!定义,如本例中的CalcStub。 530 531 2)由业务定义,框架不关心其内容,只要求其必须实现步骤1中定义的接口trait,如本例中的CalcService。 532 533 2.1 定义CalcService服务 534 535 CalcService的定义如下所示,实现了ICalc和IRemoteBroker特征,服务中没有任何成员,如有需要可以根据业务需要进行定义。 536 537 ``` 538 /// example.calc.ipc.ICalcService type 539 pub struct CalcService; 540 // 实现ICalc特征 541 impl ICalc for CalcService { 542 fn add(&self, num1: i32, num2: i32) -> IpcResult<i32> { 543 Ok(add(&num1, &num2)) 544 } 545 fn sub(&self, num1: i32, num2: i32) -> IpcResult<i32> { 546 Ok(sub(&num1, &num2)) 547 } 548 fn mul(&self, num1: i32, num2: i32) -> IpcResult<i32> { 549 Ok(mul(&num1, &num2)) 550 } 551 fn div(&self, num1: i32, num2: i32) -> IpcResult<i32> { 552 Ok(div(&num1, &num2)) 553 } 554 } 555 // 实现IRemoteBroker特征 556 impl IRemoteBroker for CalcService {} 557 /// add num1 + num2 558 pub fn add(num1: &i32, num2: &i32) -> i32 { 559 num1 + num2 560 } 561 /// sub num1 + num2 562 pub fn sub(num1: &i32, num2: &i32) -> i32 { 563 num1 - num2 564 } 565 /// mul num1 + num2 566 pub fn mul(num1: &i32, num2: &i32) -> i32 { 567 num1 * num2 568 } 569 /// div num1 + num2 570 pub fn div(num1: &i32, num2: &i32) -> i32 { 571 match num2 { 572 0 => { 573 println!("Zero cannot be divided"); 574 -1 575 }, 576 _ => num1 / num2, 577 } 578 } 579 ``` 580 581 2.2 实现on_icalc_remote_request()方法 582 583 当服务收到IPC请求,IPC框架会回调该方法,业务在该方法中完成如下处理: 584 585 1)完成参数的解析。 586 587 2)调用具体的服务IPC方法。 588 589 3)将处理结果写会reply。 590 591 示例代码如下: 592 593 ``` 594 fn on_icalc_remote_request(stub: &dyn ICalc, code: u32, data: &BorrowedMsgParcel, 595 reply: &mut BorrowedMsgParcel) -> IpcResult<()> { 596 match code.try_into()? { 597 ICalcCode::CodeAdd => { 598 let num1: i32 = data.read().expect("Failed to read num1 in addition operation"); 599 let num2: i32 = data.read().expect("Failed to read num2 in addition operation"); 600 let ret = stub.add(num1, num2)?; 601 reply.write(&ret)?; 602 Ok(()) 603 } 604 ICalcCode::CodeSub => { 605 let num1: i32 = data.read().expect("Failed to read num1 in subtraction operation"); 606 let num2: i32 = data.read().expect("Failed to read num1 in subtraction operation"); 607 let ret = stub.sub(num1, num2)?; 608 reply.write(&ret)?; 609 Ok(()) 610 } 611 ICalcCode::CodeMul => { 612 let num1: i32 = data.read().expect("Failed to read num1 in multiplication operation"); 613 let num2: i32 = data.read().expect("Failed to read num1 in multiplication operation"); 614 let ret = stub.mul(num1, num2)?; 615 reply.write(&ret)?; 616 Ok(()) 617 } 618 ICalcCode::CodeDiv => { 619 let num1: i32 = data.read().expect("Failed to read num1 in division operation"); 620 let num2: i32 = data.read().expect("Failed to read num1 in division operation"); 621 let ret = stub.div(num1, num2)?; 622 reply.write(&ret)?; 623 Ok(()) 624 } 625 } 626 } 627 ``` 628 6293. 定义代理 630 631 代理的定义由业务提供名字,通过宏define_remote_object定义代理的类型,业务需要为代理实现ICalc。示例如下: 632 633 ``` 634 impl ICalc for CalcProxy { 635 fn add(&self, num1: i32, num2: i32) -> IpcResult<i32> { 636 let mut data = MsgParcel::new().expect("MsgParcel should success"); 637 data.write(&num1)?; 638 data.write(&num2)?; 639 let reply = self.remote.send_request(ICalcCode::CodeAdd as u32, 640 &data, false)?; 641 let ret: i32 = reply.read().expect("need reply i32"); 642 Ok(ret) 643 } 644 fn sub(&self, num1: i32, num2: i32) -> IpcResult<i32> { 645 let mut data = MsgParcel::new().expect("MsgParcel should success"); 646 data.write(&num1)?; 647 data.write(&num2)?; 648 let reply = self.remote.send_request(ICalcCode::CodeSub as u32, 649 &data, false)?; 650 let ret: i32 = reply.read().expect("need reply i32"); 651 Ok(ret) 652 } 653 fn mul(&self, num1: i32, num2: i32) -> IpcResult<i32> { 654 let mut data = MsgParcel::new().expect("MsgParcel should success"); 655 data.write(&num1)?; 656 data.write(&num2)?; 657 let reply = self.remote.send_request(ICalcCode::CodeMul as u32, 658 &data, false)?; 659 let ret: i32 = reply.read().expect("need reply i32"); 660 Ok(ret) 661 } 662 fn div(&self, num1: i32, num2: i32) -> IpcResult<i32> { 663 let mut data = MsgParcel::new().expect("MsgParcel should success"); 664 data.write(&num1)?; 665 data.write(&num2)?; 666 let reply = self.remote.send_request(ICalcCode::CodeDiv as u32, 667 &data, false)?; 668 let ret: i32 = reply.read().expect("need reply i32"); 669 Ok(ret) 670 } 671 } 672 ``` 673 674 上述对象最终通过宏define_remote_object调用,将业务定义的类型和IPC框架进行结合,宏define_remote_object提供了如下几个关键信息: 675 676 1)服务的接口特征ICalc。 677 678 2)服务的描述符为“example.calc.ipc.ICalcService”。 679 680 3)Rust服务类型名为CalcStub。 681 682 4)服务处理IPC请求的入口方法为on_icalc_remote_request。 683 684 5)代理类型为CalcProxy。 685 686 示例代码如下: 687 688 ``` 689 define_remote_object!( 690 ICalc["example.calc.ipc.ICalcService"] { 691 stub: CalcStub(on_icalc_remote_request), 692 proxy: CalcProxy, 693 } 694 ); 695 ``` 696 6974. 创建并注册服务 698 699 服务定义完成后,只有注册到samgr后,其他进程才能获取该服务的代理,然后完成和该服务的通信。示例代码如下: 700 701 ``` 702 fn main() { 703 init_access_token(); 704 // 创建服务对象,最终的服务对象为CalcStub 705 let service = CalcStub::new_remote_stub(CalcService).expect("create CalcService success"); 706 // 向samgr注册服务 707 add_service(&service.as_object().expect("get ICalc service failed"), 708 EXAMPLE_IPC_CALC_SERVICE_ID).expect("add server to samgr failed"); 709 println!("join to ipc work thread"); 710 // 将主线程转换为IPC线程,至此服务所在进程陷入循环 711 join_work_thread(); 712 } 713 ``` 714 715 注意:add_service为IPC 框架提供的临时调试接口,该接口应该由samgr模块提供。 716 7175. 获取代理 718 719 通过向samgr发起请求,可以获取到指定服务的代理对象,之后便可以调用该代理对象的IPC方法实现和服务的通信。示例代码如下: 720 721 ``` 722 fn get_calc_service() -> RemoteObjRef<dyn ICalc> 723 { 724 let object = get_service(EXAMPLE_IPC_CALC_SERVICE_ID).expect("get icalc service failed"); 725 let remote = <dyn ICalc as FromRemoteObj>::try_from(object); 726 let remote = match remote { 727 Ok(x) => x, 728 Err(error) => { 729 println!("convert RemoteObj to CalcProxy failed: {}", error); 730 panic!(); 731 } 732 }; 733 remote 734 } 735 ``` 736 737 注意:示例中的get_service()为IPC框架提供的临时接口,该接口由samgr模块提供。 738 7396. 测试Calculartor服务能力 740 741 当测试用例Calculator_Ability pass表示CalcService 服务能力ok。 742 743 ``` 744 #[test] 745 fn calculator_ability() { 746 let remote = get_calc_service(); 747 // add 748 let ret = remote.add(5, 5).expect("add failed"); 749 assert_eq!(ret, 10); 750 // sub 751 let ret = remote.sub(5, 5).expect("sub failed"); 752 assert_eq!(ret, 0); 753 // mul 754 let ret = remote.mul(5, 5).expect("mul failed"); 755 assert_eq!(ret, 25); 756 // div 757 let ret = remote.div(5, 5).expect("div failed"); 758 assert_eq!(ret, 1); 759 } 760 ``` 761 762## 相关仓<a name="section1371113476307"></a> 763 764分布式软总线子系统 765 766**communication\_ipc** 767 768[commonlibrary\_c\_utils](https://gitee.com/openharmony/commonlibrary_c_utils) 769 770[distributedschedule\_samgr](https://gitee.com/openharmony/distributedschedule_samgr) 771 772