一個TCP長鏈接設備管理後臺工程(四)

前篇

一個TCP長鏈接設備管理後臺工程(一)
一個TCP長鏈接設備管理後臺工程(二)
一個TCP長鏈接設備管理後臺工程(三)git

Github倉庫地址github

協議解析

從前面內容咱們能夠發現,808協議是一個很典型的協議格式:json

固定字段+變長字段

其中固定字段用來檢測一個幀格式的完整性和有效性,因此通常會包含一下內容:幀頭+變長字段對應的長度+校驗。因爲這一段的數據格式固定,目的單一,因此處理起來比較簡單。segmentfault

變長字段的長度是由固定字段終端某一個子字段的值決定的,並且這部分的格式比較多變,須要靈活處理。這一字段咱們一般稱爲Body或者Apdu。數組

咱們首先說明變長字段的處理流程。bash

Body處理

正由於Body字段格式靈活,因此爲了提升代碼的複用性和拓展性,咱們須要對Body的處理機制進行抽象,提取出一個相對通用的接口出來。app

有經驗的工程師都知道,一個協議格式處理,無非就是編碼和解碼。編碼咱們稱之爲Marshal,解碼咱們稱之爲Unmarshal。對於不一樣的格式,咱們只須要提供不一樣的Marshal和Unmarshal實現便可。函數

從前面分析能夠知道,咱們如今面對的一種格式是相似於Plain的格式,這種格式沒有基本的分割符,下面咱們就對這種編碼來實現Marshal和Unmarshal。咱們將這部分邏輯定義爲一個codec包測試

package codec

func Unmarshal(data []byte, v interface{}) (int, error){}
func Marshal(v interface{}) ([]byte, error){}

參考官方庫解析json的流程,很快咱們就想到了用反射來實現這兩個功能。ui

首先咱們來分析Unmarshal,咱們須要按照v的類型,將data數據按照對應的長度和類型賦值。舉個最簡單的例子:

func TestSimple(t *testing.T) {
    type Body struct {
        Age1 int8
        Age2 int16
    }

    data := []byte{0x01, 0x02, 0x03}
    pack := Body{}
    i, err := Unmarshal(data, &pack)
    if err != nil {
        t.Errorf("err:%s", err.Error())
    }

    t.Log("len:", i)
    t.Log("pack:", pack)
}
$ go test -v server/codec -run TestSimple
=== RUN   TestSimple
--- PASS: TestSimple (0.00s)
    codec_test.go:20: len: 3
    codec_test.go:21: pack: {1 515}
PASS
ok      server/codec    0.002s

對於Body結構體,第一個字段是int8,佔用一個字節,因此分配的值是0x01。第二個字段是int16,佔用兩個字節,分配的值是0x02,0x03,而後把這兩個字節按照大端格式組合成一個int16就好了。因此結果就是Age1字段爲1(0x01),Age2字段爲515(0x0203)

因此處理的關鍵是,咱們要識別出v interface{}的類型,而後計算該類型對應的大小,再將data中對應大小的數據段組合成對應類型值複製給v中的對應字段。

v interface{}的類型多變,可能會涉及到結構體嵌套等,因此會存在遞歸處理,固然第一步咱們須要獲取到v的類型:

rv := reflect.ValueOf(v)
switch rv.Kind() {
    case reflect.Int8:
        //
    case reflect.Uint8:
        //
    case reflect.Int16:
        //
    case reflect.Uint16:
        //
    case reflect.Int32:
        //
    case reflect.Uint32:
        //
    case reflect.Int64:
        //
    case reflect.Uint64:
        //
    case reflect.Float32:
        //
    case reflect.Float64:
        //
    case reflect.String:
        //
    case reflect.Slice:
        //
    case reflect.Struct:
        //須要對struct中的每一個元素進行解析
}

其餘的類型都比較好處理,須要說明的是struct類型,首先咱們要可以遍歷struct中的各個元素,因而咱們找到了:

fieldCount := v.NumField()
v.Field(i)

NumField()可以獲取結構體內部元素個數,而後Field(i)經過指定index就能夠獲取到指定的元素了。獲取到了元素後,咱們就須要最這個元素進行再次的Unmarshal,也就是遞歸。可是此時咱們經過v.Field(i)獲取到的是reflect.Value類型,而不是interface{}類型了,因此遞歸的入參咱們使用reflect.Value。另外還須要考慮的一個問題是data數據的索引問題,一次調用Unmarshal就會消耗掉必定字節的data數據,消耗的長度應該可以被獲取到,以方便下一次調用Unmarshal時,可以對入參的data數據索引作正確的設定。所以,Unmarshal函數須要返回一個當前當用後所佔用的字節長度。好比int8就是一個字節,struct就是各個字段字節之和。

func Unmarshal(data []byte, v interface{})  (int,error) {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Ptr || rv.IsNil() {
        return 0,fmt.Errorf("error")
    }

    return refUnmarshal(data, reflect.ValueOf(v))
}

func refUnmarshal(data []byte, v reflect.Value)  (int,error) {
    var usedLen int = 0
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        usedLen = usedLen + 1
    case reflect.Uint8:
        usedLen = usedLen + 1
    case reflect.Int16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        usedLen = usedLen + 2
    case reflect.Uint16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        usedLen = usedLen + 2
    case reflect.Int32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        usedLen = usedLen + 4
    case reflect.Uint32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        usedLen = usedLen + 4
    case reflect.Int64:
        usedLen = usedLen + 8
    case reflect.Uint64:
        usedLen = usedLen + 8
    case reflect.Float32:
        usedLen = usedLen + 4
    case reflect.Float64:
        usedLen = usedLen + 8
    case reflect.String:
        //待處理
    case reflect.Slice:
        //待處理
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            l, err := refUnmarshal(data[usedLen:], v.Field(i), v.Type().Field(i), streLen)
            if err != nil {
                return 0, err
            }

            usedLen = usedLen + l
        }
    }
    return usedLen, nil
}

解析到這個地方咱們發現,咱們又遇到了另外的一個問題:咱們沒有辦法單純的經過類型來獲取到string和struct的長度,並且咱們還必須處理這兩個類型,由於這兩個類型在協議處理中是很常見的。既然單純的經過類型沒法判斷長度,咱們就要藉助tag了。咱們嘗試着在string和slice上設定tag來解決這個問題。可是tag是屬於結構體的,只有結構體內部元素才能擁有tag,並且咱們不能經過元素自己獲取tag,必須經過上層的struct的type才能獲取到,因此此時咱們入參還要加入一個經過結構體type獲取到的對應字段reflect.StructField:

func refUnmarshal(data []byte, v reflect.Value, tag reflect.StructField) (int, error) {
    var usedLen int = 0
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        usedLen = usedLen + 1
    case reflect.Uint8:
        usedLen = usedLen + 1
    case reflect.Int16:
        usedLen = usedLen + 2
    case reflect.Uint16:
        usedLen = usedLen + 2
    case reflect.Int32:
        usedLen = usedLen + 4
    case reflect.Uint32:
        usedLen = usedLen + 4
    case reflect.Int64:
        usedLen = usedLen + 8
    case reflect.Uint64:
        usedLen = usedLen + 8
    case reflect.Float32:
        usedLen = usedLen + 4
    case reflect.Float64:
        usedLen = usedLen + 8
    case reflect.String:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            //
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }
        usedLen = usedLen + int(lens)
    case reflect.Slice:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            //
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }

        usedLen = usedLen + int(lens)
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            l, err := refUnmarshal(data[usedLen:], v.Field(i), v.Type().Field(i))
            if err != nil {
                return 0, err
            }

            usedLen = usedLen + l
        }
    }
    return usedLen, nil
}

這樣咱們就能過獲取到全部的字段對應的長度了,這個很關鍵。而後咱們只須要根據對應的長度,從data中填充對應的數據值便可

func refUnmarshal(data []byte, v reflect.Value, tag reflect.StructField) (int, error) {
    var usedLen int = 0
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        v.SetInt(int64(data[0]))
        usedLen = usedLen + 1
    case reflect.Uint8:
        v.SetUint(uint64(data[0]))
        usedLen = usedLen + 1
    case reflect.Int16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetInt(int64(Bytes2Word(data)))
        usedLen = usedLen + 2
    case reflect.Uint16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetUint(uint64(Bytes2Word(data)))
        usedLen = usedLen + 2
    case reflect.Int32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetInt(int64(Bytes2DWord(data)))
        usedLen = usedLen + 4
    case reflect.Uint32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetUint(uint64(Bytes2DWord(data)))
        usedLen = usedLen + 4
    case reflect.Int64:
        v.SetInt(64)
        usedLen = usedLen + 8
    case reflect.Uint64:
        v.SetUint(64)
        usedLen = usedLen + 8
    case reflect.Float32:
        v.SetFloat(32.23)
        usedLen = usedLen + 4
    case reflect.Float64:
        v.SetFloat(64.46)
        usedLen = usedLen + 8
    case reflect.String:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            //
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }

        if len(data) < int(lens) {
            return 0, fmt.Errorf("data to short")
        }

        v.SetString(string(data[:lens]))
        usedLen = usedLen + int(lens)

    case reflect.Slice:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            //
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }

        v.SetBytes(data[:lens])
        usedLen = usedLen + int(lens)
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            l, err := refUnmarshal(data[usedLen:], v.Field(i), v.Type().Field(i))
            if err != nil {
                return 0, err
            }

            usedLen = usedLen + l
        }
    }
    return usedLen, nil
}

一個基本的Unmarshal函數就完成了。可是這個處理是比較理想的,在實際中可能會存在這樣的一種狀況:在一個協議中有若干字段,其餘的字段都是固定長度,只有一個字段是長度可變的,而這個可變長度的計算是由整體長度-固定長度來計算出來的。在這種狀況下,咱們須要提早計算出已知字段的固定長度,而後用data長度-固定長度,獲得惟一的可變字段的長度。因此我如今要有一個獲取這個結構的有效長度的函數。前面的Unmarshal內部已經能夠獲取到每一個字段的長度了,咱們只須要把這個函數簡單改造一下就好了:

func RequireLen(v interface{}) (int, error) {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Ptr || rv.IsNil() {
        return 0, fmt.Errorf("error")
    }

    return refRequireLen(reflect.ValueOf(v), reflect.StructField{})
}

func refRequireLen(v reflect.Value, tag reflect.StructField) (int, error) {
    var usedLen int = 0
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        usedLen = usedLen + 1
    case reflect.Uint8:
        usedLen = usedLen + 1
    case reflect.Int16:
        usedLen = usedLen + 2
    case reflect.Uint16:
        usedLen = usedLen + 2
    case reflect.Int32:
        usedLen = usedLen + 4
    case reflect.Uint32:
        usedLen = usedLen + 4
    case reflect.Int64:
        usedLen = usedLen + 8
    case reflect.Uint64:
        usedLen = usedLen + 8
    case reflect.Float32:
        usedLen = usedLen + 4
    case reflect.Float64:
        usedLen = usedLen + 8
    case reflect.String:
        strLen := tag.Tag.Get("len")
        if strLen == "" {
            return 0, nil
        }
        lens, err := strconv.ParseInt(strLen, 10, 0)
        if err != nil {
            return 0, err
        }

        usedLen = usedLen + int(lens)
    case reflect.Slice:
        strLen := tag.Tag.Get("len")
        if strLen == "" {
            return 0, nil
        }
        lens, err := strconv.ParseInt(strLen, 10, 0)
        if err != nil {
            return 0, err
        }

        usedLen = usedLen + int(lens)
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            l, err := refRequireLen(v.Field(i), v.Type().Field(i))
            if err != nil {
                return 0, err
            }

            usedLen = usedLen + l
        }
    }
    return usedLen, nil
}

這樣咱們就能夠實現一個完整的Unmarshal

func Unmarshal(data []byte, v interface{}) (int, error) {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Ptr || rv.IsNil() {
        return 0, fmt.Errorf("error")
    }

    lens, err := RequireLen(v)
    if err != nil {
        return 0, err
    }

    if len(data) < lens {
        return 0, fmt.Errorf("data too short")
    }

    return refUnmarshal(data, reflect.ValueOf(v), reflect.StructField{}, len(data)-lens)
}

func refUnmarshal(data []byte, v reflect.Value, tag reflect.StructField, streLen int) (int, error) {
    var usedLen int = 0
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        v.SetInt(int64(data[0]))
        usedLen = usedLen + 1
    case reflect.Uint8:
        v.SetUint(uint64(data[0]))
        usedLen = usedLen + 1
    case reflect.Int16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetInt(int64(Bytes2Word(data)))
        usedLen = usedLen + 2
    case reflect.Uint16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetUint(uint64(Bytes2Word(data)))
        usedLen = usedLen + 2
    case reflect.Int32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetInt(int64(Bytes2DWord(data)))
        usedLen = usedLen + 4
    case reflect.Uint32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetUint(uint64(Bytes2DWord(data)))
        usedLen = usedLen + 4
    case reflect.Int64:
        v.SetInt(64)
        usedLen = usedLen + 8
    case reflect.Uint64:
        v.SetUint(64)
        usedLen = usedLen + 8
    case reflect.Float32:
        v.SetFloat(32.23)
        usedLen = usedLen + 4
    case reflect.Float64:
        v.SetFloat(64.46)
        usedLen = usedLen + 8
    case reflect.String:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            lens = streLen
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }

        if len(data) < int(lens) {
            return 0, fmt.Errorf("data to short")
        }

        v.SetString(string(data[:lens]))
        usedLen = usedLen + int(lens)

    case reflect.Slice:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            lens = streLen
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }

        v.SetBytes(data[:lens])
        usedLen = usedLen + int(lens)
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            l, err := refUnmarshal(data[usedLen:], v.Field(i), v.Type().Field(i), streLen)
            if err != nil {
                return 0, err
            }

            usedLen = usedLen + l
        }
    }
    return usedLen, nil
}

理解了上面的流程,Marshal就就很好寫了,只是複製過程反過來就好了。這其中還有一些小的轉換邏輯將字節數組轉換成多字節整形:Bytes2Word、Word2Bytes、Bytes2DWord、Dword2Bytes。這類轉換都使用大端格式處理。完整代碼以下:

package codec

import (
    "fmt"
    "reflect"
    "strconv"
)

func RequireLen(v interface{}) (int, error) {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Ptr || rv.IsNil() {
        return 0, fmt.Errorf("error")
    }

    return refRequireLen(reflect.ValueOf(v), reflect.StructField{})
}

func Unmarshal(data []byte, v interface{}) (int, error) {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Ptr || rv.IsNil() {
        return 0, fmt.Errorf("error")
    }

    lens, err := RequireLen(v)
    if err != nil {
        return 0, err
    }

    if len(data) < lens {
        return 0, fmt.Errorf("data too short")
    }

    return refUnmarshal(data, reflect.ValueOf(v), reflect.StructField{}, len(data)-lens)
}

func Marshal(v interface{}) ([]byte, error) {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Ptr || rv.IsNil() {
        return []byte{}, fmt.Errorf("error")
    }

    return refMarshal(reflect.ValueOf(v), reflect.StructField{})
}

func refRequireLen(v reflect.Value, tag reflect.StructField) (int, error) {
    var usedLen int = 0
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        usedLen = usedLen + 1
    case reflect.Uint8:
        usedLen = usedLen + 1
    case reflect.Int16:
        usedLen = usedLen + 2
    case reflect.Uint16:
        usedLen = usedLen + 2
    case reflect.Int32:
        usedLen = usedLen + 4
    case reflect.Uint32:
        usedLen = usedLen + 4
    case reflect.Int64:
        usedLen = usedLen + 8
    case reflect.Uint64:
        usedLen = usedLen + 8
    case reflect.Float32:
        usedLen = usedLen + 4
    case reflect.Float64:
        usedLen = usedLen + 8
    case reflect.String:
        strLen := tag.Tag.Get("len")
        if strLen == "" {
            return 0, nil
        }
        lens, err := strconv.ParseInt(strLen, 10, 0)
        if err != nil {
            return 0, err
        }

        usedLen = usedLen + int(lens)
    case reflect.Slice:
        strLen := tag.Tag.Get("len")
        if strLen == "" {
            return 0, nil
        }
        lens, err := strconv.ParseInt(strLen, 10, 0)
        if err != nil {
            return 0, err
        }

        usedLen = usedLen + int(lens)
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            l, err := refRequireLen(v.Field(i), v.Type().Field(i))
            if err != nil {
                return 0, err
            }

            usedLen = usedLen + l
        }
    }
    return usedLen, nil
}

func refUnmarshal(data []byte, v reflect.Value, tag reflect.StructField, streLen int) (int, error) {
    var usedLen int = 0
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        v.SetInt(int64(data[0]))
        usedLen = usedLen + 1
    case reflect.Uint8:
        v.SetUint(uint64(data[0]))
        usedLen = usedLen + 1
    case reflect.Int16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetInt(int64(Bytes2Word(data)))
        usedLen = usedLen + 2
    case reflect.Uint16:
        if len(data) < 2 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetUint(uint64(Bytes2Word(data)))
        usedLen = usedLen + 2
    case reflect.Int32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetInt(int64(Bytes2DWord(data)))
        usedLen = usedLen + 4
    case reflect.Uint32:
        if len(data) < 4 {
            return 0, fmt.Errorf("data to short")
        }
        v.SetUint(uint64(Bytes2DWord(data)))
        usedLen = usedLen + 4
    case reflect.Int64:
        v.SetInt(64)
        usedLen = usedLen + 8
    case reflect.Uint64:
        v.SetUint(64)
        usedLen = usedLen + 8
    case reflect.Float32:
        v.SetFloat(32.23)
        usedLen = usedLen + 4
    case reflect.Float64:
        v.SetFloat(64.46)
        usedLen = usedLen + 8
    case reflect.String:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            lens = streLen
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }

        if len(data) < int(lens) {
            return 0, fmt.Errorf("data to short")
        }

        v.SetString(string(data[:lens]))
        usedLen = usedLen + int(lens)

    case reflect.Slice:
        strLen := tag.Tag.Get("len")
        var lens int = 0
        if strLen == "" {
            lens = streLen
        } else {
            lens64, err := strconv.ParseInt(strLen, 10, 0)
            if err != nil {
                return 0, err
            }

            lens = int(lens64)
        }

        v.SetBytes(data[:lens])
        usedLen = usedLen + int(lens)
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            l, err := refUnmarshal(data[usedLen:], v.Field(i), v.Type().Field(i), streLen)
            if err != nil {
                return 0, err
            }

            usedLen = usedLen + l
        }
    }
    return usedLen, nil
}

func refMarshal(v reflect.Value, tag reflect.StructField) ([]byte, error) {
    data := make([]byte, 0)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    switch v.Kind() {
    case reflect.Int8:
        data = append(data, byte(v.Int()))
    case reflect.Uint8:
        data = append(data, byte(v.Uint()))
    case reflect.Int16:
        temp := Word2Bytes(uint16(v.Int()))
        data = append(data, temp...)
    case reflect.Uint16:
        temp := Word2Bytes(uint16(v.Uint()))
        data = append(data, temp...)
    case reflect.Int32:
        temp := Dword2Bytes(uint32(v.Int()))
        data = append(data, temp...)
    case reflect.Uint32:
        temp := Dword2Bytes(uint32(v.Uint()))
        data = append(data, temp...)
    case reflect.String:
        strLen := tag.Tag.Get("len")
        lens, err := strconv.ParseInt(strLen, 10, 0)
        if err != nil {
            return []byte{}, err
        }

        if int(lens) > v.Len() {
            zeroSlice := make([]byte, int(lens)-v.Len())
            data = append(data, zeroSlice...)
        }
        data = append(data, v.String()...)
    case reflect.Slice:
        strLen := tag.Tag.Get("len")
        lens, err := strconv.ParseInt(strLen, 10, 0)
        if err != nil {
            return []byte{}, err
        }

        if int(lens) > v.Len() {
            zeroSlice := make([]byte, int(lens)-v.Len())
            data = append(data, zeroSlice...)
        }
        data = append(data, v.Bytes()...)
    case reflect.Struct:
        fieldCount := v.NumField()

        for i := 0; i < fieldCount; i++ {
            d, err := refMarshal(v.Field(i), v.Type().Field(i))
            if err != nil {
                return []byte{}, err
            }

            data = append(data, d...)
        }
    }
    return data, nil
}

func Bytes2Word(data []byte) uint16 {
    if len(data) < 2 {
        return 0
    }
    return (uint16(data[0]) << 8) + uint16(data[1])
}

func Word2Bytes(data uint16) []byte {
    buff := make([]byte, 2)
    buff[0] = byte(data >> 8)
    buff[1] = byte(data)
    return buff
}

func Bytes2DWord(data []byte) uint32 {
    if len(data) < 4 {
        return 0
    }
    return (uint32(data[0]) << 24) + (uint32(data[1]) << 16) + (uint32(data[2]) << 8) + uint32(data[3])
}

func Dword2Bytes(data uint32) []byte {
    buff := make([]byte, 4)
    buff[0] = byte(data >> 24)
    buff[1] = byte(data >> 16)
    buff[2] = byte(data >> 8)
    buff[3] = byte(data)
    return buff
}

測試程序codec_test.go

package codec

import (
    "testing"
)

func TestUnmarshal(t *testing.T) {
    type Data struct {
        Size    int8
        Size2   uint16
        Size3   uint32
        Name    string `len:"5"`
        Message string
        Sec     []byte `len:"3"`
    }

    type Body struct {
        Age1   int8
        Age2   int16
        Length int32
        Data1  Data
    }

    data := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0x31, 0x32, 0x33, 0x34, 0x35, 0x31, 0x30, 0x03, 0x02, 0x01}
    pack := Body{}
    i, err := Unmarshal(data, &pack)
    if err != nil {
        t.Errorf("err:%s", err.Error())
    }

    t.Log("len:", i)
    t.Log("pack:", pack)
}

func TestMarshal(t *testing.T) {
    type Data struct {
        Size    int8
        Size2   uint16
        Size3   uint32
        Name    string `len:"5"`
        Message string
        Sec     []byte `len:"3"`
    }

    type Body struct {
        Age1   int8
        Age2   int16
        Length int32
        Data1  Data
    }

    pack := Body{
        Age1:   13,
        Age2:   1201,
        Length: 81321,
        Data1: Data{
            Size:    110,
            Size2:   39210,
            Size3:   85632,
            Name:    "ASDFG",
            Message: "ZXCVBN",
            Sec:     []byte{0x01, 0x02, 0x03},
        },
    }
    data, err := Marshal(&pack)
    if err != nil {
        t.Errorf("err:%s", err.Error())
    }

    t.Log("data:", data)
    t.Log("pack:", pack)
}

測試結果:

$ go test -v server/codec -run TestMarshal
=== RUN   TestMarshal
--- PASS: TestMarshal (0.00s)
    codec_test.go:70: data: [13 4 177 0 1 61 169 110 153 42 0 1 78 128 65 83 68 70 71 90 88 67 86 66 78 1 2 3]
    codec_test.go:71: pack: {13 1201 81321 {110 39210 85632 ASDFG ZXCVBN [1 2 3]}}
PASS
ok      server/codec    0.001s

# xml @ xia in ~/work/code/code_go/cmdtest [10:21:47] 
$ go test -v server/codec -run TestUnmarshal
=== RUN   TestUnmarshal
--- PASS: TestUnmarshal (0.00s)
    codec_test.go:31: len: 24
    codec_test.go:32: pack: {1 515 67438087 {-95 41635 2762319527 12345 10 [3 2 1]}}
PASS
ok      server/codec    0.002s
相關文章
相關標籤/搜索