// frame.go 帧协议,wire format 与 zonat 桥接帧完全一致 // (ver(1) + cmd(1) + length(2, LE) + sid(4, LE) + data)。 // 本文件取自 zonat client/internal/frame 的本地修复副本,仅裁掉未用方法。 package agent import ( "encoding/binary" "encoding/json" "errors" "fmt" "io" "net" ) const FrameVersion byte = 1 // 帧头部 const ( sizeOfVer = 1 sizeOfCmd = 1 sizeOfLength = 2 sizeOfSid = 4 headerSize = sizeOfVer + sizeOfCmd + sizeOfSid + sizeOfLength ) const ( frameBufSize = 0x10000 ) type Frame struct { ver byte cmd byte sid uint32 data []byte } func NewFrame(version byte, cmd byte, sid uint32) Frame { return Frame{ver: version, cmd: cmd, sid: sid} } // ReadFrame 读取一帧。头部与数据均用 ReadFull,防止单次 Read 短读导致解析错乱, // 也不会把后续帧的数据读进缓冲丢弃。 func ReadFrame(conn net.Conn) (Frame, error) { f := NewFrame(0, 0, 0) hdr := RawHeader{} if _, err := io.ReadFull(conn, hdr[:]); err != nil { return f, err } f.ver = hdr.Version() f.cmd = hdr.Cmd() f.sid = hdr.StreamID() length := int(hdr.Length()) if length == 0 { return f, nil } data := make([]byte, length) if _, err := io.ReadFull(conn, data); err != nil { return f, err } f.data = data return f, nil } func WriteFrame(conn net.Conn, frame Frame) error { hdr := RawHeader{} hdr[0] = frame.ver hdr[1] = frame.cmd binary.LittleEndian.PutUint16(hdr[2:], uint16(len(frame.data))) binary.LittleEndian.PutUint32(hdr[4:], frame.sid) buf := hdr.Bytes() buf = append(buf, frame.data...) _, err := conn.Write(buf) return err } func (f *Frame) Version() byte { return f.ver } func (f *Frame) Cmd() byte { return f.cmd } func (f *Frame) Length() uint16 { return uint16(len(f.data)) } func (f *Frame) Data() []byte { return f.data } func (f *Frame) StreamID() uint32 { return f.sid } func (f *Frame) SetData(data []byte) error { if len(data) > frameBufSize-1 { return fmt.Errorf("超出最大长度:%d", frameBufSize-1) } f.data = data return nil } func (f *Frame) Marshal(v any) error { buf, err := json.Marshal(v) if err != nil { return err } return f.SetData(buf) } func (f *Frame) Unmarshal(v any) error { if len(f.data) == 0 { return errors.New("没有数据") } return json.Unmarshal(f.data, v) } func (f *Frame) String() string { return fmt.Sprintf("Version:%d Cmd:%d StreamID:%d Length:%d", f.Version(), f.Cmd(), f.StreamID(), f.Length()) } type RawHeader [headerSize]byte func (h RawHeader) Version() byte { return h[0] } func (h RawHeader) Cmd() byte { return h[1] } func (h RawHeader) Length() uint16 { return binary.LittleEndian.Uint16(h[2:]) } func (h RawHeader) StreamID() uint32 { return binary.LittleEndian.Uint32(h[4:]) } func (h RawHeader) Bytes() []byte { buf := make([]byte, headerSize) copy(buf, h[:]) return buf }