- 使用 Go 实现的 MQTT 3.1.1 代理核心(broker library),可作为库集成,也附带独立服务器程序。
- 支持 TCP 与 WebSocket 两种监听方式,可同时配置多个监听器。
- 内置 QoS 0/1/2 消息处理、遗嘱消息(Will)、保留消息(Retained)、主题通配符(
+、#)。 - 提供默认的 内存持久化(
MemoryPersistence),可通过Persistence接口扩展到 Redis / LevelDB 等外部存储。 - 内部
BrokerStats记录连接数、吞吐、丢弃等指标,便于对接监控系统。
本仓库是对上游 alsm/hrotti 的增强与安全加固分支,重点补齐了原项目在报文解析、并发与资源控制上的安全隐患。
环境要求:本项目在
go.mod中声明toolchain go1.25.13(已修复标准库 CVE)。Go 1.21+ 会自动下载并使用该工具链;如显式指定,可用GOTOOLCHAIN=go1.25.13 go build。
go mod tidy
go build ./...
# 以独立服务器方式启动(默认监听 tcp://0.0.0.0:1883,可用 HROTTI_URL 覆盖)
go run .
# 或使用配置文件启动(多监听 / WebSocket)
go run . -conf config.json以 MqttBrokerCore 作为依赖库,创建一个 MQTT 服务器:
package main
import (
"os"
"os/signal"
"syscall"
hrotti "github.com/FasterEdge/MqttBrokerCore/broker"
)
func main() {
h := hrotti.NewHrotti(100, &hrotti.MemoryPersistence{})
hrotti.INFO = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime)
h.AddListener("test", hrotti.NewListenerConfig("tcp://0.0.0.0:1883"))
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
h.Stop()
}| 参数 | 默认值 | 说明 |
|---|---|---|
-conf |
空 | JSON 配置文件路径;提供后按配置启动多个 TCP / WebSocket 监听器 |
HROTTI_URL |
tcp://0.0.0.0:1883 |
未提供 -conf 时的单监听地址,格式为 tcp://… 或 ws://… |
{
"maxQueueDepth": 100,
"listeners": {
"tcp": { "url": "tcp://0.0.0.0:1883" },
"ws": { "url": "ws://0.0.0.0:2000/mqtt" }
},
"logging": {
"info": "stdout",
"protocol": "discard",
"error": "stderr",
"debug": "discard"
}
}maxQueueDepth:每个客户端的待发送消息队列大小(默认 100)。listeners:键名任意,url仅支持tcp://或ws://(其它 scheme 会被拒绝)。logging:将info/protocol/error/debug分别定向到stdout、stderr或discard。
| 场景 | 示例 | 说明 |
|---|---|---|
| 单节点本地测试 | go run . |
默认监听 0.0.0.0:1883(可通过 HROTTI_URL 覆盖)。 |
| 多监听 + WebSocket | go run . -conf config.json |
同时开 TCP 与 WS 监听,共享同一主题树。 |
| 持久化会话 | CleanSession=false 且使用相同 ClientIdentifier |
断线重连后保留未确认的 QoS 1/2 消息与订阅。 |
Hrotti:broker 实例,持有客户端表、订阅树、持久化器与统计对象。- 位图订阅存储:
subscriptionMap.subBitmap实现 O(1) 主题匹配,支持+/#通配符。 - 消息 ID 池:
messageIDs分配 1–65534,耗尽时返回ErrMsgIDsExhausted,避免写入非法MessageID=0。 - 持久化接口:
Persistence(Init/Open/Add/GetAll/Delete/Close/Exists/Replace)。
| 问题 | 修复 |
|---|---|
decodeLength 无限读取字节导致协议失效 / OOM |
限制最多 4 字节并返回错误 |
ReadPacket 未限制 RemainingLength,可分配大内存 |
新增 MaxRemainingLength(默认 64 MiB)上限 |
| 报文体短读未检测,导致协议同步失效 | 引入 decodeReader 捕获 truncation 错误 |
WillFlag=0 时未检查 WillQoS / WillRetain |
在 ConnectPacket.Validate 中拒绝不一致包 |
| 慢/不再读取的客户端导致发送死锁 | 所有发送改为非阻塞 select…default 模式 |
NewListenerConfig 在 URL 解析失败时返回 nil |
新增 NewListenerConfigWithError 并校验 scheme |
getClient 返回 nil 导致空指针 panic |
在 FindRetained / DeliverMessage 中加空值保护 |
空 ClientIdentifier 且 CleanSession=0 未拒绝 |
按 MQTT 规范返回 CONN_REF_ID_REJ |
感谢上游项目 alsm/hrotti 提供的原始实现与设计思路,本项目在其基础上进行兼容性增强与安全加固。
