你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS開發之玩轉藍牙CoreBluetooth

iOS開發之玩轉藍牙CoreBluetooth

編輯:IOS開發基礎

之前詳細談過不少關於HTTP協議的知識點,TCP/IP也通過tcpdump做過簡單的介紹,但網絡協議的本質其實是連接,設備或者端之間連接的方式有多種,常見的http或者基於tcp的socket只是森林一葉,還有些不那麼常見的協議比如藍牙。適當腦洞,也能玩出不少新花樣來。

關鍵概念

談到藍牙,很容易讓人聯想到藍牙穿戴設備,好像聽起來更靠近硬件層一些。蘋果其實對iOS和OSX上的藍牙已做了一層很好的封裝,看過CoreBluetooth Framework的大致API之後,基本上就將其流程明白個大概。難點在於理解其工作模式和理清一些關鍵概念,比如Peripehral, Central, Service, characteristics等等,不要被這些陌生的單詞嚇到,網絡協議的應用大多脫不了CS的架構模型,這裡和大家一起對照傳統的Client/Server架構來梳理下iOS和OSX上CoreBluetooth的重要知識點。我畫了一張圖,方便大家一目了然的明白CoreBluetooth的工作原理。

bt00.png

我們只需要把Peripehral, Central, Service, characteristics幾個概念理清,再各自對應到我們之前關於CS的知識體系之中就可以輕松的做一層自己的封裝了。

初次查看CoreBluetooth文檔的時候,很容易把Central理解成Server,其實剛好相反,Peripheral才是我們的Server。正如上圖所示,Peripheral和Central之間建立的是一對多的關系。每個Peripheral會以廣播的模式告訴外界自己能提供哪些Service,這裡Service的概念和我們傳統CS架構當中的Service基本是一致的,每個PeriPheral可以提供多個Service,而每個Service呢,會包含多個characteristic,characteristic是個陌生但十分關鍵的概念,可以把characteristic理解成一個Service模塊具體提供哪些服務,比如一個心率監測Service同時包含心率測量characteristic和地理位置定位characteristic。

Peripheral作為Server,Central作為Client,Peripheral廣播自己的Service和characteristic,Central訂閱某一個具體的characteristic,Peripheral就和Central之間通過characteristic建立了一個雙向的數據通道,整個模型非常簡潔而且符合我們CS的架構體系。接下來具體看下CoreBluetooth的相關API。

優雅的CoreBluetooth

首先值得開心一把的是iOS和OSX使用的是同一套API封裝,都是基於CoreBluetooth Framework,只在極細小的地方有些差異,完全可以做一層library的封裝在兩個平台上無縫銜接使用。

在具體搭建基於CoreBluetooth應用之前,要先確立到底哪一方作為Peripheral,哪一方又是Central。Macbook,iPhone,iPad都能成為Peripheral或者Central。我們通過代碼的方式再看一遍上面的架構流程。

Server端

創建Peripheral,也就是我們的Server:

_peripheral = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

生成Service以備添加到Peripheral當中:

CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES];

生成characteristics以備添加到Service當中:

self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]
                                                                     properties:CBCharacteristicPropertyNotify|CBCharacteristicPropertyWrite
                                                                          value:nil
                                                                    permissions:CBAttributePermissionsReadable|CBAttributePermissionsWriteable];

建立Peripheral,Server,characteristics三者之間的關系並開始廣播服務:

//建立關系
transferService.characteristics = @[self.transferCharacteristic];
[self.peripheral addService:transferService];
//開始廣播
[self.peripheral startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];

Client端

創建我們的Central,也就是client:

_central = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

掃描可用的Peripheral:

[self.central scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]
                                         options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];

掃描到Peripheral之後連接:

[self.central connectPeripheral:targetPeripheral options:nil];

連接成功之後查找可用的Service:

[peripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]];

找到Service之後,進一步查找可用的Characteristics並訂閱:

//查找Characteristics
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]] forService:service];

查找到Characteristics訂閱:

//訂閱
[peripheral setNotifyValue:YES forCharacteristic:characteristic];

訂閱之後Central和Peripheral之間就建立了一個雙向的數據通道,後續二者之間的數據傳輸就可以通過characteristic來完成了。

數據傳輸

有了數據通道,接下來就是如何傳輸數據了。說到數據傳輸就免不了要確定應用層的協議,類似平時我們使用socket實現游戲的網絡模塊時,需要自定義應用層協議才能實現業務數據的交換,協議的設計這裡就不展開說了,之前有過相關經驗的童鞋完全可以把協議層遷移過來。

再看下Peripheral是如何向Central發送數據的,首先Peripheral會向自己的characteristic寫數據:

[self.peripheral updateValue:chunk forCharacteristic:self.transferCharacteristic onSubscribedCentrals:@[self.central]];

Central那一端會通過如下回調收到來自Peripheral的數據流:

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

這裡值得注意的是二者數據的發送與獲取,是以二進制流的方式發送的,是NSData的形式封裝的,Peripheral可以持續不停的發送二進制流,所以Central端收到的時候需要自己做協議的解析,根據自定義協議將整個流拆成一個個的業務Packet包。

而Central發送的時候確是封裝成了一個個的Request,比如Central端調用如下API發送數據:

[self.discoveredPeripheral writeValue:data forCharacteristic:self.discoveredCharacterstic type:CBCharacteristicWriteWithoutResponse];

Peripheral端會收到如下回調:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests

數據被封裝成了單獨的CBATTRequest,直接去Request當中取value就可以獲取到Central所發送過來的數據。

已知的坑

我之前測試協議的時候發現一個不大不小的坑,多個Central(比如A和B)端同時一個Peripheral發送數據的時候,Peripheral會收到多個CBATTRequest,奇怪的是每個CBATTRequest當中的Central都會指向最先建立連接的A,結果導致Peripheral端無法判斷write請求的數據來自哪一個Central。

簡單腦洞

藍牙不僅僅能應用於穿戴式設備,還能做一些好玩的小眾應用或者游戲,其本質是一個小型封閉的局域網,不用經過第三方的Server或者Cloud,很安全。

比如兩台iPhone設備之間通過基於藍牙的IM App進行聊天(距離這麼近,為什麼不當面聊,黑人問號?)。

比如一些基於藍牙對戰的小游戲。

比如通過藍牙在iPhone和Macbook之間做數據同步。


  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved