<a target="_blank" href="http://t.cn/RhfSa04">基于藍牙對等網絡通信就是使用Game Kit中的GKSession、GKSessionDelegate、GKPeerPickerController和GKPeerPickerControllerDelegate來實作。開發過程分為3個步驟:連接配接、發送資料和接收資料。</a>
<a target="_blank" href="http://t.cn/RhfSa04">下面我們通過一個執行個體介紹一下基于藍牙對等網絡通信過程。使用者點選“連接配接”按鈕,建立連接配接過程中會出現連接配接對話框,根據具體情況也會彈出其它的對話框。這些都是針對藍牙對等網絡标準對話框,而Wifi對等網絡沒有标準對話框可以使用,需要開發者自己實作。當兩個裝置連接配接好之後,兩個玩家就可以連續輕點“點選”按鈕,點選的次數會傳遞給對方,倒計時時間是30秒。</a>
<a target="_blank" href="http://t.cn/RhfSa04"></a>
<a target="_blank" href="http://t.cn/RhfSa04">1、連接配接</a>
<a target="_blank" href="http://t.cn/RhfSa04">由于對等網絡連接配接過程有點複雜,貫穿了這些協定和類,我們繪制了連接配接過程的流程圖。</a>
<a target="_blank" href="http://t.cn/RhfSa04">下面我們通過代碼直接介紹連接配接流程,其中ViewController.h代碼如下:</a>
<a target="_blank" href="http://t.cn/RhfSa04">[java] view plaincopy</a>
<a target="_blank" href="http://t.cn/RhfSa04">#import <UIKit/UIKit.h> </a>
<a target="_blank" href="http://t.cn/RhfSa04"> </a>
<a target="_blank" href="http://t.cn/RhfSa04">#import <GameKit/GameKit.h> </a>
<a target="_blank" href="http://t.cn/RhfSa04"> </a>
<a target="_blank" href="http://t.cn/RhfSa04">#define GAMING 0 //遊戲進行中 </a>
<a target="_blank" href="http://t.cn/RhfSa04">#define GAMED 1 //遊戲結束 </a>
<a target="_blank" href="http://t.cn/RhfSa04">@interface ViewController : UIViewController <GKSessionDelegate, GKPeerPickerControllerDelegate> </a>
<a target="_blank" href="http://t.cn/RhfSa04">{ </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSTimer *timer; </a>
<a target="_blank" href="http://t.cn/RhfSa04">} </a>
<a target="_blank" href="http://t.cn/RhfSa04">@property (weak, nonatomic) IBOutlet UILabel *lblTimer; </a>
<a target="_blank" href="http://t.cn/RhfSa04">@property (weak, nonatomic) IBOutlet UILabel *lblPlayer2; </a>
<a target="_blank" href="http://t.cn/RhfSa04">@property (weak, nonatomic) IBOutlet UILabel *lblPlayer1; </a>
<a target="_blank" href="http://t.cn/RhfSa04">@property (weak, nonatomic) IBOutlet UIButton *btnConnect; </a>
<a target="_blank" href="http://t.cn/RhfSa04">@property (weak, nonatomic) IBOutlet UIButton *btnClick; </a>
<a target="_blank" href="http://t.cn/RhfSa04">@property (nonatomic, strong) GKPeerPickerController *picker; </a>
<a target="_blank" href="http://t.cn/RhfSa04">@property (nonatomic, strong) GKSession *session; </a>
<a target="_blank" href="http://t.cn/RhfSa04">- (IBAction)onClick:(id)sender; </a>
<a target="_blank" href="http://t.cn/RhfSa04">- (IBAction)connect:(id)sender; </a>
<a target="_blank" href="http://t.cn/RhfSa04">//清除UI畫面上的資料 </a>
<a target="_blank" href="http://t.cn/RhfSa04">-(void) clearUI; </a>
<a target="_blank" href="http://t.cn/RhfSa04">//更新計時器 </a>
<a target="_blank" href="http://t.cn/RhfSa04">-(void) updateTimer; </a>
<a target="_blank" href="http://t.cn/RhfSa04">@end </a>
<a target="_blank" href="http://t.cn/RhfSa04">使用Game Kit需要引入頭檔案<GameKit/GameKit.h>,之前需要把GameKit.framework架構添加到工程中。而且定義類的時候需要實作協定GKSessionDelegate和GKPeerPickerControllerDelegate,并且定義GKPeerPickerController類型的屬性picker,定義GKSession類型的屬性session。</a>
<a target="_blank" href="http://t.cn/RhfSa04">ViewController.m中建立GKPeerPickerController對象的代碼如下:</a>
<a target="_blank" href="http://t.cn/RhfSa04">- (IBAction)connect:(id)sender { </a>
<a target="_blank" href="http://t.cn/RhfSa04">_picker = [[GKPeerPickerController alloc] init]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">_picker.delegate = self; ① </a>
<a target="_blank" href="http://t.cn/RhfSa04">_picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby; ② </a>
<a target="_blank" href="http://t.cn/RhfSa04">[_picker show]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">使用者點選的連接配接按鈕時,觸發connect:方法。在該方法中建立GKPeerPickerController對象。建立完成不要忘記設定GKPeerPickerController委托為self,第②行代碼所示。在第③行代碼中connectionTypesMask屬性是設定對等網絡連接配接類型,其中有兩種類型選擇:GKPeerPickerConnectionTypeNearby和GKPeerPickerConnectionTypeOnline,GKPeerPickerConnectionTypeNearby用于藍牙通訊也是預設的通訊方法,GKPeerPickerConnectionTypeOnline用于Wifi通訊的區域網路通訊,這種方式麻煩,需要開發人員自己設計UI畫面,自己使用Bonjour服務發現管理連接配接,以及自己編寫輸入輸出流實作通訊。如果給使用者一個選擇對話框,代碼可以如下編寫:</a>
<a target="_blank" href="http://t.cn/RhfSa04">_picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby | GKPeerPickerConnectionTypeOnline;</a>
<a target="_blank" href="http://t.cn/RhfSa04">其中“線上”就是GKPeerPickerConnectionTypeOnline類型,“附近”就是GKPeerPickerConnectionTypeNearby類型。</a>
<a target="_blank" href="http://t.cn/RhfSa04">連接配接成功之後回調ViewController.m中的回調委托方法peerPickerController:didConnectPeer:toSession:代碼:</a>
<a target="_blank" href="http://t.cn/RhfSa04">- (void)peerPickerController:(GKPeerPickerController *)pk didConnectPeer:(NSString *)peerID </a>
<a target="_blank" href="http://t.cn/RhfSa04">toSession:(GKSession *) session </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSLog(@”建立連接配接”); </a>
<a target="_blank" href="http://t.cn/RhfSa04">_session = session; ① </a>
<a target="_blank" href="http://t.cn/RhfSa04">_session.delegate = self; ② </a>
<a target="_blank" href="http://t.cn/RhfSa04">[_session setDataReceiveHandler:self withContext:nil]; ③ </a>
<a target="_blank" href="http://t.cn/RhfSa04">_picker.delegate = nil; </a>
<a target="_blank" href="http://t.cn/RhfSa04">[_picker dismiss]; ④ </a>
<a target="_blank" href="http://t.cn/RhfSa04">[_btnClick setEnabled:YES]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">[_btnConnect setTitle:@"斷開連接配接" forState:UIControlStateNormal]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">//開始計時 </a>
<a target="_blank" href="http://t.cn/RhfSa04">timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self </a>
<a target="_blank" href="http://t.cn/RhfSa04">selector:@selector(updateTimer) </a>
<a target="_blank" href="http://t.cn/RhfSa04">userInfo:nil repeats:YES]; ⑤ </a>
<a target="_blank" href="http://t.cn/RhfSa04">上述代碼第①行_session = session将委托方法中傳回的會話參數指派給成員變量,這樣我們就獲得了一個會話對象。這種方式中,會話ID是應用程式的包ID,如果想自己配置設定會話ID,可以實作下面委托方法,在方法中使用GKSession的構造方法initWithSessionID:displayName: sessionMode:,自己建立會話對象。</a>
<a target="_blank" href="http://t.cn/RhfSa04">- (GKSession *)peerPickerController:(GKPeerPickerController *)picker </a>
<a target="_blank" href="http://t.cn/RhfSa04">sessionForConnectionType:(GKPeerPickerConnectionType)type { </a>
<a target="_blank" href="http://t.cn/RhfSa04">GKSession *session = [[GKSession alloc] initWithSessionID: <自定義SessionID> </a>
<a target="_blank" href="http://t.cn/RhfSa04">displayName:<顯示的名字> sessionMode:GKSessionModePeer]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">return session; </a>
<a target="_blank" href="http://t.cn/RhfSa04">有的時候會話的狀态會發生變化,我們要根據狀态的變化做一些UI的清理和資源的釋放。監測狀态變化在委托方法session:peer:didChangeState:中實作,方法代碼如下:</a>
<a target="_blank" href="http://t.cn/RhfSa04">- (void)session:(GKSession *)session peer:(NSString *)peerID </a>
<a target="_blank" href="http://t.cn/RhfSa04">didChangeState:(GKPeerConnectionState)state </a>
<a target="_blank" href="http://t.cn/RhfSa04">if (state == GKPeerStateConnected) </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSLog(@”connected”); </a>
<a target="_blank" href="http://t.cn/RhfSa04">} else if (state == GKPeerStateDisconnected) </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSLog(@”disconnected”); </a>
<a target="_blank" href="http://t.cn/RhfSa04">[self clearUI]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">其中GKPeerStateConnected常量是已經連接配接狀态,GKPeerStateDisconnected常量是斷開連接配接狀态。</a>
<a target="_blank" href="http://t.cn/RhfSa04">2、發送資料</a>
<a target="_blank" href="http://t.cn/RhfSa04">發送資料的代碼如下:</a>
<a target="_blank" href="http://t.cn/RhfSa04">- (IBAction)onClick:(id)sender { </a>
<a target="_blank" href="http://t.cn/RhfSa04">int count = [_lblPlayer1.text intValue]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">_lblPlayer1.text = [NSString stringWithFormat:@"%i",++count]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSString *sendStr = [NSString </a>
<a target="_blank" href="http://t.cn/RhfSa04">stringWithFormat:@"{\"code\":%i,\"count\":%i}",GAMING,count]; ① </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSData* data = [sendStr dataUsingEncoding: NSUTF8StringEncoding]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">if (_session) { </a>
<a target="_blank" href="http://t.cn/RhfSa04">[_session sendDataToAllPeers:data </a>
<a target="_blank" href="http://t.cn/RhfSa04">withDataMode:GKSendDataReliable error:nil]; ② </a>
<a target="_blank" href="http://t.cn/RhfSa04">3、接收資料</a>
<a target="_blank" href="http://t.cn/RhfSa04">為了接收資料首先需要在設定會話時候通過[_session setDataReceiveHandler:self withContext:nil]語句設定接收資料的處理程式是self。這樣當資料到達時候就會觸發下面的方法特定:</a>
<a target="_blank" href="http://t.cn/RhfSa04">- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer </a>
<a target="_blank" href="http://t.cn/RhfSa04">inSession:(GKSession *)session context:(void *)context </a>
<a target="_blank" href="http://t.cn/RhfSa04">id jsonObj = [NSJSONSerialization JSONObjectWithData:data </a>
<a target="_blank" href="http://t.cn/RhfSa04">options:NSJSONReadingMutableContainers error:nil]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSNumber *codeObj = [jsonObj objectForKey:@"code"]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">if ([codeObj intValue]== GAMING) { </a>
<a target="_blank" href="http://t.cn/RhfSa04">NSNumber * countObj= [jsonObj objectForKey:@"count"]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">_lblPlayer2.text = [NSString stringWithFormat:@"%@",countObj]; </a>
<a target="_blank" href="http://t.cn/RhfSa04">} else if ([codeObj intValue]== GAMED) { </a>
<a target="_blank" href="http://t.cn/RhfSa04">上面的代碼是接收到資料之後,進行JSON解碼,取出遊戲狀态和點選次數。</a>
<a target="_blank" href="http://t.cn/RhfSa04">主要的程式代碼就是這些,根據具體的業務情況還可以能有所變化,讀者可以下載下傳完整代碼在兩台之間裝置或是一個裝置一個模拟器之間進行測試。</a>