使用golang实现tcp自定义协议解码器 LengthFieldBasedFrameDecoder

358 阅读1分钟

本文使用golang 实现一个基于长度字段的解码器 LengthFieldBasedFrameDecoder

LengthFieldBasedFrameDecoder

type LengthFieldBasedFrameDecoder struct {
    byteOrder           binary.ByteOrder
    maxFrameLength      int
    lengthFieldOffset   int
    lengthFieldLength   int
    lengthAdjustment    int
    initialBytesToStrip int
}

Decode 方法

func (l *LengthFieldBasedFrameDecoder) Decode(reader *bufio.Reader) (io.Reader, error) {
    // lengthFieldEndOffset
    lengthFieldEndOffset := l.lengthFieldOffset + l.lengthFieldLength
    // read header buffer
    headerBuffer := make([]byte, lengthFieldEndOffset)
    //reader head from reader
    n, err := io.ReadFull(reader, headerBuffer)
    if n != len(headerBuffer) || err != nil {
       panic(fmt.Errorf("reade message lengthFieldEndOffset faield %v", err))
    }
    //lengthFiledBuff
    lengthFieldBuff := headerBuffer[l.lengthFieldOffset:lengthFieldEndOffset]
    //frame length
    frameLength := unpackFieldLength(l.byteOrder, l.lengthFieldLength, lengthFieldBuff)
    //adjust
    frameLength += int64(l.lengthAdjustment + lengthFieldEndOffset)
    //frameReader
    frameReader := io.MultiReader(
       // lengthFieldOffset + lengthFieldLength
       bytes.NewReader(headerBuffer),
       // frameLength - len(headerBuffer)
       io.LimitReader(reader, frameLength-int64(lengthFieldEndOffset)),
    )
    // strip bytes
    if l.initialBytesToStrip > 0 {
       n, err := io.CopyN(io.Discard, frameReader, int64(l.initialBytesToStrip))
       if n != int64(l.initialBytesToStrip) || err != nil {
          panic(fmt.Errorf("initialBytesToStrip: %d -> %d, %w", l.initialBytesToStrip, n, err))
       }
    }
    //转成 io.Reader
    return frameReader, nil
}

unpackFieldLength 解析长度字段

func unpackFieldLength(byteOrder binary.ByteOrder, fieldLen int, buff []byte) (frameLength int64) {
    switch fieldLen {
    case 1:
       frameLength = int64(buff[0])
    case 2:
       frameLength = int64(byteOrder.Uint16(buff))
    case 4:
       frameLength = int64(byteOrder.Uint32(buff))
    case 8:
       frameLength = int64(byteOrder.Uint64(buff))
    default:
       panic(fmt.Errorf("should not reach here"))
    }
    return
}

说明

主要参考netty的 LengthFieldBasedFrameDecoder实现