150 lines
3.5 KiB
Go
150 lines
3.5 KiB
Go
|
|
package adapter
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/binary"
|
||
|
|
"errors"
|
||
|
|
"io"
|
||
|
|
)
|
||
|
|
|
||
|
|
// 协议常量.
|
||
|
|
const (
|
||
|
|
// MessageTypeData 表示数据消息.
|
||
|
|
MessageTypeData byte = 0x01
|
||
|
|
// MessageTypeAck 表示 ACK 确认.
|
||
|
|
MessageTypeAck byte = 0x02
|
||
|
|
// MessageTypeNack 表示 NACK 否定确认.
|
||
|
|
MessageTypeNack byte = 0x03
|
||
|
|
|
||
|
|
// 协议限制.
|
||
|
|
maxTopicLength = 65535
|
||
|
|
maxUUIDLength = 255
|
||
|
|
maxPayloadSize = 1 << 30
|
||
|
|
topicLengthSize = 2
|
||
|
|
uuidLengthSize = 1
|
||
|
|
payloadLengthSize = 4
|
||
|
|
)
|
||
|
|
|
||
|
|
// 预定义错误.
|
||
|
|
var (
|
||
|
|
ErrNilMessage = errors.New("message is nil")
|
||
|
|
ErrTopicTooLong = errors.New("topic too long")
|
||
|
|
ErrUUIDTooLong = errors.New("uuid too long")
|
||
|
|
ErrPayloadTooLarge = errors.New("payload too large")
|
||
|
|
)
|
||
|
|
|
||
|
|
// TCPMessage 表示 TCP 传输的消息.
|
||
|
|
type TCPMessage struct {
|
||
|
|
Type byte // 消息类型
|
||
|
|
Topic string // 主题
|
||
|
|
UUID string // 消息 UUID
|
||
|
|
Payload []byte // 消息内容
|
||
|
|
}
|
||
|
|
|
||
|
|
// EncodeTCPMessage 将消息编码为字节数组.
|
||
|
|
// 格式: [消息类型 1字节][Topic长度 2字节][Topic][UUID长度 1字节][UUID][Payload长度 4字节][Payload].
|
||
|
|
func EncodeTCPMessage(msg *TCPMessage) ([]byte, error) {
|
||
|
|
if msg == nil {
|
||
|
|
return nil, ErrNilMessage
|
||
|
|
}
|
||
|
|
|
||
|
|
topicLen := len(msg.Topic)
|
||
|
|
if topicLen > maxTopicLength {
|
||
|
|
return nil, ErrTopicTooLong
|
||
|
|
}
|
||
|
|
|
||
|
|
uuidLen := len(msg.UUID)
|
||
|
|
if uuidLen > maxUUIDLength {
|
||
|
|
return nil, ErrUUIDTooLong
|
||
|
|
}
|
||
|
|
|
||
|
|
payloadLen := len(msg.Payload)
|
||
|
|
if payloadLen > maxPayloadSize {
|
||
|
|
return nil, ErrPayloadTooLarge
|
||
|
|
}
|
||
|
|
|
||
|
|
// 计算总长度
|
||
|
|
totalLen := 1 + topicLengthSize + topicLen + uuidLengthSize + uuidLen + payloadLengthSize + payloadLen
|
||
|
|
buf := make([]byte, totalLen)
|
||
|
|
|
||
|
|
offset := 0
|
||
|
|
|
||
|
|
// 写入消息类型
|
||
|
|
buf[offset] = msg.Type
|
||
|
|
offset++
|
||
|
|
|
||
|
|
// 写入 Topic 长度和内容
|
||
|
|
binary.BigEndian.PutUint16(buf[offset:], uint16(topicLen))
|
||
|
|
offset += topicLengthSize
|
||
|
|
copy(buf[offset:], []byte(msg.Topic))
|
||
|
|
offset += topicLen
|
||
|
|
|
||
|
|
// 写入 UUID 长度和内容
|
||
|
|
buf[offset] = byte(uuidLen)
|
||
|
|
offset++
|
||
|
|
copy(buf[offset:], []byte(msg.UUID))
|
||
|
|
offset += uuidLen
|
||
|
|
|
||
|
|
// 写入 Payload 长度和内容
|
||
|
|
binary.BigEndian.PutUint32(buf[offset:], uint32(payloadLen))
|
||
|
|
offset += payloadLengthSize
|
||
|
|
copy(buf[offset:], msg.Payload)
|
||
|
|
|
||
|
|
return buf, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// DecodeTCPMessage 从字节数组解码消息.
|
||
|
|
func DecodeTCPMessage(reader io.Reader) (*TCPMessage, error) {
|
||
|
|
msg := &TCPMessage{}
|
||
|
|
|
||
|
|
// 读取消息类型
|
||
|
|
msgTypeBuf := make([]byte, 1)
|
||
|
|
if _, err := io.ReadFull(reader, msgTypeBuf); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
msg.Type = msgTypeBuf[0]
|
||
|
|
|
||
|
|
// 读取 Topic 长度
|
||
|
|
topicLenBuf := make([]byte, topicLengthSize)
|
||
|
|
if _, err := io.ReadFull(reader, topicLenBuf); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
topicLen := binary.BigEndian.Uint16(topicLenBuf)
|
||
|
|
|
||
|
|
// 读取 Topic
|
||
|
|
topicBuf := make([]byte, topicLen)
|
||
|
|
if _, err := io.ReadFull(reader, topicBuf); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
msg.Topic = string(topicBuf)
|
||
|
|
|
||
|
|
// 读取 UUID 长度
|
||
|
|
uuidLenBuf := make([]byte, 1)
|
||
|
|
if _, err := io.ReadFull(reader, uuidLenBuf); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
uuidLen := uuidLenBuf[0]
|
||
|
|
|
||
|
|
// 读取 UUID
|
||
|
|
uuidBuf := make([]byte, uuidLen)
|
||
|
|
if _, err := io.ReadFull(reader, uuidBuf); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
msg.UUID = string(uuidBuf)
|
||
|
|
|
||
|
|
// 读取 Payload 长度
|
||
|
|
payloadLenBuf := make([]byte, payloadLengthSize)
|
||
|
|
if _, err := io.ReadFull(reader, payloadLenBuf); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
payloadLen := binary.BigEndian.Uint32(payloadLenBuf)
|
||
|
|
|
||
|
|
// 读取 Payload
|
||
|
|
payloadBuf := make([]byte, payloadLen)
|
||
|
|
if _, err := io.ReadFull(reader, payloadBuf); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
msg.Payload = payloadBuf
|
||
|
|
|
||
|
|
return msg, nil
|
||
|
|
}
|