參考資料:
建立一個項目 ProtobufServer 然後添加 SuperSocket 和 protobuf 的依賴包。
添加protobuf依賴包 輸入的搜尋詞是 Google.ProtocolBuffers
添加SuperSocket依賴包 輸入搜尋詞是 SuperSocket,要添加兩個SuperSocket.Engine 和 SuperSocket
這裡說明一下:為什麼protobuf明明序列化成Protobuf Data 了為什麼還要再加一個Length來打包,因為tcp這個流發送會參數粘包、分包,如果不加個協定來解析會讀取錯誤的資料而導緻無法反序列化 Protobuf Data (自行谷歌 tcp 粘包、分包)
就是我們可以弄唯一個資料包,然後這個資料包裡面必須包含一個枚舉值,然後還包含了其他類型的資料包,每一個枚舉值對應一個資料包,然後傳送過來後,可以用分支判斷來擷取值。
那我們先設計一個 DefeatMessage.proto包含内容:
然後再把CallMessage和BackMessage補全
然後在我們的路徑packages\Google.ProtocolBuffers.2.4.1.555\tools裡面有兩個工具protoc.exe 和 protogen.exe,我們可以執行下面的指令來生成我們的c#代碼
protoc --descriptor_set_out=DefeatMessage.protobin --proto_path=pack --include_imports pack\DefeatMessage.proto
protogen DefeatMessage.protobin
注意路徑要自己修改
如果有報Expected top-level statement (e.g. "message").這麼一個錯誤,那就是你cmd的編碼和proto的編碼不一緻,要改成一緻。
生成完c#代碼後,我們就要設計ProtobufRequestInfo了。這個比較簡單,隻要實作IRequestInfo接口。我們這裡在實作接口帶的屬性外另加一個 DefeatMessage 和 DefeatMessage.Types.Type,其中DefeatMessage是為了存儲我們解包完資料後反序列化出來的對象,Type是為了友善區分我們應該取出DefeatMessage裡面的哪個值。
readBuffer: 接收緩沖區, 接收到的資料存放在此數組裡
offset: 接收到的資料在接收緩沖區的起始位置
length: 本輪接收到的資料的長度
toBeCopied: 表示當你想緩存接收到的資料時,是否需要為接收到的資料重新建立一個備份而不是直接使用接收緩沖區
rest: 這是一個輸出參數, 它應該被設定為當解析到一個為正的請求後,接收緩沖區還剩餘多少資料未被解析
主要是接收到資料的一個方法實作,當然ss裡面還帶了指令模式的實作,不過這個不在本文章裡面說。這裡的實作了接收到不同的資料給列印出來,然後接收到CallMessage資料的話就給用戶端回發一條資訊
伺服器的代碼就到這裡,可以編譯運作起來看看有無錯誤。
與伺服器實作相同,先通過NuGet添加 SuperSocket.ClientEngine 和 protobuf 的依賴包。
有三個實作:
把前面實作伺服器時候生成的通訊資料包拷貝過來,然後和實作伺服器的ProtobufRequestInfo一樣,隻不過這裡隻是實作接口IPackageInfo而已
這裡的資料解析的實作與伺服器的實作有點不同,不過下一個版本可能會統一,如果統一起來的話,那麼以後資料解析就可以做成和插件一樣,同時可以給伺服器和用戶端使用。
data:也是資料緩存區
rest:緩存區還剩下多少
這個實作與伺服器的不同就在BufferList本身就已經有處理分包,就不需要我們自己再做處理。
這個真的沒有什麼好說了。運作效果如下:
這裡的列印資訊是相對比較簡單,大家可以自己下載下傳源碼來加些列印資料,讓看起來更好看點。
既然前面提到了Netty,那就順便實作一個簡單的伺服器來通訊看看。
Netty的實作在網絡上有超級多的例子,這裡就簡單的介紹一下就可以,首先先生成java的通訊包代碼
protoc --proto_path=pack --java_out=./ pack/DefeatMessage.proto
protoc --proto_path=pack --java_out=./ pack/BackMessage.proto
protoc --proto_path=pack --java_out=./ pack/CallMessage.proto
這幾個指令要自己靈活改變們不要死死的硬搬。
因為Netty裡面已經有幫我們實作了protobuf的解析,是以我們不需要自己實作。我們隻要繼承ChannelInboundHandlerAdapter然後通過channelRead就可以拿到解析好的對象,然後轉換成我們自己的類型,就可以直接使用。這裡同樣是實作不同類型的消息列印和CallMessage消息就回複資訊給用戶端。
主要是添加已經有的編碼解碼和消息接收的類就可以了。
整個代碼編寫完成後,直接運作并打開我們前面的用戶端進通訊發資料。運作結果如下:
當然這三個例子直接簡單的說明如何使用架構來解決我們的問題,實際開發過程中肯定不隻是我們例子的這麼點東西,需要考慮的東西還很多,這裡隻是寫一些可以運作起來的例子作為抛磚引玉。希望能給不懂的同學有點啟發作用。謝謝您百忙中抽出時間來觀看我的分享。
由于本人水準有限,知識有限,文章難免會有錯誤,歡迎大家指正。如果有什麼問題也歡迎大家回複交流。要是你覺得本文還可以,那麼點選一下推薦。
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。