藍牙 4.0 ATT屬性協議

轉自:https://mp.weixin.qq.com/s?__biz=MjM5MzM4MDM3Mw%3D%3D&chksm=b28fa48285f82d94f2b1ac8a7486a9c7582ad88cc29b3c14d16969929142a429e7c0bd4b7177&idx=1&mid=2448221688&scene=21&sn=a0238ec6dab2b2760dc3bbff71749596

本節主要內容: ble4.0協議講解 13 之“快遞員”屬性協議(ATT)

預習知識:

屬性包括三種類型:服務項、特徵值和描述符。三者之間存在樹狀包含關係,服務項包含一個或多個特徵值,特徵值包含一個或多個描述符,多個服務項組織在一起,構成屬性規範(Attribute Profile)。對於常用的屬性規範,比如體重計、心率計,BLE協會做了具體定義,這樣的話,只要BLE主從設備均遵守某個Profile來進行設計,那麼二者就能夠優雅的通信。

BLE的屬性類型是有限的,有四個大類:

  • Primary Service(首要服務項)
  • Secondary Service(次要服務項)
  • Include(包含服務項)
  • Characteristic(特徵值)

這些屬性類型分別對應了指定的UUID,BLE對這些UUID與屬性類型的映射關係做了規定:

  • 0x1800 – 0x26FF :服務項類型
  • 0x2700 – 0x27FF :單位
  • 0x2800 – 0x28FF :屬性類型
  • 0x2900 – 0x29FF :描述符類型
  • 0x2A00 – 0x7FFF :特徵值類型

權限(Permission)花樣比較多:

        ​​​​​​​訪問權限(Access Permission)- 只讀、只寫、讀寫

        加密權限(Encryption Permission) – 加密、不加密

        認證權限(Authentication Permission) – 需要認證、無需認證

        授權權限(Authorization Permission) – 需要授權、無需授權

 

屬性協議規定了客戶端(Client)和服務器(Server)之間通信的方式,共六種:

  • Request(請求)
  • Response(響應)
  • Command(命令)
  • Indication(指示)
  • Confirmation(確認)
  • Notification(通知)

客戶端發送Request,服務器需要返回一個Response,表明服務器收到了。

服務器發送Indication,客戶端需要返回一個Confirmation,表明客戶端收到了。

以上兩種方式,均是單線程操作,即下一個Request/Indication操作需要在上一個操作收到Response/Confirmation之後才能開始。

客戶端發送Command,服務器無需任何返回。

服務器發送Notification,客戶端無需任何返回。

因此Command和Notification是不可靠的通信。當通信環境不佳,客戶端頻繁發送Command,可能發生服務器接收不到或丟棄的情況,Notification也類似。

 

3.4、屬性協議(ATT)

上節是通過Profile搭建數據庫,這節定義通信協議進行數據庫的數據讀寫操作。通信協議主要3個方面:通信方式、通信包格式、具體通信數據包。

3.4.1、通信協議方法

屬性協議主要用來發現、讀寫、通知和指示屬性。具體如表3-12

這裏涉及到“原子操作”,上面的通信方式中Request和Response是一對,Indication和Confirmation是一對,在沒有得到對方的應答或者確認信息之前是不能進行第2包的請求或者指示數據。命令和通知因爲沒有應答信息,所以可以在任何時候進行包傳輸。

3.4.2、屬性協議包格式

    協議包格式如圖3-26所示。包含有1字節的操作碼和其他參數,其他參數有兩種格式,一種是不包含加密認證信息的參數,另一種是帶有12字節的加密認證信息的協議包。

 

需要注意的是,如果鏈路本身是加密的,那麼包含認證標示的包將不被髮送。因爲加密鏈路已經進行一次安全認證,不需要再次進行認證。協議原文如下:

An Attribute PDU  that includes an Authentication Signature should not be sent on an encrypted  link. Note: an encrypted link already includes authentication data on every  packet and therefore adding more authentication data is not required.

3.4.3、屬性協議PDUs

屬性協議數據包,是用來對屬性數據庫操作的實體。這一節幾乎全部用圖來描述。具體怎麼發送數據庫中的服務,下節中會講到ATT協議怎麼對應GATT服務發現。

3.4.3.1、交換MTU

    圖3-27爲交換MTU包格式。圖3-28爲交換MTU示意。圖3-29爲Sniffer採集空中交換MTU數據。

圖3-27 交換MTU包格式

 

圖3-28交換MTU示意

圖3-29 Sniffer採集空中交換MTU數據

 

在低功耗藍牙連接中,屬性協議默認的MTU爲23字節。一般客戶端是不會發起這個請求的,它的默認值就是23,當雙方交換的值不同時,用較小的那個作爲最終使用的值。

3.4.3.2、找信息請求\應答(Find InformationRequest\Response)

    圖3-30爲找信息請求\應答包格式。圖3-31爲找信息請求\應答示意。圖3-32爲Sniffer採集空中找信息請求\應答數據。

 

查找信息請求和回覆用來查找一系列屬性的句柄和類型信息。這是唯一一個能讓客戶端發現任意屬性類型的消息。

查找信息請求包含有兩個句柄:起始句柄和結束句柄。它們定義了該請求用到的屬性句柄範圍。爲了找到所有數值的屬性,該請求的起始句柄將是0x0001,結束句柄設爲0xFFFF。但是回覆的信息中因爲長度限制,並不能包含所有的屬性,所以需要再次請求,只是將起始句柄改爲查找到的最大句柄的後一個句柄開始。

查找信息響應包含句柄-類型對。它有兩種格式,一種是基於16位的UUID格式,允許在一個包中最多包含有5個屬性句柄-類型對;另一種是基於128位的UUID,允許包含有1個屬性句柄-類型對。在同一應答包中不能包含有這兩種格式。

3.4.3.3、按類型值查找請求\應答(Find By Type Value Request\Response)

     圖3-33爲按類型值查找請求\應答包格式。圖3-34爲按類型值查找請求\應答示意。圖3-35爲Sniffer採集空中按類型值查找請求\應答數據。

按類型值查找請求和回覆可以根據給定的類型與數值查找相應的屬性。該請求包含有兩個句柄:起始句柄和結束句柄,規定查找範圍。對於這一範圍類的所有屬性,如果和請求中所指定的類型和數值一樣,那麼這個屬性就要在響應中返回。

 

這個命令主要用來查找特定的首要服務。發送請求時,客戶端將其中的類型設置爲首要服務,並將數值設爲該服務的UUID。隨後的響應將包含查找到的各個首要服務的實例的句柄範圍。某些特殊的服務只會在服務器中實現一次,針對它們的響應只會包含一個句柄範圍。

3.4.3.4、按類型讀請求\應答(Read ByType Request\Response)

圖3-36爲按類型讀請求\應答包格式。圖3-37爲按類型讀請求\應答示意。圖3-38爲Sniffer採集空中按類型讀請求\應答數據。

這個命令能在句柄範圍內讀取某個屬性值。當客戶端僅知道屬性的類型而非句柄時可以使用該請求。請求包含有起始、結束句柄和需要讀取的屬性的類型。響應將給出符合的句柄和數值。

這個請求用於搜索被包含的服務,並通過特性類型來發現服務中的所有的特性。它也被用來讀取已知類型的特性值。

 

3.4.3.5、讀請求\應答(Read Request\Response)

圖3-39爲讀請求\應答格式。圖3-40爲讀請求\應答示意。圖3-41爲Sniffer採集空中讀請求\應答數據。

讀取請求是屬性協議總最簡單的請求。該請求包含一個句柄,響應將返回該句柄對應的屬性值。只有在客戶端已知屬性句柄的情況下,才能使用該請求讀取屬性值。

 

3.4.3.6、大對象讀請求\應答(Read BlobRequest\Response)

圖3-42爲大對象讀請求\應答格式。圖3-43爲大對象讀請求\應答示意。

有時屬性值太長,無法裝入一個讀取響應,須使用大對象讀取請求來獲取剩餘字節。大對象讀取請求不光包含屬性句柄,還包含屬性值的這個數據中的偏移量。其響應將從屬性偏移量開始,包含儘可能多的屬性值。

在獲得了屬性值的前22個字節後,假如客戶端還想獲取後續的屬性值,則使用大對象讀取請求。下一條響應將返回第23個字節到44個字節;如此繼續,直到客戶端讀取到完整的屬性值。

該請求用於讀取長特徵值與長特徵描述符。

3.4.3.7、多重讀取請求\應答(Read Multiple Request\Response)

圖3-44爲多重讀取請求\應答包格式。圖3-45爲多重讀取請求\應答示意。

用來在一個操作中讀取多個屬性值。該請求包含一個或多個屬性句柄,響應則安之請求的順序返回相應的屬性值。然而因爲響應中的數值之間沒有界限,因此,在請求中除了最後一個屬性允許可變的長度,其他屬性必須爲定長的屬性值。也就是說,如果客戶端用一個多重讀取請求來讀取三個屬性,前兩個屬性的長度必須固定,最後一個屬性的長度則可以變化。

如果客戶端請求讀取的屬性值的長度超過了響應數據包所能承載的最大長度,那麼無法放入響應數據包的數值將被丟棄。

 

3.4.3.8、按組類型讀取請求\應答(Read ByGroup Type Request\Response)

圖3-46爲按組類型讀取請求\應答包格式。圖3-47爲按組類型讀取請求\應答示意。圖3-48爲Sniffer採集空中按組類型讀取請求\應答數據。

它和按類型讀取請求類型,也包含有一個句柄範圍,讀取時將其視爲一個屬性的類型來處理,只不過屬性的類型必須爲分組屬性。其響應包含所讀取的屬性句柄、屬性分組中最後一個屬性以及屬性的數值。

這意味着,如果分組類型是首要服務,它將返回所有首要服務聲明的屬性句柄、該首要服務中最後一個屬性以及首要服務聲明的數值。因此,可以僅憑單個請求來發現設備上的所有首要服務、與之關聯的屬性句柄的範圍以及這些服務的類型。

如同其他返回對個句柄-數值對的響應,如果返回的值長度可變,那麼只有長度相同的屬性值將在第一個響應中被返回。因此,客戶端必須再次發起請求,更新起始句柄來發現想要獲取的下一個屬性。

3.4.3.9、寫請求\應答(WriteRequest\Response)

圖3-49爲寫請求\應答包格式。圖3-50爲寫請求\應答示意。圖3-51爲Sniffer採集空中寫請求\應答數據。

3.4.3.10、寫命令(Write Command)

圖3-52爲寫命令包格式。圖3-53爲寫命令示意。圖3-54爲Sniffer採集空中寫命令數據。

寫入命令類似於讀取請求,區別是寫入命令沒有響應。寫入命令包含要寫入的屬性的句柄和要寫入的數值。

當無需響應時,可以使用寫入命令。此外,因爲該命令可以在任何時刻發送,即使剛發送了一條請求還未收到相應的響應,對命令的發送時延有較高的要求時,該請求也適合。

 

3.4.3.11、簽名寫命令(Signed Write Command)

圖3-55爲簽名寫命令包格式。圖3-56爲簽名寫命令示意。

 

簽名寫命令和寫命令類似,只是前者包含認證簽名。通過這樣的機制,發送端可以向服務器發送寫入命令時認證自己,而無需加密通信連接。簽名寫命令適用於以下兩種場合:

 發起加密將顯著增加數據連接的延遲

 發起加密將顯著增加簡短且無需加密的數據的送達成本

認證簽名由簽名計數器和消息認證碼構成。簽名計數器對設備間發送的每一條消息賦予不同的值,不論消息發送的間隙連接中斷與否。使用簽名計數器可以抵禦消息重放攻擊,因此,假如簽名寫入命令的簽名計數器值和先前的值相同,該命令會被忽略。消息認證碼長度爲64位,該碼位於句柄、數值和簽名計數器之後。注意,服務器需要爲每一個客戶端保存其最後使用的簽名計數器。

3.4.3.12、準備寫請求\應答(Prepare Write Request\Response) 和執行寫請求\應答(ExecuteWrite Request\Response)

準備寫請求和執行寫請求實現兩種功能:

 它們提供了長屬性值的寫入功能

 它們允許在一個單獨執行的原子操作中寫入多個值

屬性服務器包含一個準備寫入隊列,其中保存有準備寫入請求。隊列的大小可獨立配置,但通常它足夠儲存所需要準備寫入的服務。只有在收到執行寫入請求時,準備寫入的值纔會寫入屬性,也就是說執行寫入請求給出了執行這些準備寫入操作的開始信號。

準備寫請求包含句柄、偏移量和部分屬性值,這和大對象讀取類似。這說明客戶端即可以在隊列中準備多個屬性值,有可以準備一個長屬性值的各個部分。這樣,在真正執行準備隊列之前,客戶端可以確定某屬性的所有部分都能寫入服務器。

準備寫入響應也包含請求中的句柄、偏移量和部分屬性值。之所以這樣做是爲了數據傳遞的可靠性。客戶端可以對比響應和請求的字段值,保證準備的數據被正確接收。

圖3-57爲準備寫請求\應答包格式。圖3-58爲準備寫請求\應答示意。

可以這麼理解,準備寫入請求,之所以是“準備寫”,也就是沒有真正的寫到屬性中去,上文提到準備寫是寫在服務器中的單獨的一個緩衝器中,當所有需要寫入的數據都發送給服務器後,那麼就要下一個命令,即爲執行寫,“執行寫”也就是真正的執行寫入屬性的操作。

完整過程是,一旦接收完所有的準備寫入請求,服務器將擁有一個隨時可以執行的準備寫入隊列。客戶端發送表示位爲“立刻寫入”的執行寫入請求,隨後服務器將在一次原子操作中寫入所有值。屬性將按照其準備的順序寫入。如果客戶端多次準備了同一個屬性值,那麼服務器將按照順序向該屬性寫入這些值。這意味着如果使用準備隊列配置硬件狀態,例如硬件需要先後執行禁用、配置、重新啓用操作,那麼可以用一個準備隊列來實現:在隊列總先將對應的屬性寫入“禁用”,接着寫入“配置”,之後在準備隊列的末尾寫入“啓用”,隨後執行該隊列。這樣便能在一個原子操作中實現該硬件的重新配置。

圖3-59爲執行寫請求\應答包格式。圖3-60爲可靠寫請求\應答示意。

 

3.4.3.13、句柄通知(Handle Value Notification)

圖3-61爲句柄通知包格式。圖3-62爲句柄通知示意。

 

當服務器想要向客戶端發送快速得屬性狀態更新時,可以發送一條句柄值通知。這個是服務器能夠發給客戶端的兩種消息中的一種,並且是不要求響應的那種。服務器可以在任何時刻發送該通知,同時該通知也是不可靠的。

句柄值通知包含屬性句柄和數值。因此該通知是服務器發往客戶端的一條消息,用來告知某屬性的當前值。它是屬性協議中最重要的消息之一。它不僅讓客戶端能夠有效地從服務器獲取當前屬性數據庫的更新,而且也被用來通知客戶端有限狀態機的變化。

一般來說,服務器會爲所有的綁定設備配置通知。這樣做的好處在於當某客戶端重新與設備建立連接時,該設備上的服務能夠立刻將自己當前的狀態通知該客戶端。例如,將某設備的電池電量配置爲通知。

因爲通知不具備任何確認機制,它們可以在任何時刻發送,而不管當時正在進行什麼用的操作,一次,通知適用於必須立刻向客戶端發送信息的情況。

3.4.3.14、句柄指示\確認(HandleValue Indication\Confirmation)

句柄值指示類似於句柄值通知。它有着相同的屬性句柄字段和數值,不同的客戶端收到指示以後應回覆。服務器一次只能發送一條指示,並且只有收到確認響應後才能發起下一條指示。

句柄值確認不含任何數據,主要用於流控。因爲具備了確認機制,指示被視爲可靠傳輸。一旦服務器收到確認信息,它便能確定客戶端收到了該信息。

圖3-63爲句柄指示\確認包格式。圖3-64爲句柄指示\確認示意。

 

 

當某設備無法完成請求所要求的操作時,它可以發送錯誤響應。例如某設備通過發送寫入請求試圖寫入某個屬性,但該屬性是隻讀的,那麼服務器應發送錯誤響應來給出請求失敗的原因,而不是發送寫入響應正常。

錯誤響應包含導致錯誤的與請求相關的所有信息,例如請求失敗的屬性、導致錯誤的首要原因等。一旦客戶端收到錯誤響應,它會認爲該響應與其發送的最後一條請求相對應。因此,錯誤響應是另一種利用響應消息來終止請求操作的方法。這也說明對每一條可以發送的請求都會有兩種可能的響應:失敗錯誤響應和成功響應。

圖3-65爲句柄指示\確認包格式。表3-13爲錯誤代碼表。

下面對錶中的代碼進行一一說明:

 無效句柄  請求中的屬性句柄是無線的,它在服務器中不存在。該錯誤是由於服務器不使用或者不具備請求讀取或者寫入的屬性句柄。當屬性句柄被設置爲0x0000時也可以發送該錯誤。

 不允許讀取  該屬性不允許讀取其屬性值。例如當客戶端試圖讀取某控制點的只寫是屬性時,可以發送該錯誤。

 不允許昔寫入  該屬性不允許寫入其屬性值。例如試圖寫入只讀屬性值將導致該錯誤。

 PDU無效  服務器不理解收到的請求。通常當客戶端發送了錯誤格式的請求時,將導致該錯誤。例如,讀取請求必須有兩字節的句柄作爲其唯一參數。當讀取請求不具備兩字節的參數時將導致該錯誤。

 認證不足  當兩設備缺少相互認證時,便無法執行某屬性值的讀取或寫入操作。爲了進行認證,可以將連接加密,或是在設備還未配對時使用安全管理配對規程對設備進行配對與連接。

 請求不支持  服務器理解發送的請求但當前無法執行。當服務器未實行某個已知的請求,或是不理解某個未來的請求時,可以使用該錯誤。該錯誤可以作爲設備無法執行請求時的默認錯誤。

 偏移量無效  請求包含偏移量,但偏移量是無效的。偏移量和屬性值的長度相同不會導致該錯誤,但會得到一個長度爲0的值。

 準備隊列已滿  由於用於存儲等待寫入隊列的空間已滿,準備寫入請求無法被接收。

 屬性不存在  屬性經遍歷不存在。該錯誤僅用於在一個屬性範圍內查找一種或幾種特定的屬性類型時。只有查找信息請求、按類型讀取請求與按組類型讀取請求會導致該錯誤。對於查找信息請求而言,該錯誤意味着沒有找到給定的句柄範圍內的屬性。對按類型值查找請求、按類型讀取請求和按組類型讀取請求而言,該錯誤說明在某句柄範圍內沒有找到給定類型的屬性。

 屬性非大對象  當大對象讀取請求所要讀取的屬性不是大對象類型時,該請求被拒絕,客戶端必須改用讀取請求。該錯誤僅和定長的屬性值相關,並且該長度小於大對象讀取請求當前用到的MTU長度。由此簡單的做法是先使用讀取請求讀取屬性值的前22字節,隨後使用大對象讀取請求讀取後續的字節。

 祕鑰長度不足  該錯誤是由於在連接已經加密、認證與授權充足的情況下,配對期間協商的祕鑰長度不滿足服務的要求。有些屬性值需要使用高強度的祕鑰來充分保證數據的機密性。

 未知錯誤  這或許是個最棒的錯誤碼了。如果導致該錯誤的情形事先沒有考慮到過,便是“未知錯誤”。

 加密不足  可以讀取或寫入屬性值,但連接未加密。一些屬性值需要加密連接來保證數據的機密性。

 組類型不支持  服務器不認爲請求中包含的屬性類型是組類型。按組類型讀取請求只能使用服務器已知的組類型。

 資源不足  服務器的資源不足以接收和執行某特定的請求。例如,某服務配置某設備廣播數據時,若當前廣播的數據加上需要增加的廣播的數據後,數據總長度過長,則會導致該錯誤。

 應用錯誤  某請求對服務中的屬性執行了不允許的操作,服務分配了錯誤碼來報告錯誤的所在。這是一個128-255範圍內的錯誤,實際的含義有包含該屬性的服務規格書來定義。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章