分享套接字数据包序列化与反序列化方法
分享套接字?jǐn)?shù)據(jù)包序列化與反序列化方法
“簡單說一下,本文不涉及Socket的連接、數(shù)據(jù)接收,只是對數(shù)據(jù)包(byte[])的序列化和反序列化方法的封裝介紹。
本文目錄
本文背景
一般操作
本文操作
總結(jié)
1.本文背景
經(jīng)常做C/S,客戶端與服務(wù)端通信基本是TCP/UDP通信,套接字用得飛起。
比如我們有一個(gè)系統(tǒng),這個(gè)系統(tǒng)又分幾個(gè)系統(tǒng)子模塊進(jìn)程:
C++服務(wù)端
Android 客戶端
iOS 客戶端
WPF桌面管理端 ......
幾個(gè)模塊之間通過TCP或者UDP通信,數(shù)據(jù)包解析與組裝是常規(guī)操作,我們定義數(shù)據(jù)包格式如下:
一個(gè)數(shù)據(jù)包包含包頭和包體,定義如下:
包頭
| 1 | 消息標(biāo)識 | int | 用于標(biāo)識數(shù)據(jù)包是否合法 |
| 2 | 名稱 | string | 當(dāng)前消息名稱,用于標(biāo)識數(shù)據(jù)包類型 |
| 3 | 版本號 | int | 當(dāng)前消息版本號,允許程序中消息存在多個(gè)版本,用于版本迭代 |
包含這三個(gè)字段:消息標(biāo)識、名稱、版本號,唯一確定消息對象。
包體
| 1 | 字段1 | 數(shù)據(jù)類型 | 字段1 |
| 2 | 字段2 | 數(shù)據(jù)類型 | 字段2 |
包體直接定義字段信息,就像定義類屬性一樣。
另包頭與包體中數(shù)據(jù)類型定義如下:
數(shù)據(jù)包字段類型定義
| 1 | int | 4個(gè)字節(jié)的整型值 |
| 2 | string | 組成格式:字符串實(shí)際值字節(jié)長度(2個(gè)字節(jié))+字符串實(shí)際值byte |
| 3 | char | 單字節(jié)值 |
| 4 | 列表 | 組成格式:4個(gè)字節(jié)列表長度+列表實(shí)際數(shù)據(jù)值byte |
| 5 | 字典 | 同上,具體看源碼 |
其他數(shù)據(jù)類型類似,復(fù)雜數(shù)據(jù)類型使用4個(gè)字節(jié)的值字節(jié)長度+實(shí)際值byte。
給一個(gè)測試數(shù)據(jù)包
| 1 | 消息標(biāo)識 | int | 取值:0x4A534604 |
| 2 | 消息名稱 | string | 三國信息,取值:"ThreeCountries" |
| 3 | 版本號 | int | 取值:1 |
| 4 | 編號 | int | 給三國一個(gè)編號吧,取值:1 |
| 5 | 國名 | string | 取值:"蜀國" |
| 6 | 皇帝 | string | 取值:"劉備" |
| 7 | 大將個(gè)數(shù) | int | 5 |
| 8 | 大將1編號 | int | 取值:1 |
| 9 | 大將1名字 | string | 取值:"張飛" |
| 10 | 大將1備注 | string | 取值:"三板斧" |
| 11 | 大將2編號 | int | 取值:2 |
| 12 | 大將2名字 | string | 取值:"關(guān)羽" |
| 13 | 大將2備注 | string | 取值:"青龍偃月刀" |
| 14 | 大將3編號 | int | 取值:3 |
| 15 | 大將3名字 | string | 取值:"趙云" |
| 16 | 大將3備注 | string | 取值:"很猛的" |
| 17 | 大將4編號 | int | 取值:4 |
| 18 | 大將4名字 | string | 取值:"馬超" |
| 19 | 大將4備注 | string | 取值:"強(qiáng)" |
| 20 | 大將5編號 | int | 取值:5 |
| 21 | 大將5名字 | string | 取值:"黃忠" |
| 22 | 大將5備注 | string | 取值:"老當(dāng)益壯" |
大致理解下:
前三個(gè)字段是包體:用于標(biāo)識整個(gè)數(shù)據(jù)包,便于包體解析;
后面的包體,簡單說就是三國中的國家信息簡介,前三個(gè)字段為三國中的一個(gè)國家基本信息:編號、國名、皇帝,后面是該國家大將信息列表,每個(gè)大將有編號、名稱、備注等。
定義數(shù)據(jù)對象
根據(jù)數(shù)據(jù)包定義,我們可以很快定義類進(jìn)行使用,不管你是C++還是Java。下面是我用C#寫的對應(yīng)類,用于序列化與反序列化使用:
///?<summary> ///?三國 ///?</summary> public?class?ThreeCountries {///?<summary>///?獲取或者設(shè)置?ID///?</summary>public?int?ID?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?國名///?</summary>public?string?Name?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?皇帝///?</summary>public?string?Emperor?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?所選課程列表///?</summary>public?List<FamousGeneral>?Courses?{?get;?set;?}public?override?string?ToString(){return?$"三國之一{ID}:{Name}皇帝{Emperor},有?{Courses.Count}名大將";} }///?<summary> ///?三國名將 ///?</summary> public?class?FamousGeneral {///?<summary>///?獲取或者設(shè)置?編號///?</summary>public?int?ID?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?名字///?</summary>public?string?Name?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?描述///?</summary>public?string?Memo?{?get;?set;?}public?override?string?ToString(){return?$"{ID}:{Name}=>{Memo}";} }對于上面給的數(shù)據(jù)包你怎么序列化及反序列化?轉(zhuǎn)換成數(shù)據(jù)如下,下節(jié)接著討論
ThreeCountries?shuKingdom?=?new?ThreeCountries {ID?=?1,Name?=?"蜀國",Emperor?=?"劉備",Courses?=?new?System.Collections.Generic.List<FamousGeneral>{new?FamousGeneral{?ID=1,Name="張飛",Memo="三板斧"},new?FamousGeneral{?ID=2,Name="關(guān)羽",Memo="青龍偃月刀"},new?FamousGeneral{?ID=3,Name="趙云",Memo="很猛的"},new?FamousGeneral{?ID=3,Name="馬超",Memo="強(qiáng)"},new?FamousGeneral{?ID=3,Name="黃忠",Memo="老當(dāng)益壯"},} };2. 常規(guī)操作
序列化
代碼太繁瑣,我就寫個(gè)不正規(guī)的偽代碼吧
定義一個(gè)byte數(shù)組; 一、寫包頭 1、寫入4字節(jié)的消息標(biāo)識:0x4A534604 計(jì)算消息對象名稱字符串“ThreeCountries”長度,及轉(zhuǎn)換字符串為byte數(shù)組 2、寫入2字節(jié)的bytes數(shù)組長度,寫入實(shí)際的byte數(shù)組值 3、寫入4字節(jié)的消息版本號 二、寫包體 4、寫入4字節(jié)的大將個(gè)數(shù) 循環(huán)每個(gè)大將信息,依次寫入 5、寫入大將1編號 6、寫入大將1名稱 7、寫入大獎(jiǎng)1備注 8、寫入大將2編號 9、寫入大將3名稱 10、寫入大獎(jiǎng)4備注 ...寫吐了,省略號反序列化
不想寫了,累常規(guī)操作
定義一個(gè)序列化接口,每個(gè)網(wǎng)絡(luò)對象實(shí)現(xiàn)其中的序列化與反序列化接口
public?interface?ISerializeInterface {byte[]?Serialize<T>(T?t);T?Deserialize<T>(byte[]?arr); }public?class?ThreeCountries?:?ISerializeInterface {public?byte[]?Serialize<T>(T?t){//?將上面的序列化代碼寫在這}T?Deserialize<T>(byte[]?arr){//?將上面的反序列化代碼寫在這,不好意思我沒寫} }3. 本文操作
寫了半天的Demo,文章可能就寫的有點(diǎn)水了,我估計(jì)讀者也不會仔細(xì)看代碼,直接去Github check項(xiàng)目去了,哈哈。
我還是簡單說說吧,實(shí)現(xiàn)很簡單,定義一些特性,下面紅框里的代碼文件:
序列化特性及幫助類使用很簡單,在上面的數(shù)據(jù)類上加上特性,改動(dòng)不多,看下面代碼:
///?<summary> ///?三國 ///?</summary> [NetObject(Name?=?"ThreeCountries",?Version?=?1)] public?class?ThreeCountries {///?<summary>///?獲取或者設(shè)置?ID///?</summary>[NetObjectProperty(ID?=?1)]public?int?ID?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?國名///?</summary>[NetObjectProperty(ID?=?2)]public?string?Name?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?皇帝///?</summary>[NetObjectProperty(ID?=?3)]public?string?Emperor?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?所選課程列表///?</summary>[NetObjectProperty(ID?=?4)]public?List<FamousGeneral>?Courses?{?get;?set;?}public?static?NetObjectAttribute?CurrentObject?=?null;static?ThreeCountries(){CurrentObject?=?NetObjectSerializeHelper.GetAttribute<ThreeCountries,?NetObjectAttribute>(default(ThreeCountries));}public?override?string?ToString(){return?$"三國之一{ID}:{Name}皇帝{Emperor},有?{Courses.Count}名大將";} }///?<summary> ///?三國名將 ///?</summary> public?class?FamousGeneral {///?<summary>///?獲取或者設(shè)置?編號///?</summary>[NetObjectProperty(ID?=?1)]public?int?ID?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?名字///?</summary>[NetObjectProperty(ID?=?2)]public?string?Name?{?get;?set;?}///?<summary>///?獲取或者設(shè)置?描述///?</summary>[NetObjectProperty(ID?=?3)]public?string?Memo?{?get;?set;?}public?override?string?ToString(){return?$"{ID}:{Name}=>{Memo}";} }仔細(xì)看的話,只在外層類(ThreeCountries)上加了NetObject特性,和屬性上加了NetObjectProperty特性,分別標(biāo)識消息名稱、版本號及每個(gè)屬性的序列化與反序列化順序即可,類中使用的子對象Courses屬性,也只需要加屬性特性即可,如上。
下面添加單元測試,并且測試通過:
單元測試通過4. 總結(jié)
用這套代碼(demo,有所改變,但也差不多),完成了幾個(gè)類似的項(xiàng)目,每次數(shù)據(jù)通信聯(lián)調(diào)、測試問題,C++和java的同事找我時(shí),我就說:
"你先看你自己數(shù)據(jù)包的序列化和反序列化代碼有沒有問題,我這不會出問題的,完全按數(shù)據(jù)包格式轉(zhuǎn)的。"
剛開始還在那鬧,后面定位幾次問題后,類似的問題他們就沒再找我了,偷笑中。
demo寫了半天,還是有點(diǎn)累,源碼:見開源項(xiàng)目TerminalMACS。
原文鏈接:https://dotnet9.com/16583.html
歡迎關(guān)注我的微信公眾號:Dotnet9
Dotnet9微信公眾號謝謝您的支持,需要任何資源,只需要在公眾號后臺回復(fù)對應(yīng)數(shù)字即可,切記只回復(fù)“數(shù)字”:
01:dotnet
02:java
03:android
04:C++
05:qt
06:react
沒有的資源或資源鏈接失效,請給我留言或加我微信,通過百度網(wǎng)盤好友分享。
另:大部分資源可在我的網(wǎng)站搜索哦:https://dotnet9.com
總結(jié)
以上是生活随笔為你收集整理的分享套接字数据包序列化与反序列化方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 技术债! 怎样简洁高效的实现多个 Enu
- 下一篇: 回顾 | 进击吧! Blazor !第三