什么是Multipeer Connectivity?
在iOS7中,引入了一个全新的框架——Multipeer Connectivity(多点连接)。利用Multipeer Connectivity框架,即使在没有连接到WiFi(WLAN)或移动网络(xG)的情况下,距离较近的Apple设备(iMac/iPad/iPhone)之间可基于蓝牙和WiFi(P2P WiFi)技术进行发现和连接实现近场通信。
Multipeer Connectivity扩充的功能与利用AirDrop传输文件非常类似,可以将其看作AirDrop不能直接使用的补偿,代价是需要自己实现。
本Demo主要用到4个类:
MCBrowserViewController:MCBrowserViewController继承自UIViewController,提供了基本的UI应用框架。
MCAdvertiserAssistant、MCAdvertiserAssistant为针对Advertiser封装的管理助手,主要处理广播信息。
MCSession:类似TCP链接中的socket。创建MCSession时,需指定自身MCPeerID,类似bind。
MCPeerID:类似sockaddr,用于标识连接的两端endpoint,通常是昵称或设备名称。
1、简单地建立一个界面,主要有连接和发送2个UIButton。
create_button.png
2、Multipeer Connectivity框架初始化这4个类。
#pragma mark - Wifi Sharing Methods -(void)setUpMultipeer { // Setup peer ID self.myPeerID = [[MCPeerID alloc] initWithDisplayName:[UIDevice currentDevice].name]; // Setup session self.mySession = [[MCSession alloc] initWithPeer:self.myPeerID]; self.mySession.delegate = self; // Setup BrowserViewController self.browserVC = [[MCBrowserViewController alloc] initWithServiceType:@"chat" session:self.mySession]; self.browserVC.delegate = self; // Setup Advertiser self.advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:@"chat" discoveryInfo:nil session:self.mySession]; [self.advertiser start]; } -(void)showBrowserVC { [self presentViewController:self.browserVC animated:YES completion:nil]; } -(void)dismissBrowserVC { [self.browserVC dismissViewControllerAnimated:YES completion:^(void){ [self invokeAlertMethod:@"连接成功" Body:@"Both device connected successfully." Delegate:nil]; }]; } -(void)stopWifiSharing:(BOOL)isClear { if(isClear && self.mySession != nil){ [self.mySession disconnect]; [self.mySession setDelegate:nil]; self.mySession = nil; self.browserVC = nil; } }
3、MCBrowserViewController代理方法
#pragma marks MCBrowserViewControllerDelegate // 点击完成 -(void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController { [self dismissBrowserVC]; [marrReceiveData removeAllObjects]; } // 点击取消 -(void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController { [self dismissBrowserVC]; }
4、MCSession代理方法
主要处理发送方传递的文件或者信息
// Received data from remote peer - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID { NSLog(@"data receiveddddd : %lu",(unsigned long)data.length); if (data.length > 0) { if (data.length < 2) { noOfDataSend++; NSLog(@"noofdatasend : %zd",noOfDataSend); NSLog(@"array count : %zd",marrFileData.count); if (noOfDataSend < ([marrFileData count])) { [self.mySession sendData:[marrFileData objectAtIndex:noOfDataSend] toPeers:[self.mySession connectedPeers] withMode:MCSessionSendDataReliable error:nil]; }else { [self.mySession sendData:[@"File Transfer Done" dataUsingEncoding:NSUTF8StringEncoding] toPeers:[self.mySession connectedPeers] withMode:MCSessionSendDataReliable error:nil]; } } else { if ([[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] isEqualToString:@"File Transfer Done"]) { [self appendFileData]; }else { [self.mySession sendData:[@"1" dataUsingEncoding:NSUTF8StringEncoding] toPeers:[self.mySession connectedPeers] withMode:MCSessionSendDataReliable error:nil]; [marrReceiveData addObject:data]; } } } } // Received a byte stream from remote peer - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID { NSLog(@"did receive stream"); } // Start receiving a resource from remote peer - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress { NSLog(@"start receiving"); } // 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 - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error { NSLog(@"finish receiving resource"); } -(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { NSLog(@"change state : %zd",state); }
5、发送图片(此Demo只是简单地做了个收发图片的Demo,此框架可实现的功能当然不止这么简单。)
-(void)sendData { [marrFileData removeAllObjects]; NSData *sendData = UIImagePNGRepresentation([UIImage imageNamed:@"test2.png"]); NSUInteger length = [sendData length]; NSUInteger chunkSize = 100 * 1024; NSUInteger offset = 0; do { NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset; NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[sendData bytes] + offset length:thisChunkSize freeWhenDone:NO]; NSLog(@"chunk length : %lu",(unsigned long)chunk.length); [marrFileData addObject:[NSData dataWithData:chunk]]; offset += thisChunkSize; } while (offset < length); noOfdata = [marrFileData count]; noOfDataSend = 0; if ([marrFileData count] > 0) { [self.mySession sendData:[marrFileData objectAtIndex:noOfDataSend] toPeers:[self.mySession connectedPeers] withMode:MCSessionSendDataReliable error:nil]; } } -(void)appendFileData { NSMutableData *fileData = [NSMutableData data]; for (int i = 0; i < [marrReceiveData count]; i++) { [fileData appendData:[marrReceiveData objectAtIndex:i]]; } [fileData writeToFile:[NSString stringWithFormat:@"%@/Image.png", [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]] atomically:YES]; UIImageWriteToSavedPhotosAlbum([UIImage imageWithData:fileData], self, @selector(image:didFinishSavingWithError:contextInfo:), nil); } - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { if (!error) { [self invokeAlertMethod:@"发送成功" Body:@"图片已保存到手机相册" Delegate:nil]; } }