package load import ( "bytes" "fmt" "io" "github.com/mochi-mqtt/server/v2/packets" ) const ( mqttProtocolLevel5 = 5 mqttKeepaliveSec = 30 ) // encodeConnect 按 DEVELOPMENT §5 生成 MQTT 5 CONNECT: // ClientID=Username=端编号、CleanStart、心跳 30、会话过期间隔缺省为 0(Clean Start 时即为 0)。 // 不声明 Receive Maximum,避免踩 mochi 发送配额路径。 func encodeConnect(endpointID, password string) ([]byte, error) { pk := packets.Packet{ FixedHeader: packets.FixedHeader{Type: packets.Connect}, ProtocolVersion: mqttProtocolLevel5, Connect: packets.ConnectParams{ ProtocolName: []byte("MQTT"), Clean: true, ClientIdentifier: endpointID, Keepalive: mqttKeepaliveSec, UsernameFlag: true, Username: []byte(endpointID), PasswordFlag: true, Password: []byte(password), }, } var buf bytes.Buffer if err := pk.ConnectEncode(&buf); err != nil { return nil, err } return buf.Bytes(), nil } func encodeSubscribe(packetID uint16, endpointID string) ([]byte, error) { pk := packets.Packet{ FixedHeader: packets.FixedHeader{Type: packets.Subscribe, Qos: 1}, ProtocolVersion: mqttProtocolLevel5, PacketID: packetID, Filters: packets.Subscriptions{ {Filter: downTopic(endpointID), Qos: 1}, }, } var buf bytes.Buffer if err := pk.SubscribeEncode(&buf); err != nil { return nil, err } return buf.Bytes(), nil } func encodePublish(packetID uint16, endpointID string, payload []byte) ([]byte, error) { pk := packets.Packet{ FixedHeader: packets.FixedHeader{Type: packets.Publish, Qos: 1}, ProtocolVersion: mqttProtocolLevel5, TopicName: upTopic(endpointID), PacketID: packetID, Payload: payload, } var buf bytes.Buffer if err := pk.PublishEncode(&buf); err != nil { return nil, err } return buf.Bytes(), nil } func encodePuback(packetID uint16) ([]byte, error) { pk := packets.Packet{ FixedHeader: packets.FixedHeader{Type: packets.Puback}, ProtocolVersion: mqttProtocolLevel5, PacketID: packetID, } var buf bytes.Buffer if err := pk.PubackEncode(&buf); err != nil { return nil, err } return buf.Bytes(), nil } func encodePingreq() []byte { return []byte{0xC0, 0x00} } func downTopic(id string) string { return "nix/c/" + id + "/down" } func upTopic(id string) string { return "nix/c/" + id + "/up" } func connackReason(raw []byte) (byte, error) { if len(raw) < 2 || raw[0]>>4 != packets.Connack { return 0, fmt.Errorf("不是 CONNACK:type=0x%02x len=%d", byteAt(raw, 0), len(raw)) } rem, n, err := decodeRemainingLength(raw[1:]) if err != nil { return 0, err } body := raw[1+n:] if rem < 2 || len(body) < 2 { return 0, fmt.Errorf("CONNACK 过短 remaining=%d body=%d", rem, len(body)) } return body[1], nil } func decodePublish(raw []byte) (payload []byte, packetID uint16, qos byte, err error) { if len(raw) < 2 { return nil, 0, 0, io.ErrUnexpectedEOF } qos = (raw[0] >> 1) & 0x3 rem, n, err := decodeRemainingLength(raw[1:]) if err != nil { return nil, 0, 0, err } body := raw[1+n:] if len(body) != rem { return nil, 0, 0, io.ErrUnexpectedEOF } pk := packets.Packet{ ProtocolVersion: mqttProtocolLevel5, FixedHeader: packets.FixedHeader{ Type: packets.Publish, Remaining: rem, Qos: qos, }, } if err := pk.PublishDecode(body); err != nil { return nil, 0, qos, err } return pk.Payload, pk.PacketID, qos, nil } func decodeRemainingLength(b []byte) (value int, n int, err error) { var mul uint32 = 1 var v uint32 for i := 0; i < len(b) && i < 4; i++ { v += uint32(b[i]&127) * mul n++ if b[i]&128 == 0 { return int(v), n, nil } mul *= 128 } return 0, 0, io.ErrUnexpectedEOF } func byteAt(b []byte, i int) byte { if i < 0 || i >= len(b) { return 0 } return b[i] }