美文网首页
MultipeerConnectivity.framework

MultipeerConnectivity.framework

作者: 一代骄马 | 来源:发表于2018-02-07 09:49 被阅读34次

    版权声明:本文为博主原创文章,未经博主允许不得转载。 http://blog.csdn.net/phunxm/article/details/43450167

    目录(?)[+]

    AirDrop

    苹果在2010推出的OS X 10.7 Lion系统中加入了全新的AirDrop功能,该功能允许两台Mac机之间无线传输文件。区别于传统的局域网文件共享方式,AirDrop不要求两台机器在同一个网络内。用户无需设置,只需要打开AirDrop文件夹即可查看到其他用户,分享文件变得非常便捷。

    AirDrop不需要基于(无线)路由器或者手动建立热点组网,它是利用Mac与Mac之间的点对点网络来进行会话传输。这一切由系统在后台完成,无需断开当前WiFi网络,也不影响当前连接WiFi网络的通信,就可以与其他Mac通过内置特定信道通信。

    WWDC13上推出的iOS7也开始支持iOS设备之间使用AirDrop实现共享传输。关于AirDrop的条件要求及内部机制,可参考《为什么iOS 7 和 OS X 之间的AirDrop 不能互传?》。

    WWDC14推出的OS X 10.10 Yosemite操作系统,终于打通了与iOS移动设备之间的跨平台AirDrop传输。运行Mac OS X Yosemite 10.10版本的Mac设备(型号≥2012)和运行iOS 7及以上的iOS设备(≥iPhone5,≥iPad 4,iPadmini,≥iPod touch)之间才能实现跨平台文件传输。

    根据官方资料显示,AirDrop基于蓝牙和WiFi实现(AirDrop does the rest usingWi-Fi and Bluetooth)。具体来说,通过低功耗蓝牙技术(BLE)进行发现(Advertising/Browsing),使用WiFi Direct(P2P WiFi)技术进行数据传输。可参考《iOS 7的AirDrop是利用什么信号来传输的?》《What Is AirDrop? How Does It Work?》。因此,开启AirDrop不要求双方必须联网或连接到同一局域网,但必须同时打开WiFi和蓝牙,且进行传输的两台设备必须保持在9米的范围之内。

    Multipeer Connectivity

    在iOS7中,引入了一个全新的框架——Multipeer Connectivity(多点连接)。利用Multipeer Connectivity框架,即使在没有连接到WiFi(WLAN)或移动网络(xG)的情况下,距离较近的Apple设备(iMac/iPad/iPhone)之间可基于蓝牙和WiFi(P2P WiFi)技术进行发现和连接实现近场通信。

    Multipeer Connectivity扩充的功能与利用AirDrop传输文件非常类似,可以将其看作AirDrop不能直接使用的补偿,代价是需要自己实现。手机不联网也能跟附近的人聊得火热FireChatSee You Around等近场聊天App、近距离无网遥控交互拍照神器拍咯App就是基于Multipeer Connectivity框架实现。

    相比AirDrop,Multipeer Connectivity在进行发现和会话时并不要求同时打开WiFi和蓝牙,也不像AirDrop那样强制打开这两个开关,而是根据条件适时选择使用蓝牙或(和)WiFi。粗略测试情况如下:

    双方WiFi和蓝牙都未打开:无法发现。

    双方都开启蓝牙:通过蓝牙发现和传输。

    双方都开启WiFi:通过WiFi Direct发现和传输,速度接近AirDrop(Reliable速率稍低),不知道同一WLAN下是否优先走局域网?

    双方都同时开启了WiFi和蓝牙:应该是模拟AirDrop,通过低功耗蓝牙技术扫描发现握手,然后通过WiFi Direct传输。

    MultipeerConnectivity.framework

    以下是MultipeerConnectivity.framework的四个核心对象:

    Peer ID's allow for uniqueidentification.

    Advertiser objects tells others they'reavailable.

    Browser objects browse for advertised devices.

    Session objects handle thecommunications.

    @class 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.

    类似sockaddr,用于标识连接的两端endpoint,通常是昵称或设备名称。

    该对象只开放了displayName属性,私有MCPeerIDInternal对象持有的设备相关的_idString/_pid64字段并未公开。

    在许多情况下,客户端同时广播并发现同一个服务,这将导致一些混乱,尤其是在client/server模式中。所以,每一个服务都应有一个类型标示符——serviceType,它是由ASCII字母、数字和“-”组成的短文本串,最多15个字符。

    @class MCNearbyServiceAdvertiser

    MCNearbyServiceAdvertiser advertises availability of the local peer, and handlesinvitations from nearby peers.

    类似broadcaster。

    主线程(com.apple.main-thread(serial))创建MCNearbyServiceAdvertiser并启动startAdvertisingPeer。

    MCNearbyServiceAdvertiserDelegate异步回调(didReceiveInvitationFromPeer)切换回主线程。

    在主线程didReceiveInvitationFromPeer中创建MCSession并invitationHandler(YES, session)接受会话连接请求(accept参数为YES)。

    @class MCNearbyServiceBrowser

    MCNearbyServiceBrowser looks for nearby peers, andconnects them to sessions.

    类似servo listen+client connect。

    主线程(com.apple.main-thread(serial))创建MCNearbyServiceBrowser并启动startBrowsingForPeers。

    MCNearbyServiceBrowserDelegate异步回调(foundPeer/lostPeer)切换回主线程。

    主线程创建MCSession并启动invitePeer。

    @class MCSession

    A MCSession facilitates communication among all peers in a multipeer session.

    (MCSession) provide support for communication between connected peer devices(identified by MCPeerID). 

    Session objects maintain a set of peer ID objects that represent the peers connected to the session. 

    注意,peerID并不具备设备识别属性。

    类似TCP链接中的socket。创建MCSession时,需指定自身MCPeerID,类似bind。

    为避免频繁的会话数据通知阻塞主线程,MCSessionDelegate异步回调(didChangeState/didReceiveCertificate/didReceiveData/didReceiveStream)有一个专门的回调线程——com.apple.MCSession.callbackQueue(serial)。为避免阻塞MCSeesion回调线程,最好新建数据读(写)线程!

    @class MCAdvertiserAssistant/MCBrowserViewController

    MCAdvertiserAssistant为针对Advertiser封装的管理助手;MCBrowserViewController继承自UIViewController,提供了基本的UI应用框架。

    MCBrowser/MCAdvertiser的回调线程一般是delegate所在线程Queue:com.apple.main-thread(serial)。

    ==================================================

    Session negotiation

    1.Advertiser initiation

    // MCPeerID标识自身,discoveryInfo为广播信息。

    Advertiser::initWithPeer:withDiscoveryInfo:withServiceType

    // 启动广播(定时广播)

    // tell nearby peers that your app is willing to join sessions of a specified type.

    Advertiser::startAdvertisingPeer

    2.Browser initiation

    // MCPeerID标识自身

    Browser::initWithPeer:withServiceType

    //启动扫描/搜索,搜索到Advertiser后,回调browser:foundPeer

    // let your app search programmatically for nearby devices with apps that support sessions of a particular type.

    Browser::startBrowsingForPeers

    类似socket(SO_BROADCAST)的listen。

    3.Browser found advertiser

    //搜索到advertiser,可以发出会话邀请建立连接

    Browser::browser:foundPeer:withDiscoveryInfo:

    4.Browser invite advertiser

    //向advertiser发出会话邀请协商建立通道,类似TCP三次握手中的

    //需要基于自身MCPeerID创建specifiedMCSession

    // creates a session and invite other peers to join it.

    // Thetimeout parameter is seconds and should be a positive value.

    Browser::invitePeer:toSession:withContext:timeout:

    类似socket connect。

    // 与peer的session会话链路首先收到connecting通知

    Browser Session::didChangeState(MCSessionStateConnecting)

    5.Advertiser receive initiation with certificate

    // advertiser接收到邀请(未stopAdvertising)

    Advertiser:didReceiveInvitationFromPeer:withContext:invitationHandler:

    {

        // advertiser接受邀请,类似TCP三次握手中的

        //需要基于自身MCPeerID创建specified MCSession

        // join a session when invited by another peer.

     invitationHandler(YES, session);

    }

    invitationHandler(YES)类似socket accept。

    // 与peer的session会话链路首先收到connecting通知

    Advertiser Session::didChangeState(MCSessionStateConnecting)

    //与peer的session会话链路收到证书

    Advertiser Session::didReceiveCertificate

    6.Browser receive acknowledge from advertiser

    //与peer的session会话链路收到证书

    Browser Session::didReceiveCertificate

    //与peer的session会话链路收到connected通知

    Browser Session::didChangeState(MCSessionStateConnected)

    // browser底层可能再给advertiser发送一个包?

    7.Advertiser receive acknowledge from browser

    //与peer的session会话链路收到connected通知

    Advertiser Session::didChangeState(MCSessionStateConnected)

    至此,双方通信链路协商成功,可以基于session(connect self and peer)向peer发送data、resource或stream。

    该框架内部自行维持Session Keep-Alive,具体不可考。注意可能存在的会话过期和配对问题。

    以下为典型Browser-Advertiser发现、握手流程:

    可能存在中间人攻击man-in-the-middle attacks):

    Sender(Browser) startBrowsingForPeers,中间人(MitM)同时开启Browsing/Advertising模式。

    ①Receiver(Advertiser) startAdvertisingPeer。

    ②MitM Browser嗅探到接收Advertiser的广播报文(discoveryInfo&serviceType,可能加密或完全裸露)。

    ③MitM Advertiser原封不动广播透传嗅探到的接收Advertiser的广播报文(discoveryInfo&serviceType)。

    ④发送Browser有50%概率扫描到MitM Advertiser,误认为接收Advertiser。

    ⑤发送Browser邀请伪装的MitM Advertiser加入会话(invitePeer with Context,可能加密或完全裸露)。

    ⑥伪装的MitM Browser透传context邀请接收Advertiser加入会话(invitePeer withContext)。

    ⑦⑧⑨⑩攻击者与通信的两端同时建立起独立的会话(Browser-MitM[Advertiser/Browser]-Advertiser),透传发送Browser和接收Advertiser之间的所有数据。

    如未加密,整个过程中的数据将被中间人完全窃取,还可能插入新的内容。

    MitM攻击解决方案:

    核心思路是在打破或限制MitM与Browser/Advertiser两端建立连接,例如发现Advertiser立即锁定,减少MitM Browser透传Advertising报文给Browser的几率,从而减少或避免Browser发现MitM Advertiser。

    另外,应增加Browser发起会话(InvitePeer)的身份标识,从而减少MitM Browser伪造Invite报文的几率。

    以上过程1~3步中,由于在启动AirDrop之前没有可预知匹配的设备标识属性(例如uuid、MAC地址),广播发现是完全公开透明的,MitM是不可避免的。

    发送方或接收方创建MCSession的接口为initWithPeer:securityIdentity:encryptionPreference:

    若参数2指定加密(MCEncryptionRequired),可避免报文泄露,但是会降低传输效率,且无法阻止中间人完全透传。

    若参数1指定Security Identity提供local peer's identity,则可基于PKI对会话握手过程进行身份甄别,但是广播前需要提前交换pubKey,这对于无网场景无疑是一大挑战。或者可基于某一闭环体系内可识别的身份校验体制?

    受邀请方接受邀请(invitationHandler:YES)后,彼此都会收到对方的证书(didReceiveCertificate:fromPeer),可通过PKI校验机制,从而鉴别peer的身份。

    ==================================================

    Session transmission

    1.Messages

    (1)sendData

    //发送数据(可发给多个MCPeerID)

    Session::sendData:toPeers:withMode:error:

    // MCSession send modes for the -sendData:toPeers:withMode:error: method

    typedefNS_ENUM(NSInteger,MCSessionSendDataMode) {

     MCSessionSendDataReliable,// guaranteed reliable and in-order delivery

    MCSessionSendDataUnreliable// sent immediately without queuing, no guaranteed delivery

    } NS_ENUM_AVAILABLE(10_10,7_0);

    类似socket的两种类型:SOCK_STREAM/SOCK_DGRAM?

    (2)didReceiveData

    //接收数据(可能有多个MCPeerID/MCSession)

    Session::session:didReceiveData:fromPeer:

    2.Streams

    (1)startStream(NSOutputStream

    // streamName可预埋length

    NSOutputStream* outStream =Session::startStreamWithName:toPeer:error:

    [outStream open];

    // 启动发送线程(写数据线程),发送byte stream

    if( [outStreamhasSpaceAvailable] ) {// 检查流中是否还有可供写入的空间

    [outputStream write];// 将buffer中的字节流数据写入流中

    }

    为保证数据发送的实时性,写数据线程一直while(1)轮询队列中是否有数据要发送。

    (2)didReceiveStream(NSInputStream

    MCSession底层收到数据流后,其回调线程为com.apple.MCSession.callbackQueue(serial)。

    如果在该回调线程中直接做数据收割处理,则可能阻塞该回调线程,导致无法及时获取其他的数据流通知。因此,需要新建一个读数据线程(调用[[NSRunLoop currentRunLoop] run]激活循环loop,闲时休眠)。

    当收到数据流回调didReceiveStream时,将inputStream添加到读数据线程RunLoop的事件源中,在下一个loop,回调NSStreamDelegate,对相应NSStreamEvent进行处理。

    // 同一InputStream可能多次接收回调,可提取对比streamName中的length,以判定接收完成

    Session::session:didReceiveStream:(NSInputStream*)inputStream withName:fromPeer:

    {

    //设置代理

    inputStream.delegate =self;

    //调用performSelector将以下代码块切换到读数据线程执行:

    {

        //将该对象分配一个run loop接收stream events

    [inputStreamscheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]

        //启动接收

    [inputStreamopen];

    }

    }

    //接收回调:NSStreamDelegate

    - (void)stream:(NSStream*)inputStream handleEvent:(NSStreamEvent)eventCode

    {

    switch (eventCode) {

    case NSStreamEventHasBytesAvailable:

    //从inputstream拷贝字节流数据到buffer中进行组包

    [inputStream read];

    break;

    case NSStreamEventEndEncountered://接收完成

    case NSStreamEventErrorOccurred://接收错误

    [inputStream close];

    [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    [inputStreamrelease];

    break;

    }

    }

    3.Resources

    (1)sendResource

    //返回NSProgress用于监控进度

    Session::sendResourceAtURL:withName:toPeer:withCompletionHandler:

    //发送完成时,回调completionHandler

    (2)didStartReceivingResource/didFinishReceivingResource

    // Start receiving a resource from remote peer

    // NSProgress用于监控进度

    Session::session:didStartReceivingResource WithName:fromPeer:withProgress:

    // Finished receiving a resource from remote peer and saved the content in a temporary location - the app is responsible for moving the file to a permanent location within its sandbox

    //接收完成,需从临时localURL移动文件至永久位置

    - (void)session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:

    以下为典型Browser-Advertiser会话传输流程:

    参考:

    使用AirDrop 以无线方式共享内容

    Airdropand Multipeer Connectivity

    MultipeerConnectivity Framework

    理解iOS7的Multipeer Connectivity框架

    MultipeerConnectivity点对点连接

    如何使用MultipeerConnectivity

    Send the Monkey a Message with Multipeer Connectivity

    Nayaksb/Airdrop-MultipeerConnectivity

    Multipeer Connectivity Follow Up

    Certificate in iOS MCSession

    MultipeerGroupChat Sample Code

    相关文章

      网友评论

          本文标题:MultipeerConnectivity.framework

          本文链接:https://www.haomeiwen.com/subject/lylozxtx.html