日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ios MultipeerConnectivity蓝牙通讯

發布時間:2023/12/16 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ios MultipeerConnectivity蓝牙通讯 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

iOS藍牙通訊的三種方式:

  • GameKit.framework:iOS7之前的藍牙通訊框架,從iOS7開始過期,但是目前已經被淘汰。(不做介紹)

  • MultipeerConnectivity.framework:iOS7開始引入的新的藍牙通訊開發框架,用于取代GameKit。(詳細介紹)

  • CoreBluetooth.framework:功能強大的藍牙開發框架,要求設備必須支持藍牙4.0。

MultipeerConnectivity實現藍牙通訊

前面已經說了GameKit相關的藍牙操作類從iOS7已經全部過期,蘋果官方推薦使用MultipeerConnectivity代替。但是應該了解,MultipeerConnectivity.framework并不僅僅支持藍牙連接,準確的說它是一種支持Wi-Fi網絡、P2P Wi-Fi已經藍牙個人局域網的通信框架,它屏蔽了具體的連接技術,讓開發人員有統一的接口編程方法。通過MultipeerConnectivity連接的節點之間可以安全的傳遞信息、流或者其他文件資源而不必通過網絡服務。此外使用MultipeerConnectivity進行近場通信也不再局限于同一個應用之間傳輸,而是可以在不同的應用之間進行數據傳輸(當然如果有必要的話你仍然可以選擇在一個應用程序之間傳輸)。

要了解MultipeerConnectivity的使用必須要清楚一個概念:廣播(Advertisting)和發現(Disconvering),這很類似于一種Client-Server模式。假設有兩臺設備A、B,B作為廣播去發送自身服務,A作為發現的客戶端。一旦A發現了B就試圖建立連接,經過B同意二者建立連接就可以相互發送數據。在使用GameKit框架時,A和B既作為廣播又作為發現,當然這種情況在MultipeerConnectivity中也很常見。

  • A.廣播

無論是作為服務器端去廣播還是作為客戶端去發現廣播服務,那么兩個(或更多)不同的設備之間必須要有區分,通常情況下使用MCPeerID對象來區分一臺設備,在這個設備中可以指定顯示給對方查看的名稱(display
name)。另外不管是哪一方,還必須建立一個會話MCSession用于發送和接受數據。通常情況下會在會話的-(void)session:(MCSession
)session peer:(MCPeerID?)peerID didChangeState:(MCSessionState)state代理方法中跟蹤會話狀態(已連接、正在連接、未連接);在會話的-(void)session:(MCSession
)session didReceiveData:(NSData?)data fromPeer:(MCPeerID *)peerID代理方法中接收數據;同時還會調用會話的-(void)sendData: toPeers:withMode: error:方法去發送數據。

廣播作為一個服務器去發布自身服務,供周邊設備發現連接。在MultipeerConnectivity中使用MCAdvertiserAssistant來表示一個廣播,通常創建廣播時指定一個會話MCSession對象將廣播服務和會話關聯起來。一旦調用廣播的start方法周邊的設備就可以發現該廣播并可以連接到此服務。在MCSession的代理方法中可以隨時更新連接狀態,一旦建立了連接之后就可以通過MCSession的connectedPeers獲得已經連接的設備。

  • B.發現

前面已經說過作為發現的客戶端同樣需要一個MCPeerID來標志一個客戶端,同時會擁有一個MCSession來監聽連接狀態并發送、接受數據。除此之外,要發現廣播服務,客戶端就必須要隨時查找服務來連接,在MultipeerConnectivity中提供了一個控制器MCBrowserViewController來展示可連接和已連接的設備(這類似于GameKit中的GKPeerPickerController),當然如果想要自己定制一個界面來展示設備連接的情況你可以選擇自己開發一套UI界面。一旦通過MCBroserViewController選擇一個節點去連接,那么作為廣播的節點就會收到通知,詢問用戶是否允許連接。由于初始化MCBrowserViewController的過程已經指定了會話MCSession,所以連接過程中會隨時更新會話狀態,一旦建立了連接,就可以通過會話的connected屬性獲得已連接設備并且可以使用會話發送、接受數據。

下面用兩個不同的應用程序來演示使用MultipeerConnectivity的使用過程,其中一個應用運行在模擬器中作為廣播節點,另一個運行在iPhone真機上作為發現節點,并且實現兩個節點的文字互傳。

首先看一下作為廣播節點的程序:

#import "ViewController.h" #import <MultipeerConnectivity/MultipeerConnectivity.h>@interface ViewController ()<MCAdvertiserAssistantDelegate, MCSessionDelegate,UINavigationControllerDelegate, UIImagePickerControllerDelegate> @property (strong,nonatomic) MCSession *session; @property (strong,nonatomic) MCAdvertiserAssistant *advertiserAssistant; @end@implementation ViewController #pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];//創建節點,displayName是用于提供給周邊設備查看和區分此服務的MCPeerID *peerID=[[MCPeerID alloc]initWithDisplayName:@"KenshinCui_Advertiser"];_session=[[MCSession alloc]initWithPeer:peerID];_session.delegate = self;//創建廣播_advertiserAssistant=[[MCAdvertiserAssistant alloc]initWithServiceType:@"cmj-stream" discoveryInfo:nil session:_session];_advertiserAssistant.delegate = self; } #pragma mark - UI事件 - (IBAction)advertiserClick:(UIBarButtonItem *)sender {//開始廣播[self.advertiserAssistant start]; } - (IBAction)selectClick:(UIBarButtonItem *)sender {//發送文字//發送數據給所有已連接設備NSError *error=nil;[self.session sendData:[@"藍牙數據傳輸" dataUsingEncoding:NSUTF8StringEncoding] toPeers:[self.session connectedPeers] withMode:MCSessionSendDataUnreliable error:&error];NSLog(@"開始發送數據...");if (error) {NSLog(@"發送數據過程中發生錯誤,錯誤信息:%@",error.localizedDescription);}}#pragma mark - MCSession代理方法 -(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{NSLog(@"didChangeState");switch(state){case MCSessionStateConnected:NSLog(@"連接成功.");break;case MCSessionStateConnecting:NSLog(@"正在連接...");break;default:NSLog(@"連接失敗.");break;} } @end

再看一下作為發現節點的程序:

#import "ViewController.h" #import <MultipeerConnectivity/MultipeerConnectivity.h>@interface ViewController ()<MCSessionDelegate, MCBrowserViewControllerDelegate,UIPopoverControllerDelegate> @property (strong,nonatomic) MCSession *session; @property (strong,nonatomic) MCBrowserViewController *browserController; @property (strong, nonatomic) IBOutlet UILabel *label; @end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//創建節點MCPeerID *peerID=[[MCPeerID alloc]initWithDisplayName:@"KenshinCui"];//創建會話_session=[[MCSession alloc]initWithPeer:peerID];_session.delegate=self; }- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated. }#pragma mark- UI事件 - (IBAction)browserClick:(UIButton *)sender {_browserController=[[MCBrowserViewController alloc]initWithServiceType:@"cmj-stream" session:self.session];_browserController.delegate=self;[self presentViewController:_browserController animated:YES completion:nil]; }#pragma mark - MCBrowserViewController代理方法 -(void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController{NSLog(@"已選擇");[self.browserController dismissViewControllerAnimated:YES completion:nil]; } -(void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController{NSLog(@"取消瀏覽.");[self.browserController dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - MCSession代理方法 -(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{NSLog(@"didChangeState");switch (state) {case MCSessionStateConnected:NSLog(@"連接成功.");[self.browserController dismissViewControllerAnimated:YES completion:nil];break;case MCSessionStateConnecting:NSLog(@"正在連接...");break;default:NSLog(@"連接失敗.");break;} } //接收數據 -(void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{NSLog(@"開始接收數據...");//接收文字信息NSLog(@"%@", [NSThread currentThread]);//(<NSThread: 0x170270540>{number = 3, name = (null)})dispatch_async(dispatch_get_main_queue(), ^{NSString *string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];self.label.font = [UIFont systemFontOfSize:14.0];self.label.text = string;}); } @end

MultipeerConnectivity傳輸數據三種方式:

通過MultipeerConnectivity傳輸數據有三種方式分別為:

  • Messages

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

  • Streams

    Streams 使用 -startStreamWithName:toPeer:創建:

  • Resources

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


Messages(普通數據傳輸)

  • 普通數據的發送方式:
NSError *error=nil;NSData *data = [[NSString stringWithFormat:@"--我們要顯示的信息--"] dataUsingEncoding:NSUTF8StringEncoding];[self.session sendData:data toPeers:[self.session connectedPeers] withMode:MCSessionSendDataUnreliable error:&error];if (error) {NSLog(@"發送數據過程中發生錯誤,錯誤信息:%@",error.localizedDescription);}

普通數據的接收方式

所有數據的傳輸都在session(會話)中進行。因此,接收函數為session的回調函數。這里需要注意一下,此回調函數不是在主線程中進行的,所以我們如果要改變UI的話,需要轉化到主線程 -(void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{//NSLog(@"%@", [NSThread currentThread]);//(<NSThread: 0x170270540>{number = 3, name = (null)})dispatch_async(dispatch_get_main_queue(), ^{NSString *string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];self.textView.text = string;}); }

Streams(數據流傳輸)

數據流傳輸可以用在音頻播放傳輸。

  • 數據流基本概念介紹:

什么是數據流?
流是位數據通過通信路徑的連續傳送序列。它是單向的,從一個應用程序的角度,流可以是輸入流(讀操作流)或者輸出流(寫操作流),除了基于文件的流之外,其余的都是non-seekable的。一旦流數據被提供或者被使用,數據就不能夠從流中獲取到。

數據流的簡單分類? Cocoa包括三種與流有關的類:NSStream,NSInputStream,NSOutputStream.
NSStream是抽象類,它定義了流對象的基本接口和屬性。NSInputStream和NSOutputStream是NSStream的子類,它們實現了輸入流和輸出流的基本操作。你可以為存儲在內存中,向文件或者C
buffer寫的流數據創建NSOutputStream對象;可以為從NSData對象和文件中讀取的流數據創建NSInputStream對象;也可以在網絡套接字的兩端創建NSInputStream和NSOutputStream對象,通過流對象,你可以不用一次性將所有的流數據加載到內存中。

輸入數據流(NSInputStream)的使用步驟?
1. 從數據源創建和初始化一個NSInputStream實例
2. 將輸入流對象配置到一個run loop,open the stream
3. 通過流對象的delegate函數處理事件
4. 當所有數據讀完,進行流對象的內存處理

輸出數據流(NSOutputStream)的使用步驟?
1. 使用存儲寫入數據的存儲庫創建和初始化一個NSOutputSteam實例,并且設置它的delegate。
2. 將這個流對象布置在一個runloop上并且open the stream。
3. 處理流對象向其delegate發送的事件消息。
4. 如果流對象向內存中寫入了數據,那么可以通過使用NSStreamDataWrittenToMemoryStreamKey屬性獲取數據。
5. 當沒有數據可供寫入時,清理流對象。

了解學習數據流可以參考:
http://blog.csdn.net/CrazyChickOne/article/details/37565317

  • 數據流的發送方式
NSError *error;self.outputStream = [self.session startStreamWithName:@"super stream" toPeer:[self.session.connectedPeers firstObject] error:&error];self.outputStream.delegate = self;[self.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];if(error || !self.outputStream) {NSLog(@"%@", error);}else{[self.outputStream open];}
  • 數據流的接收方式
- (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID{NSLog(@"獲取流數據");self.inputStream = stream;self.inputStream.delegate = self;[self.inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];[self.inputStream open]; }
  • 數據流代理操作
/*** 流數據操作** @param aStream 流數據* @param eventCode 流數據獲取事件*/ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{switch (eventCode) {case NSStreamEventOpenCompleted:{NSLog(@"數據流開始");self.byteIndex = 0;self.streamData = [[NSMutableData alloc]init];}break;case NSStreamEventHasBytesAvailable:{//有數據可用NSInputStream *input = (NSInputStream *)aStream;uint8_t buffer[1024];NSInteger length = [input read:buffer maxLength:1024];NSLog(@"%ld", length);[self.streamData appendBytes:(const void *)buffer length:(NSUInteger)length];// 記住這邊的數據陸陸續續的}break;case NSStreamEventHasSpaceAvailable:{//有空間可以存放NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:[self recordPath]]];NSOutputStream *output = (NSOutputStream *)aStream;NSUInteger len = ((data.length - self.byteIndex >= 1024) ? 1024 : (data.length-self.byteIndex));NSData *data1 = [data subdataWithRange:NSMakeRange(self.byteIndex, len)];[output write:data1.bytes maxLength:len];self.byteIndex += len;}break;case NSStreamEventEndEncountered:{//結束[aStream close];[aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];if([aStream isKindOfClass:[NSInputStream class]]){NSFileManager *fileManager = [[NSFileManager alloc]init];[fileManager createFileAtPath:[self recordPath] contents:self.streamData attributes:nil];}self.byteIndex = 0;}break;case NSStreamEventErrorOccurred:{//發生錯誤NSLog(@"error");}break;default:break;} }

Resources(數據源傳輸)

在傳輸比較大型的文件時,我們通常使用數據源傳輸。

  • 數據源傳輸的發送方式
    在數據源傳輸中,我們可以通過KVO觀察,來觀察數據傳輸的狀況。下面代碼是觀察數據發送者傳送文件的進度。
    KVO鍵值觀察了解:http://blog.csdn.net/daiyibo123/article/details/45923575
NSURL *fileURL = [NSURL fileURLWithPath:[self imagePath]];self.imageProcess = [self.session sendResourceAtURL:fileURL withName:@"image" toPeer:[self.session.connectedPeers firstObject] withCompletionHandler:^(NSError *error) {\if (error) {NSLog(@"發送源數據發生錯誤:%@", error);}dispatch_async(dispatch_get_main_queue(), ^{//數據傳送完畢[self.progressViewImage setProgress:0 animated:NO];});}];//KVO觀察[self.imageProcess addObserver:selfforKeyPath:@"completedUnitCount"options:NSKeyValueObservingOptionNewcontext:nil];
  • 數據源傳輸的接受方式
    數據傳輸都是在session的代理方法中進行的。數據源傳送方式有兩個代理方法,分別為數據源傳輸剛剛開始調用session: didStartReceivingResourceWithName: fromPeer: withProgress:和數據源傳輸結束時調用session: didFinishReceivingResourceWithName: fromPeer: atURL: withError:。
    數據源傳輸剛剛開始調用:一般用于設置一些初始值,比如文件接受者的進度Progress進度KVO觀察。
    數據源傳輸結束時調用:主要用于將傳輸的文件從暫時存放的位置放到真正需要存放的位置。

  • 數據源傳輸剛剛開始調用

- (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress{NSLog(@"開始獲取文件數據");self.imageProcess = progress;//KVO觀察[self.imageProcess addObserver:selfforKeyPath:@"completedUnitCount"options:NSKeyValueObservingOptionNewcontext:nil]; }
  • 數據源傳輸結束時調用
- (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error{NSLog(@"獲取文件數據結束");NSURL *destinationURL = [NSURL fileURLWithPath:[self imagePath]];//判斷文件是否存在,存在則刪除if ([[NSFileManager defaultManager] isDeletableFileAtPath:[self imagePath]]) {[[NSFileManager defaultManager] removeItemAtPath:[self imagePath] error:nil];}//轉移文件NSError *error1 = nil;if (![[NSFileManager defaultManager] moveItemAtURL:localURL toURL:destinationURL error:&error1]) {NSLog(@"[Error] %@", error1);} }
  • KVO鍵值觀察
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ // NSLog(@"keyPath:%@", keyPath); // NSLog(@"object:%@", object); // NSLog(@"change:%@", change); // NSLog(@"context:%@", context);dispatch_async(dispatch_get_main_queue(), ^{int64_t numberCom = self.imageProcess.completedUnitCount;int64_t numberTotal = self.imageProcess.totalUnitCount;NSLog(@"%lld/%lld", numberCom, numberTotal);}); }

參考網站:

iOS7新技術:如何使用Multipeer Connectivity

NSStream

總結

以上是生活随笔為你收集整理的ios MultipeerConnectivity蓝牙通讯的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。