1. 程式人生 > >Sproto(與客戶端通訊協議)

Sproto(與客戶端通訊協議)

這裡寫圖片描述
在和客戶端通訊時,需要制訂一套通訊協議。 skynet 並沒有規定任何通訊協議,所以你可以自由選擇。
sproto 是一套由 skynet 自身提供的協議,並沒有特別推薦使用,只是一個選項。sproto 有一個獨立專案存在 。同時也複製了一份在 skynet 的原始碼庫中。
它類似 google protobuffers ,但設計的更簡單,也更利於 lua 使用。同時還提供了一套簡單的 rpc 方案。
關於 sproto 的編碼協議,在 sproto 的 readme 中已有詳述,下面介紹其 RPC 部分。
9.1 RPC
首先我們需要定義一個訊息包的主體格式。它必須有一個叫 type 的欄位,描述 RPC 到底是哪一條訊息。還需要有一個 session 欄位來表示迴應訊息的對應關係。通常這兩個欄位都被定義成 integer 。

.package {
    type 0 : integer--訊息型別
    session 1 : integer--迴應訊息對應的關係
}

使用 sproto 的 rpc 框架,每條訊息都會以這條訊息開頭,接上真正的訊息內容;連線在一起後用 sproto 的 0-pack 方式打包。注意,這個資料包並不包含長度資訊,所以真正在網路上傳輸,還需要新增長度資訊,方便分包。當然,如果你使用 skynet 的 gate 模組的話,約定了以兩位元組大端表示的長度加內容的方式分包。
構造一個 sproto rpc 的訊息處理器,應使用:
local host = sproto:host(packagename) – packagename 預設值為 “package” 即對應前面的 .package 型別。你也可以起別的名字。
這條呼叫會返回一個 host 物件,用於處理接收的訊息。
host:dispatch(msgcontent)
用於處理一條訊息。這裡的 msgcontent 也是一個字串,或是一個 userdata(指標)加一個長度。它應符合上述的以 sproto 的 0-pack 方式打包的包格式。
dispatch 呼叫有兩種可能的返回類別,由第一個返回值決定:
(1)REQUEST : 第一個返回值為 “REQUEST” 時,表示這是一個遠端請求。如果請求包中沒有 session 欄位,表示該請求不需要回應。這時,第 2 和第 3 個返回值分別為訊息型別名(即在 sproto 定義中提到的某個以 . 開頭的型別名),以及訊息內容(通常是一個 table );如果請求包中有 session 欄位,那麼還會有第 4 個返回值:一個用於生成迴應包的函式。
(2)RESPONSE :第一個返回值為 “RESPONSE” 時,第 2 和 第 3 個返回值分別為 session 和訊息內容。訊息內容通常是一個 table ,但也可能不存在內容(僅僅是一個迴應確認)。
local sender = host:attach(sp) – 這裡的 sp 指向外發出的訊息協議定義。
attach 可以構造一個傳送函式,用來將對外請求打包編碼成可以被 dispatch 正確解碼的資料包。
這個 sender 函式接受三個引數(name, args, session)。name 是訊息的字串名、args 是一張儲存用訊息內容的 table ,而 session 是你提供的唯一識別號,用於讓對方正確的迴應。 當你的協議不規定需要回應時,session 可以不給出。同樣,args 也可以為空。
9.2 Sproto Loader
由於 skynet 採用的是多 lua 虛擬機器。如果在每個 VM 裡都載入相同的 sproto 協議定義就略顯浪費。所以 skynet 還提供了一個叫 sprotoloader 的模組來共享它們。
其實現原理是在 C 模組中提供了 16 個全域性的 slot ,可以通過 sprotoloader.register 或 sprotoloader.save 在初始化時,載入需要的協議,並儲存在這些 slot 裡。通常我們只需要兩個 slot ,一個用於儲存客戶端到伺服器的協議組,另一個用於儲存伺服器到客戶端的協議組。分別位於 slot 1 和 2 。

這樣,在每個 vm 內,都可以通過 sprotoloader.load 把協議載入到 vm 中。
注意:這套 api 並非執行緒安全。所以必須自行保證在初始化完畢後再做 load 操作。(load 本身是執行緒安全的)。