MultipeerConnectivity

Multipeer connectivity是一个使附近设备通过Wi-Fi网络、P2P Wi-Fi以及蓝牙个人局域网进行通信的框架。互相链接的节点可以安全地传递信息、流或是其他文件资源,而不用通过网络服务。

Advertising & Discovering

通信的第一步是让大家互相知道彼此,我们通过广播(Advertising)和发现(discovering)服务来实现。

广播作为服务器搜索附近的节点,而节点同时也去搜索附近的广播。在许多情况下,客户端同时广播并发现同一个服务,这将导致一些混乱,尤其是在client-server模式中。

所以,每一个服务都应有一个类型(标示符),它是由ASCII字母、数字和“-”组成的短文本串,最多15个字符。通常,一个服务的名字应该由应用程序的名字开始,后边跟“-”和一个独特的描述符号。(作者认为这和 com.apple.*标示符很像),就像下边:

  1. static NSString * const XXServiceType = @"xx-service";

一个节点有一个唯一标示MCPeerID对象,使用展示名称进行初始化,它可能是用户指定的昵称,或是单纯的设备名称。

  1. MCPeerID *localPeerID = [[MCPeerID alloc] initWithDisplayName:[[UIDevice currentDevice] name]];

节点使用NSNetService或者Bonjour C API进行手动广播和发现,但这是一个特别深入的问题,关于手动节点管理可具体参见MCSession文档。

Advertising

服务的广播通过MCNearbyServiceAdvertiser来操作,初始化时带着本地节点、服务类型以及任何可与发现该服务的节点进行通信的可选信息。

发现信息使用Bonjour TXT records encoded(according to RFC 6763)发送。

  1. MCNearbyServiceAdvertiser *advertiser =
  2. [[MCNearbyServiceAdvertiser alloc] initWithPeer:localPeerID
  3. discoveryInfo:nil
  4. serviceType:XXServiceType];
  5. advertiser.delegate = self;
  6. [advertiser startAdvertisingPeer];

相关事件由advertiser的代理来处理,需遵从MCNearbyServiceAdvertiserDelegate协议。

在下例中,考虑到用户可以选择是否接受或拒绝传入连接请求,并有权以拒绝或屏蔽任何来自该节点的后续请求选项。

  1. #pragma mark - MCNearbyServiceAdvertiserDelegate
  2. - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser
  3. didReceiveInvitationFromPeer:(MCPeerID *)peerID
  4. withContext:(NSData *)context
  5. invitationHandler:(void(^)(BOOL accept, MCSession *session))invitationHandler
  6. {
  7. if ([self.mutableBlockedPeers containsObject:peerID]) {
  8. invitationHandler(NO, nil);
  9. return;
  10. }
  11. [[UIActionSheet actionSheetWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Received Invitation from %@", @"Received Invitation from {Peer}"), peerID.displayName]
  12. cancelButtonTitle:NSLocalizedString(@"Reject", nil)
  13. destructiveButtonTitle:NSLocalizedString(@"Block", nil)
  14. otherButtonTitles:@[NSLocalizedString(@"Accept", nil)]
  15. block:^(UIActionSheet *actionSheet, NSInteger buttonIndex)
  16. {
  17. BOOL acceptedInvitation = (buttonIndex == [actionSheet firstOtherButtonIndex]);
  18. if (buttonIndex == [actionSheet destructiveButtonIndex]) {
  19. [self.mutableBlockedPeers addObject:peerID];
  20. }
  21. MCSession *session = [[MCSession alloc] initWithPeer:localPeerID
  22. securityIdentity:nil
  23. encryptionPreference:MCEncryptionNone];
  24. session.delegate = self;
  25. invitationHandler(acceptedInvitation, (acceptedInvitation ? session : nil));
  26. }] showInView:self.view];
  27. }

为了简单起见,本例中使用了一个带有block的actionsheet来作为操作框,它可以直接给invitationHandler传递信 息,用以避免创建和管理delegate造成的过于凌乱的业务逻辑,以避免创建和管理自定义delegate object造成的过于凌乱的业务逻辑。这种方法可以用category来实现,或者改编任何一个CocoaPods里有效的实现

Creating a Session

在上面的例子中,我们创建了session,并在接受邀请连接时传递到节点。一个MCSession对象跟本地节点标识符、securityIdentity以及encryptionPreference参数一起进行初始化。

  1. MCSession *session = [[MCSession alloc] initWithPeer:localPeerID
  2. securityIdentity:nil
  3. encryptionPreference:MCEncryptionNone];
  4. session.delegate = self;

securityIdentity是一个可选参数。通过X.509证书,它允许节点安全识别并连接其他节点。当设置了该参数时,第一个对象应该 是识别客户端的SecIdentityRef,接着是一个或更多个用以核实本地节点身份的SecCertificateRef objects。

encryptionPreference参数指定是否加密节点之间的通信。MCEncryptionPreference枚举提供的三种值是:

MCEncryptionOptional:会话更喜欢使用加密,但会接受未加密的连接。

MCEncryptionRequired:会话需要加密。

MCEncryptionNone:会话不应该加密。

启用加密会显著降低传输速率,所以除非你的应用程序很特别,需要对用户敏感信息的处理,否则建议使用MCEncryptionNone。

MCSessionDelegate协议将会在发送和接受信息的部分被覆盖.

Discovering

客户端使用MCNearbyServiceBrowser来发现广播,它需要local peer标识符,以及非常类似MCNearbyServiceAdvertiser的服务类型来初始化:

  1. MCNearbyServiceBrowser *browser = [[MCNearbyServiceBrowser alloc] initWithPeer:localPeerID serviceType:XXServiceType];
  2. browser.delegate = self;

可能会有很多节点广播一个特定的服务,所以为了方便用户(或开发者),MCBrowserViewController将提供一个内置的、标准的方式来呈现链接到广播节点:

  1. MCBrowserViewController *browserViewController =
  2. [[MCBrowserViewController alloc] initWithBrowser:browser
  3. session:session];
  4. browserViewController.delegate = self;
  5. [self presentViewController:browserViewController
  6. animated:YES
  7. completion:
  8. ^{
  9. [browser startBrowsingForPeers];
  10. }];

当browser完成节点连接后,它将使用它的delegate调用browserViewControllerDidFinish:,以通知展示视图控制器--它应该更新UI以适应新连接的客户端。

Sending & Receiving Information

一旦节点彼此相连,它们将能互传信息。Multipeer Connectivity框架区分三种不同形式的数据传输:

Messages是定义明确的信息,比如端文本或者小序列化对象。

Streams 流是可连续传输数据(如音频,视频或实时传感器事件)的信息公开渠道。

Resources是图片、电影以及文档的文件。

Messages

Messages使用-sendData:toPeers:withMode:error::方法发送。

  1. NSString *message = @"Hello, World!";
  2. NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
  3. NSError *error = nil;
  4. if (![self.session sendData:data
  5. toPeers:peers
  6. withMode:MCSessionSendDataReliable
  7. error:&error]) {
  8. NSLog(@"[Error] %@", error);
  9. }

通过MCSessionDelegate方法 -sessionDidReceiveData:fromPeer:收取信息。以下是如何解码先前示例代码中发送的消息:

  1. #pragma mark - MCSessionDelegate
  2. - (void)session:(MCSession *)session
  3. didReceiveData:(NSData *)data
  4. fromPeer:(MCPeerID *)peerID
  5. {
  6. NSString *message =
  7. [[NSString alloc] initWithData:data
  8. encoding:NSUTF8StringEncoding];
  9. NSLog(@"%@", message);
  10. }

另一种方法是发送NSKeyedArchiver编码的对象:

  1. id <NSSecureCoding> object = // ...;
  2. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
  3. NSError *error = nil;
  4. if (![self.session sendData:data
  5. toPeers:peers
  6. withMode:MCSessionSendDataReliable
  7. error:&error]) {
  8. NSLog(@"[Error] %@", error);
  9. }
  10. #pragma mark - MCSessionDelegate
  11. - (void)session:(MCSession *)session
  12. didReceiveData:(NSData *)data
  13. fromPeer:(MCPeerID *)peerID
  14. {
  15. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
  16. unarchiver.requiresSecureCoding = YES;
  17. id object = [unarchiver decodeObject];
  18. [unarchiver finishDecoding];
  19. NSLog(@"%@", object);
  20. }

为了防范对象替换攻击,设置requiresSecureCoding为YES是很重要的,这样如果根对象类没有遵从<NSSecureCoding>,就会抛出一个异常。欲了解更多信息,请参阅[NSHipster article on NSSecureCoding]。

Streams

Streams 使用 -startStreamWithName:toPeer:创建:

  1. NSOutputStream *outputStream =
  2. [session startStreamWithName:name
  3. toPeer:peer];
  4. stream.delegate = self;
  5. [stream scheduleInRunLoop:[NSRunLoop mainRunLoop]
  6. forMode:NSDefaultRunLoopMode];
  7. [stream open];
  8. // ...

Streams通过MCSessionDelegate的方法session:didReceiveStream:withName:fromPeer:来接收:

  1. #pragma mark - MCSessionDelegate
  2. - (void)session:(MCSession *)session
  3. didReceiveStream:(NSInputStream *)stream
  4. withName:(NSString *)streamName
  5. fromPeer:(MCPeerID *)peerID
  6. {
  7. stream.delegate = self;
  8. [stream scheduleInRunLoop:[NSRunLoop mainRunLoop]
  9. forMode:NSDefaultRunLoopMode];
  10. [stream open];
  11. }

输入和输出的streams必须安排好并打开,然后才能使用它们。一旦这样做,streams就可以被读出和写入。

Resources

Resources 发送使用 -sendResourceAtURL:withName:toPeer:withCompletionHandler::

  1. NSURL *fileURL = [NSURL fileURLWithPath:@"path/to/resource"];
  2. NSProgress *progress =
  3. [self.session sendResourceAtURL:fileURL
  4. withName:[fileURL lastPathComponent]
  5. toPeer:peer
  6. withCompletionHandler:^(NSError *error)
  7. {
  8. NSLog(@"[Error] %@", error);
  9. }];

返回的NSProgress对象可以是通过KVO(Key-Value Observed)来监视文件传输的进度,并且它提供取消传输的方法:-cancel。

接收资源实现MCSessionDelegate两种方 法:-session:didStartReceivingResourceWithName:fromPeer:withProgress: 和 -session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:

  1. #pragma mark - MCSessionDelegate
  2. - (void)session:(MCSession *)session
  3. didStartReceivingResourceWithName:(NSString *)resourceName
  4. fromPeer:(MCPeerID *)peerID
  5. withProgress:(NSProgress *)progress
  6. {
  7. // ...
  8. }
  9. - (void)session:(MCSession *)session
  10. didFinishReceivingResourceWithName:(NSString *)resourceName
  11. fromPeer:(MCPeerID *)peerID
  12. atURL:(NSURL *)localURL
  13. withError:(NSError *)error
  14. {
  15. NSURL *destinationURL = [NSURL fileURLWithPath:@"/path/to/destination"];
  16. NSError *error = nil;
  17. if (![[NSFileManager defaultManager] moveItemAtURL:localURL
  18. toURL:destinationURL
  19. error:&error]) {
  20. NSLog(@"[Error] %@", error);
  21. }
  22. }

再次说明,在传输期间NSProgress parameter in -session:didStartReceivingResourceWithName:fromPeer:withProgress:允许接收节点来 监控文件传输进度。在 -session:didFinishReceivingResourceWithName:fromPeer:atURL:withError: 中,delegate的责任是从临时localURL移动文件至永久位置。

Multipeer是突破性的API,其价值才刚刚开始被理解。虽然完整的支持功能比如AirDrop目前仅限于最新的设备,你应该会看到它将成为让所有人盼望的功能。

时间: 2024-10-11 10:57:05

MultipeerConnectivity的相关文章

iOS开发总结(A0)- MultipeerConnectivity

MultipeerConnectivity 使设备通过 bluetooth 或者local wifi连接,简单易用 主要类: 1. advertiser:向其他设备广播 若用系统的标准ui,使用 MCAdvertiserAssistant 若自定义ui,使用MCNearbyServiceAdvertiser 注意:保证同一个服务,同一时间只有一个advertiser,否则周围设备可能搜到两个相同的服务设备,容易引起混乱, (在不需要,要及时stop advertiser) 2. browser,

MultipeerConnectivity.framework梳理

AirDrop 苹果在2010推出的OS X 10.7 Lion系统中加入了全新的AirDrop功能,该功能允许两台Mac机之间无线传输文件.区别于传统的局域网文件共享方式,AirDrop不要求两台机器在同一个网络内.用户无需设置,只需要打开AirDrop文件夹即可查看到其他用户,分享文件变得非常便捷. AirDrop实际上是利用Mac与Mac之间的点对点网络来进行的,本质上还是局域网传输,但是不需要路由器或者复杂地手动建立热点了,这一切由系统在后台完成,无需断开当前WiFi网络,就可以直接建立

MultipeerConnectivity框架,近场通信的基本使用

Multipeer connectivity是一个使附近设备通过Wi-Fi网络.P2P Wi-Fi以及蓝牙个人局域网进行通信的框架.互相链接的节点可以安全地传递信息.流或是其他文件资源,而不用通过网络服务.此框架是在iOS7以后推出,旨在替代GameKit下的GKPeerPickerController通信.通过此框架我们可以直接连接同一网络下的设备,让其直接进行类似微信,qq那样的即时通讯效果. 原理 其中通讯的原理,是利用节点来进行广播服务(标示符),其他节点可以通过服务(标示符)发现广播.

自己总结的 iOS ,Mac 开源项目以及库,知识点------持续更新

自己在 git  上看到一个非常好的总结的东西,但是呢, fork  了几次,就是 fork  不到我的 git 上,干脆复制进去,但是,也是认真去每一个每一个去认真看了,并且也是补充了一些,感觉非常棒,所以好东西要分享,为啥用 CN 博客,有个好处,可以随时修改,可以持续更新,不用每次都要再发表,感觉这样棒棒的 我们 自己总结的iOS.mac开源项目及库,持续更新.... github排名 https://github.com/trending,github搜索:https://github.

iOS的基本框架

在iOS中框架是一个目录,包含了共享资源库,用于访问该资源库中储存的代码的头文件,以及图像.声音文件等其他资源.共享资源库定义应用程序可以调用的函数和方法.    iOS为应用程序开发提供了许多可使用的框架,并构成IOS操作系统的层次架构,分为四层,从上到下依次为:Cocoa Touch Layer(触摸UI层).MediaLayer(媒体层).Core Services Layer(核心服务层).Core OS Layer(核心OS层). 低层次框架提供IOS的基本服务和技术,高层次框架建立在

2.OC蓝牙功能

一.  最早的蓝牙框架是GameKit,iOS7之前用的比较多,它有只能支持iOS设备间的传输,但是使用步骤简单,我们只需要搞清楚两个类就可以了. GKPeerPickerController:熟称浏览器,调用此控制器的show方法来显示当前的蓝牙热点,一旦发现有另一页在查找蓝牙的用户,之间就能实链接. GKSession:连接会话,主要用于发送和接受传输数据.档两个程序进行连接时,GKPeerPickerController的代理方法会将两者建立的会话(GKSession)对象传递给制定的对象

iOS开发 非常全的三方库、插件、大牛博客等等

UI 下拉刷新 EGOTableViewPullRefresh- 最早的下拉刷新控件. SVPullToRefresh- 下拉刷新控件. MJRefresh- 仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能.可以自定义上下拉刷新的文字说明.具体使用看"使用方法". (国人写) XHRefreshControl- XHRefreshControl 是一款高扩展性.低耦合度的下拉刷新.上提加载更多的组件.(国人写) CBStoreHo

iOS 9.3 到 iOS 10.0 的开发API不同点

iOS 9.3 to iOS 10.0 API Differences Objective-C /usr/include Accelerate AudioToolbox AudioUnit AVFoundation AVKit CallKit (Added) CFNetwork CloudKit Contacts CoreBluetooth CoreData CoreFoundation CoreGraphics CoreImage CoreLocation CoreMedia CoreMoti

iOS、mac开源项目及库汇总

UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能.可以自定义上下拉刷新的文字说明.具体使用看“使用方法”. (国人写) XHRefreshControl – XHRefreshControl 是一款高扩展性.低耦合度的下拉刷新.上提加载更多的组件.(国人写) CBStoreHou