AirDrop
苹果在2010推出的OS X 10.7 Lion系统中加入了全新的AirDrop功能,该功能允许两台Mac机之间无线传输文件。区别于传统的局域网文件共享方式,AirDrop不要求两台机器在同一个网络内。用户无需设置,只需要打开AirDrop文件夹即可查看到其他用户,分享文件变得非常便捷。
AirDrop实际上是利用Mac与Mac之间的点对点网络来进行的,本质上还是局域网传输,但是不需要路由器或者复杂地手动建立热点了,这一切由系统在后台完成,无需断开当前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,iPad mini,≥iPod touch)之间才能实现跨平台文件传输。
根据官方资料显示,AirDrop基于蓝牙和WiFi实现(AirDrop does the rest usingWi-Fi and Bluetooth)。具体来说,通过低功耗蓝牙技术(BLE)进行发现(Advertising/Discovery),使用WiFi
Direct(P2P WiFi)技术进行数据传输。可参考《iOS 7的AirDrop是利用什么信号来传输的?》《WhatIs
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不能直接使用的补偿,代价是需要自己实现。FireChat和See
YouAround等近场聊天App就是基于Multipeer Connectivity框架实现。
相比AirDrop,Multipeer Connectivity在进行发现和会话时并不要求同时打开WiFi和蓝牙,也不像AirDrop那样强制打开这两个开关,而是根据条件适时选择使用蓝牙或(和)WiFi。粗略测试情况如下:
(1)双方WiFi和蓝牙都未打开:无法发现。
(2)双方都开启蓝牙:通过蓝牙发现和传输。
(3)双方都开启WiFi:通过WiFi Direct发现和传输,速度接近AirDrop(Reliable速率稍低),不知道同一WLAN下是否优先走局域网?
(4)双方都同时开启了WiFi和蓝牙:应该是模拟AirDrop,通过低功耗蓝牙技术扫描发现握手,然后通过WiFi Direct传输。
MultipeerConnectivity.framework
以下是MultipeerConnectivity.framework核心的四个对象:
- Peer ID‘s allow for uniqueidentification.
- Advertiser objects tellsothers they‘re available.
- Browser objects browse foradvertised devices.
- Session objects handle thecommunications.
This framework is used in twophases: the discovery phase, and the session phase.
@class MCPeerID
MCPeerIDrepresents a peer in a multipeer session.
Peer IDs (MCPeerID
)
uniquely identify an app runningon a device to nearby peers.
provideinformation that identifies the device and its user to other nearby devices.
类似sockaddr,用于标识endpoint,通常是昵称或设备名称。
在许多情况下,客户端同时广播并发现同一个服务,这将导致一些混乱,尤其是在client/server模式中。所以,每一个服务都应有一个类型标示符——serviceType,它是由ASCII字母、数字和“-”组成的短文本串,最多15个字符。
@class MCNearbyServiceAdvertiser
MCNearbyServiceAdvertiser advertisesavailability of the local peer, and handles invitations from nearby peers.
类似broadcaster。
@class MCNearbyServiceBrowser
MCNearbyServiceBrowser looks for nearbypeers, and connects them to sessions.
类似servo listener。
@class MCSession
A MCSession facilitates communication amongall peers in a multipeer session.
(MCSession)
providesupport for communication between connected peer devices.
Sessionobjects maintain a set of peer ID objects that represent the peers connected tothe session.
类似TCP链接中的socket。创建MCSession时,需指定MCPeerID,类似bind。
@class MCAdvertiserAssistant/MCBrowserViewController
MCAdvertiserAssistant为针对Advertiser封装的管理助手;MCBrowserViewController继承自UIViewController,提供了。
Session negotiatation
1.Advertiser initiation
// MCPeerID标识自身
Advertiser::initWithPeer:withDiscoveryInfo:withServiceType
//启动广播(定时广播)
// tell nearby peers that your app is willing to join sessions of aspecified type.
Advertiser::startAdvertisingPeer
2.Browser initiation
// MCPeerID标识自身
Browser::initWithPeer:withServiceType
//启动扫描/搜索,搜索到Advertiser后,回调browser:foundPeer
// let your app search programmatically for nearby devices with appsthat 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三次握手中的<SYN>
//需要基于自身MCPeerID创建specifiedMCSession
// creates a session and invite other peers to join it.
// Thetimeout parameter is seconds and shouldbe a positive value.
Browser::invitePeer:toSession:withContext:timeout:
类似socket connect。
5.Advertiser receive initiation with certificate
// advertiser接收到邀请(未stopAdvertising)
Advertiser:didReceiveInvitationFromPeer:withContext:invitationHandler:
{
// advertiser接受邀请,类似TCP三次握手中的<SYN,ACK>
//需要基于自身MCPeerID创建specifiedMCSession
// join a session when invited by another peer.
invitationHandler(YES, session);
}
invitationHandler(YES)类似socket accept。
// advertiser收到证书
Advertiser Session::didReceiveCertificate
6.Browser receive acknowledge from advertiser
// browser收到证书
Browser Session::didReceiveCertificate
// browser收到advertiser会话链路established通知
Browser Session::didChangeState(MCSessionStateConnected)
// browser底层可能再给advertiser发送一个<ACK>包?
7.Advertiser receive acknowledge from browser
// advertiser收到browser会话链路established通知
Advertiser Session::didChangeState(MCSessionStateConnected)
至此,双方通信链路协商成功,可以基于session发送data、resource或stream。
该框架内部自行维持Session Keep-Alive,具体不可考。注意可能存在的会话过期和配对问题。
Session transmission
1.Messages
(1)sendData
//发送数据(可发给多个MCPeerID)
Session::sendData:toPeers:withMode:error:
// MCSessionsend modes for the -sendData:toPeers:withMode:error: method
typedefNS_ENUM(NSInteger,MCSessionSendDataMode) {
MCSessionSendDataReliable, // guaranteed reliable and in-order delivery
MCSessionSendDataUnreliable // sentimmediately 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];
//启动发送线程,发送bytestream
[NSOutputStream write];
(2)didReceiveStream(NSInputStream)
//同一InputStream可能多次接收回调,可提取对比streamName中的length,以判定接收完成
Session::session:didReceiveStream:(NSInputStream*)streamwithName:fromPeer:
{
//启动接收线程
stream.delegate =self;
[stream scheduleInRunLoop];
[streamopen];
}
//接收回调:NSStreamDelegate
- (void)stream:(NSStream*)aStreamhandleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventHasBytesAvailable:
//接收bytestream进行组包
[NSInputStream read]
break;
case
NSStreamEventEndEncountered:
//接收完成
break;
case
NSStreamEventErrorOccurred:
//接收错误
break;
}
3.Resources
(1)sendResource
//返回NSProgress用于监控进度
Session::sendResourceAtURL:withName:toPeer:withCompletionHandler:
//发送完成时,回调completionHandler
(2)didStartReceivingResource/didFinishReceivingResource
// Startreceiving a resource from remote peer
// NSProgress用于监控进度
Session::session:didStartReceivingResourceWithName:fromPeer:withProgress:
// Finishedreceiving a resource from remote peer and saved the content in a temporarylocation - the app is responsible for moving the file to a permanent locationwithin
its sandbox
//接收完成,需从临时localURL移动文件至永久位置
- (void)session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:
参考:
《Airdropand
Multipeer Connectivity》《MultipeerConnectivity
Framework》