iOS进程间通信之CFMessagePort

 

iOS系统是出了名的封闭,每个应用的活动范围被严格地限制在各自的沙盒中。尽管如此,iOS还是提供了若干进程间通信机制,CFMessagePort就是其中之一。

从类名可以看出,CFMessagePort属于Core Foundation层的东西,其实现部分是开源的,代码在可以在苹果的开源代码库中找到。

使用方式

1、消息接收者

CFMessagePort端口消息的接收者需要实现以下功能:

1.1 注册监听

消息接收者需要通过以下方式注册消息监听:

-(void)startListenning
{if (0 != mMsgPortListenner && CFMessagePortIsValid(mMsgPortListenner)){CFMessagePortInvalidate(mMsgPortListenner);}mMsgPortListenner = CFMessagePortCreateLocal(kCFAllocatorDefault,CFSTR(LOCAL_MACH_PORT_NAME),onRecvMessageCallBack, NULL, NULL);CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, mMsgPortListenner, 0);CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);NSLog(@"start listenning");
}

其中LOCAL_MACH_PORT_NAME的定义为:

#define LOCAL_MACH_PORT_NAME    "com.wangzz.demo"

经过查看源码发现,CFMessagePort实际上是通过mach port实现的。Mach port是iOS系统提供的基于端口的输入源,可用于线程或进程间通讯。而Runloop支持的输入源类型中就包括基于端口的输入源,因此可以使用Runloop做为CFMessagePort端口源事件的监听者。

上述代码有几点需要说明:

  • 通过CFMessagePortCreateLocal可以创建一个本地CFMessagePortRef对象

  • CFMessagePort对象是靠一个字符串来唯一标识的,这一点非常重要,在这里字符串是由宏LOCAL_MACH_PORT_NAME定义的;

  • 创建CFMessagePort对象的同时设置了端口源事件的回调函数onRecvMessageCallBack,用于处理端口源事件;

  • 将创建的对象作为输入源添加到Runloop中,从而实现对端口源事件的监听,当Runloop收到对应的端口源事件时,会调用上一步中指定的回调方法;

1.2 实现回调方法

回调函数为CFMessagePortCallBack类型,其定义部分为:

typedef CFDataRef (*CFMessagePortCallBack) (CFMessagePortRef local,SInt32 msgid,CFDataRef data,void *info
);

各个参数的含义为:

  • CFMessagePortRef local

当前接收消息的CFMessagePortRef对象。

  • SInt32 msgid

这个字段非常有用,用于标识消息。如果通信双方进程约定号每个msgid对应的数据结构,即可实现较为复杂的通信。

  • CFDataRef data

通信的真正数据部分。

  • void *info

为使用CFMessagePortCreateLocal方法创建port端口时指定的CFMessagePortContext对象的info字段,通常为空。

该回调方法可以返回一个CFDataRef类型的数据给port消息的发送者,以实现有效的双方通信,这一点也非常重要。

我的回调函数onRecvMessageCallBack的实现:

CFDataRef onRecvMessageCallBack(CFMessagePortRef local,SInt32 msgid,CFDataRef cfData, void*info)
{NSLog(@"onRecvMessageCallBack is called");NSString *strData = nil;if (cfData){const UInt8  * recvedMsg = CFDataGetBytePtr(cfData);strData = [NSString stringWithCString:(char *)recvedMsg encoding:NSUTF8StringEncoding];/**实现数据解析操作**/NSLog(@"receive message:%@",strData);}//为了测试,生成返回数据NSString *returnString = [NSString stringWithFormat:@"i have receive:%@",strData];const char* cStr = [returnString UTF8String];NSUInteger ulen = [returnString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];CFDataRef sgReturn = CFDataCreate(NULL, (UInt8 *)cStr, ulen);return sgReturn;
}

该方法实现的较为简单,解析约定的数据(测试代码中约定传送的是string),为了测试,同时生成一个CFDataRef数据返回给port消息的发送者。

1.3 取消端口监听

可以通过如下方式取消对port端口的监听:

- (void)endLisenning
{CFMessagePortInvalidate(mMsgPortListenner);CFRelease(mMsgPortListenner);
}

CFMessagePortInvalidate会停止port消息的发送和接收操作,而只有调用了CFRelease,CFMessagePortRef对象才真正的被释放掉。

2、消息发送者

发送部分代码如下:

-(NSString *)sendMessageToDameonWith:(id)msgInfo msgID:(NSInteger)msgid
{// 生成Remote portCFMessagePortRef bRemote = CFMessagePortCreateRemote(kCFAllocatorDefault, CFSTR(MACH_PORT_REMOTE));if (nil == bRemote) {NSLog(@"bRemote create failed");return nil;}// 构建发送数据(string)NSString    *msg = [NSString stringWithFormat:@"%@",msgInfo];NSLog(@"send msg is :%@",msg);const char *message = [msg UTF8String];CFDataRef data,recvData = nil;data = CFDataCreate(NULL, (UInt8 *)message, strlen(message));// 执行发送操作CFMessagePortSendRequest(bRemote, msgid, data, 0, 100 , kCFRunLoopDefaultMode, &recvData);if (nil == recvData) {NSLog(@"recvData date is nil.");CFRelease(data);CFMessagePortInvalidate(bRemote);CFRelease(bRemote);return nil;}// 解析返回数据const UInt8  * recvedMsg = CFDataGetBytePtr(recvData);if (nil == recvedMsg) {NSLog(@"receive date err.");CFRelease(data);CFMessagePortInvalidate(bRemote);CFRelease(bRemote);return nil;}NSString    *strMsg = [NSString stringWithCString:(char *)recvedMsg encoding:NSUTF8StringEncoding];NSLog(@"%@",strMsg);CFRelease(data);CFMessagePortInvalidate(bRemote);CFRelease(bRemote);CFRelease(recvData);return strMsg;
}

其中MACH_PORT_REMOTE的定义为:

#define MACH_PORT_REMOTE    "com.wangzz.demo"

发送消息时要相对简单,首先通过CFMessagePortCreateRemote生成一个Remote的CFMessagePortRef,这里需要注意的是CFMessagePortCreateRemote时传入的字符串唯一标识MACH_PORT_REMOTE必须和消息接收者创建local的CFMessagePortRef时使用的字符串唯一标识是同一个!

通过查看源码发现,CFMessagePortCreateRemote会根据MACH_PORT_REMOTE定义的字符串为唯一标识获取消息接收者通过CFMessagePortCreateLocal使用相同字符串创建的底层mach port端口,从而实现向消息接收者发送信息。

如果消息接收者还没有创建或者通过CFMessagePortCreateLocal创建local端口失败时,想要通过CFMessagePortCreateRemote去创建remote端口肯定是失败的。

说明

  • 很遗憾的是,在iOS7及以后系统中,CFMessagePort的通信机制不再可用。

在使用CFMessagePortCreateLocal/CFMessagePortCreateRemote创建CFMessagePortRef对象时会失败,官方文档中是这么说的:

This method is not available on iOS 7 and later—it will return NULL and log a sandbox violation in syslog. See Concurrency Programming Guide for possible replacement technologies.
  • CFMessagePort只能用于本地进程通信。

  • CFMessagePort是基于mach port端口的通信方式,不但可以用于进程通信,也可以用于线程间通信,只是线程间通信有了GCD和Cocoa提供的原生方法,已经能很方便的实现了,没必要再使用CFMessagePort。

  • 进程通信使用场景

iOS系统多任务机制,使得进程间通信基本都只能用于越狱开发。常用的场景是前端有一个UI程序用于界面展示,后端有一个daemo精灵程序用于任务处理。

demo工程

特地做了了个demo工程,以便更好地演示CFMessagePort的使用,可以到CSDN下载。

为了模拟进程间通信场景,我将消息接收进程CFMessagePortReceive做成了能够后台播放音乐的程序,以便其切到后台后能继续存活。

由于CFMessagePort不再支持iOS7及以后系统,本demo实在iOS6系统上测试的。

demo使用方式:

  • CFMessagePortReceive启动后,点击Start Listenning创建CFMessagePort接口并开始监听port消息,然后将CFMessagePortReceive切到后台;

  • 启动CFMessagePortSend程序,在输入框中写入内容,点击发送按钮即可和CFMessagePortReceive通信。

  • MessagePort通信过程中会有日志输出,可以使用以下方式查看日志:

1.真机

选择:Xcode->Window->Organizer->Devices,然后选中窗口左侧当前设备的Console窗口查看。

2.模拟器

选择:模拟器->调试->打开系统日志,或者直接使用快捷键?/直接打开系统控制台查看日志。

参考文档

  • CF-855.14

  • Threading Programming Guide

  • CFMessagePort Reference

转载于:https://www.cnblogs.com/ruixin-jia/p/6072547.html

iOS进程间通信之CFMessagePort相关推荐

  1. iOS面试准备 - ios篇

    iOS面试准备 - ios篇 ios面试准备 - objective-c篇 ios面试准备 - 网络篇 IOS面试准备 - C++篇 iOS面试准备 - 其他篇 运行时 https://juejin. ...

  2. Runloop随手记

    Runloop是iOS开发的一个难点,需要不断体会才能真正理解. 一个runloop有几个要素组成,一个是runloop模式,一个是source,一个是observer.runloop模式是需要监视的 ...

  3. iOS内IPC(进程间通信)方法小结

    转自:http://blog.csdn.net/lifengzhong/article/details/7739539 受限与iOS系统的运行机制,绝大多数情况下一个应用在进入后台后会马上进入susp ...

  4. iOS 知识点整理 (持续更新...)

    整理了些iOS相关的基础问题,每个问题可能会再写些扩展,需要具体了解可以看题目下方的链接 如有错漏,欢迎指出,谢谢 一.Swift 1.给一个数组,要求写一个函数,交换数组中的两个元素(swift可用 ...

  5. Android iOS 开发全面对比分析

    1 前言 Android和iOS是移动端的两大平台,Android以它的开源.易上手.开发成本低而受到广大开发者的青睐,而iOS作为苹果的封闭系统,以它的简单.流畅高效.高冷等特点也吸引了一大批果粉开 ...

  6. iOS 系统分析(一) 阅读内核准备知识

    原文出自[听云技术博客]:http://blog.tingyun.com/web/a... 0x01 iOS体系架构 1.1 iOS 系统的整体体系架构 用户体验( The User Experien ...

  7. iOS事件处理,看我就够了~

    该文章属于<简书 - 刘小壮>原创,转载请注明: <简书 - 刘小壮> https://www.jianshu.com/p/b0884faae603 好久没写博客了,前后算起来 ...

  8. iOS NSRunloop 详解

    概念 Runloop就像它的名字一样,就是跑环.我的理解就是一个死循环.是一个可以随时睡眠,随时唤醒的死循环 大家可以想一下,手机app为什么会一直运行?而且在接收到用户点击等等操作时就会有所反映.这 ...

  9. 转载 一个渣硕iOS春招总结 | 掘金技术征文

    https://www.qingtingip.com/h_219584.html 地处北方一隅,今年很多公司春招没来现场,所以基本都是提前批的线上面试,整个三月都过的比较累,4月份的校招应该不参加了, ...

最新文章

  1. ListMapSet的操作和遍历
  2. 【转】简单介绍几个CDS视图聚合函数
  3. 现在的人工智能逆天到什么地步了?
  4. 通用权限管理系统组件 (GPM - General Permissions Manager) 中实现按部门组织机构设置权限...
  5. 《天天数学》连载03:一月三日
  6. namespace关键字-1
  7. VMware vRealize Operations Manager的内部版本号(2145975)
  8. NavigationView内的Android ExpandableListView
  9. 什么是阿里云服务器系统盘和数据盘?
  10. 【CNN】94页论文综述卷积神经网络:从基础技术到研究前景
  11. 海量数据挖掘MMDS week7: 局部敏感哈希LSH(进阶)
  12. python爬虫简历项目怎么写_爬虫项目咋写,爬取什么样的数据可以作为项目写在简历上?...
  13. 那些你该知道的CSS颜色代码大全都在这里了,点击查阅
  14. 怎么用cmd打开python
  15. Kafka 发送消息 Idempotent -- Spring 整合
  16. Airflow基础架构简介
  17. 游戏账号交易平台,是专门为网络游戏提供相关交易服务的电子商务平台,主要从事网络游戏账号的交易。
  18. 地铁译:Spark for python developers ---Spark的数据戏法
  19. 1024程序员节主题征文 | 2022年1024程序员节只剩一天
  20. Windows11 安装教程(Ultraiso-制作启动盘)

热门文章

  1. 厉害了!Antiilatency推出移动位置追踪器!
  2. android开发常见的设计模式,Android开发有哪些常用设计模式?
  3. 魔改ResNet反超Transformer再掀架构之争!作者说“没一处是创新”,这些优化trick值得学...
  4. 世界首个2nm制程芯片公布!这次IBM跑在了台积电三星英特尔前面
  5. 17个提升iOS开发效率的神器
  6. 3、循序渐进设计模式-创建型
  7. VS2010没有Intellisense(智能感知)的解决办法
  8. 传清华应届生获Facebook offer
  9. 北京站售票人员倒票视频
  10. 普通幕僚:Ownership意识不足的几种症状