日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

二进制序列化

發布時間:2023/12/4 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 二进制序列化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在計算機世界,萬物皆01二進制,包括各種各樣的文件格式和網絡協議,二進制格式最為常見!NewLife.Core 內置了完整的二進制序列化框架 Binary,經過十多年洗禮,發展到了第三代支持Handler處理器擴展。Binary的同類框架有 Protobuf、Thrift、MessagePack。

Nuget包:NewLife.Core

源碼地址:https://github.com/NewLifeX/X/tree/master/NewLife.Core/Serialization/Binary

主要特性

Binary主要功能特性:

  • 體積極小。Binary是Schemaless架構,不包含字段名和序號,用最少的字節去保存數據

  • 壓縮整數。大多數時候Int32字段保存的數字很小,采用七位壓縮編碼整數保存可以減少體積

  • 格式簡單。盡管Binary只有.NET版,但其格式非常簡單,可以很容易在其它語言上實現

  • 支持性很廣。Binary設計初衷,就是用于實現各種已知文件格式和通信協議,例如ZipFile

  • 支持動態特性。可根據某些字段值,生成不同消息類型,例如MQTT和DNS協議

  • 可讀性較差。二進制格式且沒有Schema,可讀性較差

  • 無版本支持。需要讀寫雙方約定好多版本格式的兼容

  • Binary設計理念,就是用最小的體積去保存數據,且能夠靈活實現各種文件格式和通信協議的序列化。

    直接序列化對象,在沒有使用額外壓縮算法的條件下,Binary幾乎是結果體積最小的序列化框架。

    快速用法

    想要序列化一個對象,或者反序列化一個數據流到對象,最直接的想法就是這樣

    // 快速讀取 public static T FastRead<T>(Stream stream, Boolean encodeInt = true); // 快速寫入 public static Packet FastWrite(Object value, Boolean encodeInt = true); public static void FastWrite(Object value, Stream stream, Boolean encodeInt = true);

    Binary.FastWrite 可以直接把一個對象序列化為數據包Packet,可以理解為字節數組Byte[]的包裝。

    Binary.FastRead 從數據流中反序列化得到目標類型的對象,這里必須指定目標類型,否則Binary不知道應該如何解析。

    例子

    [Fact] public void Fast() {var model = new MyModel { Code = 1234, Name = "Stone" };var pk = Binary.FastWrite(model);Assert.Equal(8, pk.Total);Assert.Equal("D2090553746F6E65", pk.ToHex());Assert.Equal("0gkFU3RvbmU=", pk.ToArray().ToBase64());var model2 = Binary.FastRead<MyModel>(pk.GetStream());Assert.Equal(model.Code, model2.Code);Assert.Equal(model.Name, model2.Name);var ms = new MemoryStream();Binary.FastWrite(model, ms);Assert.Equal("D2090553746F6E65", ms.ToArray().ToHex()); } private class MyModel {public Int32 Code { get; set; }public String Name { get; set; } }

    序列化帶有一個整型和一個字符串的對象,結果只有8個字節!

    Packet用法可參考

    此處為語雀文檔,點擊鏈接查看:https://www.yuque.com/go/doc/31527106

    標準讀寫

    Binary主要成員

    /// <summary>使用7位編碼整數。默認false不使用</summary> public Boolean EncodeInt { get; set; } /// <summary>小端字節序。默認false大端</summary> public Boolean IsLittleEndian { get; set; } /// <summary>使用指定大小的FieldSizeAttribute特性,默認false</summary> public Boolean UseFieldSize { get; set; } /// <summary>使用對象引用,默認true</summary> public Boolean UseRef { get; set; } = true; /// <summary>大小寬度。可選0/1/2/4,默認0表示壓縮編碼整數</summary> public Int32 SizeWidth { get; set; } /// <summary>要忽略的成員</summary> public ICollection<String> IgnoreMembers { get; set; } /// <summary>處理器列表</summary> public IList<IBinaryHandler> Handlers { get; private set; } /// <summary>數據流。默認實例化一個內存數據流</summary> public virtual Stream Stream { get; set; } /// <summary>主對象</summary> public Stack<Object> Hosts { get; private set; } /// <summary>成員</summary> public MemberInfo Member { get; set; } /// <summary>字符串編碼,默認utf-8</summary> public Encoding Encoding { get; set; } /// <summary>序列化屬性而不是字段。默認true</summary> public Boolean UseProperty { get; set; } // 處理器 public Binary AddHandler(IBinaryHandler handler); public Binary AddHandler<THandler>(Int32 priority = 0); public T GetHandler<T>(); // 寫入 public virtual Boolean Write(Object value, Type type = null); // 讀取 public virtual Object Read(Type type); public T Read<T>(); public virtual Boolean TryRead(Type type, ref Object value);

    Stream 最為重要,代表序列化和反序列化的數據流,默認實例化一個內存流。

    EncodeInt 指定使用壓縮編碼整數,效果非常明顯!

    IsLittleEndian 部分協議使用大端字節序。

    UseFieldSize 部分協議的長度位和數據區并沒有挨在一起,需要借助FieldSizeAttribute特性。例如ZipEntry中有這么一段:

    /// <summary>文件名長度</summary> private readonly UInt16 FileNameLength; /// <summary>擴展數據長度</summary> private readonly UInt16 ExtraFieldLength; // ZipDirEntry成員 /// <summary>注釋長度</summary> private readonly UInt16 CommentLength; // ZipDirEntry成員 /// <summary>分卷號。</summary> public UInt16 DiskNumber; // ZipDirEntry成員 /// <summary>內部文件屬性</summary> public UInt16 InternalFileAttrs; // ZipDirEntry成員 /// <summary>擴展文件屬性</summary> public UInt32 ExternalFileAttrs; // ZipDirEntry成員 /// <summary>文件頭相對位移</summary> public UInt32 RelativeOffsetOfLocalHeader; /// <summary>文件名,如果是目錄,則以/結束</summary> [FieldSize("FileNameLength")] public String FileName; /// <summary>擴展字段</summary> [FieldSize("ExtraFieldLength")] public Byte[] ExtraField; // ZipDirEntry成員 /// <summary>注釋</summary> [FieldSize("CommentLength")] public String Comment;

    IgnoreMembers 指定某些成員不參與序列化,支持動態指定。例如ZipFile的目錄實體和文件實體,需要序列化的字段有所不同。

    Encoding 指定序列化字符串時使用的文本編碼。

    設置好各種參數后,就可以Write/Read來序列化或反序列化對象了。安全起見,建議每個Binary只用一次,重復使用可能有意想不到的后果。

    自定義擴展

    Binary設計時使用Handler處理器架構,Write/Read內部實際上是逐個遍歷Handler,直到找到能夠處理的Handler為止。因此Handler也有優先級,其中基礎數據類型BinaryGeneral處理器優先級最高。

    BinaryGeneral 負責處理數字、布爾、時間日期、字符串等等基礎數據類型。

    BinaryNormal 負責處理字節數組、Guid、Packet等常見類型。

    BinaryList 負責處理數組和列表。

    BinaryDictionary 負責處理字典。

    BinaryComposite 負責處理復雜對象,反射各成員,遞歸序列化。該處理器優先級最低。

    來看看怎么樣自定義一個處理器,以顏色處理器為例:

    /// <summary>顏色處理器。</summary> public class BinaryColor : BinaryHandlerBase {/// <summary>實例化</summary>public BinaryColor(){Priority = 0x50;}/// <summary>寫入對象</summary>/// <param name="value">目標對象</param>/// <param name="type">類型</param>/// <returns></returns>public override Boolean Write(Object value, Type type){if (type != typeof(Color)) return false;var color = (Color)value;WriteLog("WriteColor {0}", color);Host.Write(color.A);Host.Write(color.R);Host.Write(color.G);Host.Write(color.B);return true;}/// <summary>嘗試讀取指定類型對象</summary>/// <param name="type"></param>/// <param name="value"></param>/// <returns></returns>public override Boolean TryRead(Type type, ref Object value){if (type != typeof(Color)) return false;var a = Host.ReadByte();var r = Host.ReadByte();var g = Host.ReadByte();var b = Host.ReadByte();var color = Color.FromArgb(a, r, g, b);WriteLog("ReadColor {0}", color);value = color;return true;} }

    最后只需要掛載到Binary上即可序列化和反序列化帶有Color類型的成員

    var bn = new Binary(); bn.AddHandler<BinaryColor>();

    總結

    .NET內部自帶二進制序列化BinaryFormatter,它會帶上大量額外信息,導致體積很大,基本上很少用到。

    Binary設計的初衷是序列化各種文件格式和通信協議,因此并沒有過多考慮作為RPC通信格式。實際上NewLife組件自己的RPC框架ApiServer并沒有使用Binary,而是選擇了兼容性比較好的Json。

    在中通的100億Redis大數據中,盡管是二進制kv數據,同樣沒有用到Binary。因為它需要對字節數據進行極致控制,并且需要做多版本兼容。因此它實際上是直接讀寫二進制數據流,然后借用了Binary的一些輔助方法。

    總結

    以上是生活随笔為你收集整理的二进制序列化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。