MultipeerConnectivity
Multipeer connectivity是一个使附近设备通过Wi-Fi网络、P2P Wi-Fi以及蓝牙个人局域网进行通信的框架。互相链接的节点可以安全地传递信息、流或是其他文件资源,而不用通过网络服务。
Advertising & Discovering
通信的第一步是让大家互相知道彼此,我们通过广播(Advertising)和发现(discovering)服务来实现。
广播作为服务器搜索附近的节点,而节点同时也去搜索附近的广播。在许多情况下,客户端同时广播并发现同一个服务,这将导致一些混乱,尤其是在client-server模式中。
所以,每一个服务都应有一个类型(标示符),它是由ASCII字母、数字和“-”组成的短文本串,最多15个字符。通常,一个服务的名字应该由应用程序的名字开始,后边跟“-”和一个独特的描述符号。(作者认为这和 com.apple.*标示符很像),就像下边:
- static NSString * const XXServiceType = @"xx-service";
一个节点有一个唯一标示MCPeerID对象,使用展示名称进行初始化,它可能是用户指定的昵称,或是单纯的设备名称。
- MCPeerID *localPeerID = [[MCPeerID alloc] initWithDisplayName:[[UIDevice currentDevice] name]];
节点使用NSNetService或者Bonjour C API进行手动广播和发现,但这是一个特别深入的问题,关于手动节点管理可具体参见MCSession文档。
Advertising
服务的广播通过MCNearbyServiceAdvertiser来操作,初始化时带着本地节点、服务类型以及任何可与发现该服务的节点进行通信的可选信息。
发现信息使用Bonjour TXT records encoded(according to RFC 6763)发送。
- MCNearbyServiceAdvertiser *advertiser =
- [[MCNearbyServiceAdvertiser alloc] initWithPeer:localPeerID
- discoveryInfo:nil
- serviceType:XXServiceType];
- advertiser.delegate = self;
- [advertiser startAdvertisingPeer];
相关事件由advertiser的代理来处理,需遵从MCNearbyServiceAdvertiserDelegate协议。
在下例中,考虑到用户可以选择是否接受或拒绝传入连接请求,并有权以拒绝或屏蔽任何来自该节点的后续请求选项。
- #pragma mark - MCNearbyServiceAdvertiserDelegate
- - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser
- didReceiveInvitationFromPeer:(MCPeerID *)peerID
- withContext:(NSData *)context
- invitationHandler:(void(^)(BOOL accept, MCSession *session))invitationHandler
- {
- if ([self.mutableBlockedPeers containsObject:peerID]) {
- invitationHandler(NO, nil);
- return;
- }
- [[UIActionSheet actionSheetWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Received Invitation from %@", @"Received Invitation from {Peer}"), peerID.displayName]
- cancelButtonTitle:NSLocalizedString(@"Reject", nil)
- destructiveButtonTitle:NSLocalizedString(@"Block", nil)
- otherButtonTitles:@[NSLocalizedString(@"Accept", nil)]
- block:^(UIActionSheet *actionSheet, NSInteger buttonIndex)
- {
- BOOL acceptedInvitation = (buttonIndex == [actionSheet firstOtherButtonIndex]);
- if (buttonIndex == [actionSheet destructiveButtonIndex]) {
- [self.mutableBlockedPeers addObject:peerID];
- }
- MCSession *session = [[MCSession alloc] initWithPeer:localPeerID
- securityIdentity:nil
- encryptionPreference:MCEncryptionNone];
- session.delegate = self;
- invitationHandler(acceptedInvitation, (acceptedInvitation ? session : nil));
- }] showInView:self.view];
- }
为了简单起见,本例中使用了一个带有block的actionsheet来作为操作框,它可以直接给invitationHandler传递信 息,用以避免创建和管理delegate造成的过于凌乱的业务逻辑,以避免创建和管理自定义delegate object造成的过于凌乱的业务逻辑。这种方法可以用category来实现,或者改编任何一个CocoaPods里有效的实现。
Creating a Session
在上面的例子中,我们创建了session,并在接受邀请连接时传递到节点。一个MCSession对象跟本地节点标识符、securityIdentity以及encryptionPreference参数一起进行初始化。
- MCSession *session = [[MCSession alloc] initWithPeer:localPeerID
- securityIdentity:nil
- encryptionPreference:MCEncryptionNone];
- session.delegate = self;
securityIdentity是一个可选参数。通过X.509证书,它允许节点安全识别并连接其他节点。当设置了该参数时,第一个对象应该 是识别客户端的SecIdentityRef,接着是一个或更多个用以核实本地节点身份的SecCertificateRef objects。
encryptionPreference参数指定是否加密节点之间的通信。MCEncryptionPreference枚举提供的三种值是:
MCEncryptionOptional:会话更喜欢使用加密,但会接受未加密的连接。
MCEncryptionRequired:会话需要加密。
MCEncryptionNone:会话不应该加密。
启用加密会显著降低传输速率,所以除非你的应用程序很特别,需要对用户敏感信息的处理,否则建议使用MCEncryptionNone。
MCSessionDelegate协议将会在发送和接受信息的部分被覆盖.
Discovering
客户端使用MCNearbyServiceBrowser来发现广播,它需要local peer标识符,以及非常类似MCNearbyServiceAdvertiser的服务类型来初始化:
- MCNearbyServiceBrowser *browser = [[MCNearbyServiceBrowser alloc] initWithPeer:localPeerID serviceType:XXServiceType];
- browser.delegate = self;
可能会有很多节点广播一个特定的服务,所以为了方便用户(或开发者),MCBrowserViewController将提供一个内置的、标准的方式来呈现链接到广播节点:
- MCBrowserViewController *browserViewController =
- [[MCBrowserViewController alloc] initWithBrowser:browser
- session:session];
- browserViewController.delegate = self;
- [self presentViewController:browserViewController
- animated:YES
- completion:
- ^{
- [browser startBrowsingForPeers];
- }];
当browser完成节点连接后,它将使用它的delegate调用browserViewControllerDidFinish:,以通知展示视图控制器--它应该更新UI以适应新连接的客户端。
Sending & Receiving Information
一旦节点彼此相连,它们将能互传信息。Multipeer Connectivity框架区分三种不同形式的数据传输:
Messages是定义明确的信息,比如端文本或者小序列化对象。
Streams 流是可连续传输数据(如音频,视频或实时传感器事件)的信息公开渠道。
Resources是图片、电影以及文档的文件。
Messages
Messages使用-sendData:toPeers:withMode:error::方法发送。
- NSString *message = @"Hello, World!";
- NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
- NSError *error = nil;
- if (![self.session sendData:data
- toPeers:peers
- withMode:MCSessionSendDataReliable
- error:&error]) {
- NSLog(@"[Error] %@", error);
- }
通过MCSessionDelegate方法 -sessionDidReceiveData:fromPeer:收取信息。以下是如何解码先前示例代码中发送的消息:
- #pragma mark - MCSessionDelegate
- - (void)session:(MCSession *)session
- didReceiveData:(NSData *)data
- fromPeer:(MCPeerID *)peerID
- {
- NSString *message =
- [[NSString alloc] initWithData:data
- encoding:NSUTF8StringEncoding];
- NSLog(@"%@", message);
- }
另一种方法是发送NSKeyedArchiver编码的对象:
- id <NSSecureCoding> object = // ...;
- NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
- NSError *error = nil;
- if (![self.session sendData:data
- toPeers:peers
- withMode:MCSessionSendDataReliable
- error:&error]) {
- NSLog(@"[Error] %@", error);
- }
- #pragma mark - MCSessionDelegate
- - (void)session:(MCSession *)session
- didReceiveData:(NSData *)data
- fromPeer:(MCPeerID *)peerID
- {
- NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
- unarchiver.requiresSecureCoding = YES;
- id object = [unarchiver decodeObject];
- [unarchiver finishDecoding];
- NSLog(@"%@", object);
- }
为了防范对象替换攻击,设置requiresSecureCoding为YES是很重要的,这样如果根对象类没有遵从<NSSecureCoding>,就会抛出一个异常。欲了解更多信息,请参阅[NSHipster article on NSSecureCoding]。
Streams
Streams 使用 -startStreamWithName:toPeer:创建:
- NSOutputStream *outputStream =
- [session startStreamWithName:name
- toPeer:peer];
- stream.delegate = self;
- [stream scheduleInRunLoop:[NSRunLoop mainRunLoop]
- forMode:NSDefaultRunLoopMode];
- [stream open];
- // ...
Streams通过MCSessionDelegate的方法session:didReceiveStream:withName:fromPeer:来接收:
- #pragma mark - MCSessionDelegate
- - (void)session:(MCSession *)session
- didReceiveStream:(NSInputStream *)stream
- withName:(NSString *)streamName
- fromPeer:(MCPeerID *)peerID
- {
- stream.delegate = self;
- [stream scheduleInRunLoop:[NSRunLoop mainRunLoop]
- forMode:NSDefaultRunLoopMode];
- [stream open];
- }
输入和输出的streams必须安排好并打开,然后才能使用它们。一旦这样做,streams就可以被读出和写入。
Resources
Resources 发送使用 -sendResourceAtURL:withName:toPeer:withCompletionHandler::
- NSURL *fileURL = [NSURL fileURLWithPath:@"path/to/resource"];
- NSProgress *progress =
- [self.session sendResourceAtURL:fileURL
- withName:[fileURL lastPathComponent]
- toPeer:peer
- withCompletionHandler:^(NSError *error)
- {
- NSLog(@"[Error] %@", error);
- }];
返回的NSProgress对象可以是通过KVO(Key-Value Observed)来监视文件传输的进度,并且它提供取消传输的方法:-cancel。
接收资源实现MCSessionDelegate两种方 法:-session:didStartReceivingResourceWithName:fromPeer:withProgress: 和 -session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:
- #pragma mark - MCSessionDelegate
- - (void)session:(MCSession *)session
- didStartReceivingResourceWithName:(NSString *)resourceName
- fromPeer:(MCPeerID *)peerID
- withProgress:(NSProgress *)progress
- {
- // ...
- }
- - (void)session:(MCSession *)session
- didFinishReceivingResourceWithName:(NSString *)resourceName
- fromPeer:(MCPeerID *)peerID
- atURL:(NSURL *)localURL
- withError:(NSError *)error
- {
- NSURL *destinationURL = [NSURL fileURLWithPath:@"/path/to/destination"];
- NSError *error = nil;
- if (![[NSFileManager defaultManager] moveItemAtURL:localURL
- toURL:destinationURL
- error:&error]) {
- NSLog(@"[Error] %@", error);
- }
- }
再次说明,在传输期间NSProgress parameter in -session:didStartReceivingResourceWithName:fromPeer:withProgress:允许接收节点来 监控文件传输进度。在 -session:didFinishReceivingResourceWithName:fromPeer:atURL:withError: 中,delegate的责任是从临时localURL移动文件至永久位置。
Multipeer是突破性的API,其价值才刚刚开始被理解。虽然完整的支持功能比如AirDrop目前仅限于最新的设备,你应该会看到它将成为让所有人盼望的功能。
MultipeerConnectivity的更多相关文章
- iOS开发--通过MultipeerConnectivity完成蓝牙通讯
iOS开发–通过MultipeerConnectivity完成蓝牙通讯 iOS蓝牙通讯的三种方式: GameKit.framework:iOS7之前的蓝牙通讯框架,从iOS7开始过期,但是目前已经被淘 ...
- MultipeerConnectivity框架,近场通信的基本使用
Multipeer connectivity是一个使附近设备通过Wi-Fi网络.P2P Wi-Fi以及蓝牙个人局域网进行通信的框架.互相链接的节点可以安全地传递信息.流或是其他文件资源,而不用通过网络 ...
- 蓝牙 MultipeerConnectivity
在iOS7中,引入了一个全新的框架——Multipeer Connectivity(多点连接). 利用Multipeer Connectivity框架,即使在没有连接到WiFi(WLAN)或移动网络( ...
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
--系统应用与系统服务 iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用 ...
- ios项目里扒出来的json文件
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px Menlo; color: #000000 } p.p2 { margin: 0.0px 0. ...
- iOS--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook等系统服务开发汇总
iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用系统应用.使用系统服务: ...
- Github上关于iOS的各种开源项目集合(强烈建议大家收藏,查看,总有一款你需要)
下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITableVie ...
- 2.OC蓝牙功能
一. 最早的蓝牙框架是GameKit,iOS7之前用的比较多,它有只能支持iOS设备间的传输,但是使用步骤简单,我们只需要搞清楚两个类就可以了. GKPeerPickerController:熟称浏 ...
- IOS框架和服务
在iOS中框架是一个目录,包含了共享资源库,用于访问该资源库中储存的代码的头文件,以及图像.声音文件等其他资源.共享资源库定义应用程序可以调用的函数和方法. iOS为应用程序开发提供了许多可使用的框架 ...
随机推荐
- Ajax表单提交插件jquery form
jQuery Form插件是一个优秀的Ajax表单插件,我们可以非常容易的使用它处理表单控件的值,清空和复位表单控件,附件上传,以及完成Ajax表单提交. jQuery Form有两个核心方法ajax ...
- TCP回射客户程序:str_cli函数
str_cli函数完成客户处理循环: 从标准输入读入一行文本,写到服务器上,读回服务器对该行的回射,并把回射行写到标准输出上 读入一行,写到服务器 fgets读入一行文本,writen把该行发送给服务 ...
- 习题二:string数组应用
说明: 读字符串char buf[100]="xxx:yyy:zzz:aaa:bbb" 按“:”进行分解到string数组中去 逻辑: 通过指针遍历整个字符串 遇到'\0'表示字符 ...
- Lambert
Dmap -- 贴图信息 LightColor -- 灯光颜色 KL -- 灯光强度值(开放给美术) EnvColor -- 环境颜色 ka -- 环境光强度 (开放给美术) Dmap * (max ...
- [Android] 输入系统(一)
Android输入系统是人与机器交互最主要的手段.我们通过按键或者触碰屏幕,会先经由linux产生中断,进行统一的处理过后,转换成Android能识别的事件信息,然后Android的输入系统去获取事件 ...
- u8818 G300的官方rom刷机包及详细的刷机方法
之前看到有些机友在找华为u8818的官方rom固件包,在这里我给大家分享一下这个手机的官方原版的固件rom包吧,有2.3的,也有4.0的,大家可以根据自己的需求来下载. 华为U8818官方2.3固件: ...
- 转:Java同步synchronized使用
原文链接 作者:Jakob Jenkov Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(s ...
- POJ1789 Truck History(prim)
题目链接. 分析: 最大的敌人果然不是别人,就是她(英语). 每种代表车型的串,他们的distance就是串中不同字符的个数,要求算出所有串的distance's 最小 sum. AC代码如下: #i ...
- treap启发式合并
注意输入v要在建根的前面. #include <cstdio> #include <iostream> #include <algorithm> #include ...
- POJ Round Numbers(数位DP)
题目大意: Round Number: 将一个整数转化为二进制数字后,(不含前导0) 要是0的个数 大于等于1的个数 则是 Round Number 问从L-R之中有多少个Round Number ...