鲜为人知的Multipeer Connectivity(部分转载)

来源:互联网 发布:知乎的成本结构 编辑:程序博客网 时间:2024/04/28 10:33

相信大家对AirDrop都比较熟悉,AirDrop不要求两台机器在同一个网络内,不管是在mac笔记本还是iphone,只需要打开AirDrop就可以将文件分享给附近的人。在iOS7中,引入了一个全新的框架——Multipeer Connectivity(多点连接),利用这一框架,即使在没有连接到WiFi或移动网络的情况下,距离较近的Apple设备之间可基于蓝牙和WiFi技术进行近场通信。与AirDrop不同的地方在于,使用该框架需要自己实现设备的交互。

        由于前段时间,项目的需要,对Multipeer Connectivity框架进行了研究,利用苹果提供的MultipeerConnectivity.framework框架,进行二次封装,再实现文件发送接受细节,可以满足近距离文件共享的功能。下面对几个典型的方法进行说明,详细的在使用过程中可参考官方的说明文档和demo。

       设备之间的通信通过广播(Advertising)和发现(discovering)服务来实现,我们把这些设备称作节点,一个节点有一个唯一标示MCPeerID对象,它的定义如下:

MCPeerID represents a peer in a multipeer session.Peer IDs (MCPeerID) uniquely identify an app running on a device to nearby peers.provide information that identifies the device and its user to other nearby devices.

     为了便于大家理解,接下来的内容转载自CocoaChina的一篇文章,写的很不错,链接点击打开链接。

    Advertising & Discovering

通信的第一步是让大家互相知道彼此,我们通过广播(Advertising)和发现(discovering)服务来实现。广播作为服务器搜索附近的节点,而节点同时也去搜索附近的广播。在许多情况下,客户端同时广播并发现同一个服务,这将导致一些混乱,尤其是在client-server模式中。所以,每一个服务都应有一个类型(标示符),它是由ASCII字母、数字和“-”组成的短文本串,最多15个字符。通常,一个服务的名字应该由应用程序的名字开始,后边跟“-”和一个独特的描述符号。(作者认为这和 com.apple.*标示符很像),就像下边:

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. static NSString * const XXServiceType = @"xx-service";   
     一个节点有一个唯一标示MCPeerID对象,使用展示名称进行初始化,它可能是用户指定的昵称,或是单纯的设备名称。
[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. MCPeerID *localPeerID = [[MCPeerID alloc] initWithDisplayName:[[UIDevice currentDevice] name]];  
      节点使用NSNetService或者Bonjour C API进行手动广播和发现,但这是一个特别深入的问题,关于手动节点管理可具体参见MCSession文档。

Advertising
服务的广播通过MCNearbyServiceAdvertiser来操作,初始化时带着本地节点、服务类型以及任何可与发现该服务的节点进行通信的可选信息。发现信息使用Bonjour TXT records encoded(according to RFC 6763)发送。

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. MCNearbyServiceAdvertiser *advertiser =   
  2.     [[MCNearbyServiceAdvertiser alloc] initWithPeer:localPeerID   
  3.                                       discoveryInfo:nil   
  4.                                         serviceType:XXServiceType];   
  5. advertiser.delegate = self;   
  6. [advertiser startAdvertisingPeer];   
        相关事件由advertiser的代理来处理,需遵从MCNearbyServiceAdvertiserDelegate协议。在下例中,考虑到用户可以选择是否接受或拒绝传入连接请求,并有权以拒绝或屏蔽任何来自该节点的后续请求选项。
[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser   
  2. didReceiveInvitationFromPeer:(MCPeerID *)peerID   
  3.        withContext:(NSData *)context   
  4.  invitationHandler:(void(^)(BOOL accept, MCSession *session))invitationHandler   
  5. {   
  6.     if ([self.mutableBlockedPeers containsObject:peerID]) {   
  7.         invitationHandler(NO, nil);   
  8.         return;   
  9.     }   
  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.    
  19.         if (buttonIndex == [actionSheet destructiveButtonIndex]) {   
  20.             [self.mutableBlockedPeers addObject:peerID];   
  21.         }   
  22.    
  23.         MCSession *session = [[MCSession alloc] initWithPeer:localPeerID   
  24.                                             securityIdentity:nil   
  25.                                         encryptionPreference:MCEncryptionNone];   
  26.         session.delegate = self;   
  27.    
  28.         invitationHandler(acceptedInvitation, (acceptedInvitation ? session : nil));   
  29.     }] showInView:self.view];   
  30. }   
  31.    
        为了简单起见,本例中使用了一个带有block的actionsheet来作为操作框,它可以直接给invitationHandler传递信息,用以避免创建和管理delegate造成的过于凌乱的业务逻辑,以避免创建和管理自定义delegate object造成的过于凌乱的业务逻辑。这种方法可以用category来实现,或者改编任何一个CocoaPods里有效的实现。

Creating a Session
       在上面的例子中,我们创建了session,并在接受邀请连接时传递到节点。一个MCSession对象跟本地节点标识符、securityIdentity以及encryptionPreference参数一起进行初始化。
[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  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的服务类型来初始化:

[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. MCNearbyServiceBrowser *browser = [[MCNearbyServiceBrowser alloc] initWithPeer:localPeerID serviceType:XXServiceType];   
  2. browser.delegate = self;   
可能会有很多节点广播一个特定的服务,所以为了方便用户(或开发者),MCBrowserViewController将提供一个内置的、标准的方式来呈现链接到广播节点:
[objc] view plain copy
 在CODE上查看代码片派生到我的代码片
  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以适应新连接的客户端。

      以上是转载的文章,将Multipeer Connectivity框架的设备发现都介绍的很清楚,接下来就是根据业务的需要,发送消息或者文件,在下一张中,我会结合实际项目向大家详细介绍各个代理的使用方法,不得不说这个框架虽然用的人很少,但对我个人而言,这是非常值得研究的,有任何问题欢迎添加我的微信或qq:985517968。


0 0