看了這,我也能設計乙個通訊協議

2021-09-25 00:00:26 字數 2739 閱讀 4473

最近給 tkeyboard 增加了不少新功能,其中最有意義的部分是,通過藍芽和 wifi,配合 protobuf,在 mac 和 ios 之間建立了兩條便捷的資料交換通道,算是打好了未來產品開發的地基,網路通道建立好了,後續的想象空間也大。

比如,現在可以將 iphone 上 safari 正在瀏覽的網頁,一鍵分享到 mac 的 safari 上,比 handoff 更方便可靠。還可以在 mac 的 safari 瀏覽網頁時,如果發現的有趣的,一鍵儲存到 iphone 上。總之,兩個端之間現在已經暢通無阻,文字,url,,等等各類資料可以隨意分享,關鍵在於怎麼去分析場景,因地制宜了。

這確實是乙個選擇,這種選擇我們可以歸納為 text based protocol,其好處是,http 已經是個偏向於應用層的非常成熟的通訊協議,一來一往,乙個 request 對應乙個 response,簡單易用,http 本身已經做好了包的切割,包內部結構的定義等,應用層只需要從中取資料即可。但其缺點也明顯,http 出於使用簡單的考慮,將協議設計成一來一回,response 發完之後即斷開請求,節省伺服器資源,是一種無狀態的協議,http 協議的使用者如果要做進一步的定製,需要採取一些奇技淫巧。而且基於 text 的協議,在協議解析的時候對於包裡面的 text 內容都有嚴格限制。

大家都知道 http url 裡面傳入引數時,需要經過 url encode,其原因就是由於 http 是基於文字的協議,在解析 url 中的 query string 時,是以一些特殊的文字符號來做切割的,比如 ?,=,& 等。如果不把內容做 encode,就有可能導致協議解析出錯。當然 http 協議的設計者替我們解決了這些問題,我們做為使用者可以高枕無憂。

http 方案另乙個弊端在於流量上面,短連線每次重複握手,每個 request 裡面有重複的 header 設定等,都會造成流量的浪費,這也是不少遊戲客戶端會建立自己的 socket 長連線通道的原因,自己設計基於流的通訊協議,這就是我們的另乙個選擇方案,stream based protocol。

tkeyboard 就是選擇了基於 stream 的協議方案,corebluetooth 上並沒有現成的基於 http 的通訊方案,所以只能自己動手做個簡單的設計,基於流的協議設計複雜度可大可小,端看應用場景。

工作量主要在於兩方面,一是資料的序列化,即將我們平時所用的 model 轉化為二進位製流,其二是定義好包的格式,在通訊框架裡做好包的切割,解析,和傳遞。最後簡化呼叫流程,提供一套簡單的類似 http 的雙向資料呼叫 api 即可。

tkeyboard 的應用場景比較簡單,所以我在設計上也做了不少簡化,大致想大家介紹下思路。

第乙個序列化的問題好解決,已有 google 的成熟方案 protobuf 可以使用,而且還有基於 objective c 的版本,model 的序列化和反序列化,乙個 api 呼叫即可完成。

第二個問題是包的格式定義。學習 tcp/ip 的意義在這裡就能體現了,無論是 tcp 包還是 ip 包,都有自己的包格式定義,而且往往是乙個 header 配合乙個 payload(類似於 http 的 body)。之所以要有包,是因為二進位製流只完成 stream 的傳輸,並不知道一次資料請求與相應的起始和結束,我們要預先定義好包結構才能做解析。這一步其實就是乙個實現 rpc 呼叫的過程,關於 rpc 現在雖然還沒有什麼官方的協議通過,但是我們完全可以自己設計乙個簡單可用的協議版本。

要能實現包的準確切割,我們需要明確包的長度。所以必須在 header 中留乙個字段,表達整個包(header + payload)由多少 bytes 構成,兩個位元組的長度就可以描述 0~65535 個位元組數,具體使用多少個位元組就看協議的使用場景了。

因為是 rpc 呼叫協議,所以包體裡必須有呼叫的名稱,即 api name 字段,這個 name 是可變長度,所以也需要將其長度資訊加入包體中,原則上,所有可變長度的內容都需記錄其精確的長度資訊,否則無法做資訊的切割。另外,呼叫方還需要知道包是請求的回應(response)還是另一端的通知(notify),所以我們還需要定義 call type 資訊,這種資訊乙個 8 位元位的列舉量就綽綽有餘了,這種固定長度的資訊就不需要記錄其長度資訊了。

一般固定長度的資訊我們放在 header 中,可變長度的資訊我們則放入 payload 中,當然,我們 rpc 呼叫的具體引數(經由 protobuf 序列化之後的 stream)也是放入 payload 中,接收方接收以後,只需讀取固定長度的位元組,即可通過反序列化,再在接收方還原成具體的應用層資料。

做完這些,就差不多有乙個簡單的通訊協議雛形了。

當然這其中還有不少細節需要處理,比如 ios 上基於 lebt 的藍芽方案,基於能耗考慮,傳送方每次能傳送的位元組數是有限制的,據我測試,乙個 central 每次向 pheripheral 傳送資料的時候,最多只能寫 512 個位元組,所以 central 端要做乙個 send buffer,傳送一次就去 buffer 中取 512 個位元組,多了就會傳送失敗。pheripheral 端接收資料也是一樣,每次只能收到 20 個 bytes,需要設計乙個 receive buffer,將資料全部快取起來,每次收到資料就做一次解析,看資料量是否夠乙個最小包的大小,夠就取出,不夠就繼續等待。

因為我們是在設計應用層協議,所以還需要考慮傳輸層是可靠還是不可靠,corebluetooth 實際上既提供了類似於 tcp 的可靠傳輸(cbcharacteristicpropertyindicate),也有類似於 udp 的不可靠傳輸(cbcharacteristicpropertynotify),不明白這一點,必然會踩坑。

還有不少其他問題要處理,比如資料的加解密,增加 crc 做包的完整性校驗,協議的版本控制,遠端呼叫的超時機制等,這裡就不一一贅述了,大家可以自己去嘗試實現下,光說不練假把式,動過手,箇中滋味自會明了。

我要有乙個朱芳雨,我也夠

我要乙個葉莉就已足夠 姚明我平生只跟乙個女孩談過戀愛,那就是我的女朋友。第一次見到葉莉的時候,我17歲。她在女子國家隊裡訓練。但那個時候,我沒有跟她說話,也不想在我進入國家隊之前就約她出去。但是只要可能的話,我會向報社的攝影記者要女子國家隊的多餘 在1999年我終於進入了國家隊後,我約葉莉出去玩。她...

我也來寫乙個貪吃蛇

最近工作量好大,好忙,趁週末練練手,花了近3小時寫了乙個貪吃蛇。後續會把自己的一些想法繼續更新,目前是1.3版本 更新過4次,文末有更新說明 實現貪吃蛇的功能很簡單。我就分享一下我實現貪吃蛇看起來在介面上移動並且吃食物長大的原理。我建了乙個陣列list arr來儲存貪吃蛇所在的每個格仔的id,並建了...

小白也能懂 如何運營乙個內容專題?

之前發了一篇 關於內容專題,這有乙份從 0 到 1 的運營方案 被行外朋友吐槽好像很有用但專業名詞太多看不懂,只適合 還是有相關基礎 的童鞋看。於是衍生出了這一篇文章,旨在希望沒做過內容專題運營或者沒做過運營的朋友,靠著文章也能想象到運營乙個簡單的內容專題大概有什麼樣的畫面。乙個小內容專題,基本會有...