Mosquitto跨协议消息投递异常:MQTT客户端无法向WebSocket客户端发消息
在Windows系统运行Mosquitto,配置1883端口使用MQTT协议,9001端口使用WebSocket协议。预期MQTT客户端发布的消息能被WebSocket客户端接收,但实际无法实现。
- MQTT客户端之间、WebSocket客户端之间均可正常收发消息,但跨协议消息无法投递
- 使用
-v参数启动Mosquitto后,日志仅记录WebSocket客户端的连接与消息操作,不记录MQTT客户端的相关行为,但MQTT客户端侧显示连接和发布操作均正常(使用MQTTNet客户端以MQTT/WebSocket模式发布消息,PAHO JavaScript客户端以WebSocket模式订阅/接收消息) - 开启或关闭防火墙均未改变现状
配置文件片段
listener 1883 protocol mqtt listener 9001 protocol websockets password_file mypasswordfile.pwd
启动日志
prompt> ./mosquitto -c myconfig.conf -v 2022-10-15 20:25:53: mosquitto version 2.0.15 starting 2022-10-15 20:25:53: Config loaded from myconfig.conf. 2022-10-15 20:25:53: Opening ipv6 listen socket on port 1883. 2022-10-15 20:25:53: Opening ipv4 listen socket on port 1883. 2022-10-15 20:25:53: Opening websockets listen socket on port 9001. 2022-10-15 20:25:53: mosquitto version 2.0.15 running
发布端代码(MQTTNet)
var mqttFactory = new MqttFactory(); var mqttClient = mqttFactory.CreateMqttClient(); var mqttClientOptions = new MqttClientOptionsBuilder(). // WithTcpServer("localhost"). // 此方式无效 WithWebSocketServer("ws://localhost:9001/mqtt"). // 此方式有效 WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311). WithClientId("publisher-client-id"). WithCredentials("myusername", "mypassword"). Build(); mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None).Wait(); var builder = new MqttApplicationMessageBuilder(); var applicationMessage = builder .WithTopic("mytopic") .WithPayload("mypayload") .Build(); mqttClient.PublishAsync(applicationMessage, CancellationToken.None).Wait();
订阅端代码(PAHO JavaScript)
mqttConnect() { this._mqtt = new Paho.MQTT.Client('localhost', 9001, '/mqtt', 'subscriber-client-id'); this._mqtt.onConnectionLost = this.onMqttConnectionLost.bind(this); this._mqtt.onMessageArrived = this.onMqttMessageReceived.bind(this); const options = { mqttVersion: 4, userName: 'myusername', password: 'mypassword', onSuccess: this.onMqttConnected.bind(this), onFailure: this.onMqttFailedConnecting.bind(this) }; this._mqtt.connect(options); ... } onMqttConnected() { console.log('Connected to MQTT broker'); ... this._mqtt.subscribe('mytopic'); } onMqttMessageReceived(message) { console.log('Received MQTT message; topic = ' + message.destinationName + ', payload = ' + message.payloadString); ... }
这不是Mosquitto的功能限制,问题出在Mosquitto 2.x的默认安全配置以及MQTT客户端的连接有效性上:
1. Mosquitto 2.x的默认访问控制规则
Mosquitto 2.0及以上版本默认启用严格的访问控制策略,未显式配置时,不同listener(对应不同协议端口)的客户端默认处于隔离的访问域,无法跨域转发消息。同时,客户端仅能访问自身发布的主题内容。
解决步骤:
在配置文件中添加访问控制相关配置:
allow_anonymous false acl_file myaclfile.acl
创建myaclfile.acl文件,添加规则允许指定用户访问目标主题:
user myusername topic readwrite mytopic
如果是测试场景,可使用更宽松的规则(生产环境需严格限制):
pattern readwrite #
2. MQTT客户端连接有效性问题(日志无记录的原因)
你提到使用WithTcpServer("localhost")连接1883端口时,Mosquitto日志无记录,但客户端显示连接正常——这说明客户端并未真正完成MQTT握手,只是本地TCP连接成功,实际未接入Mosquitto。
排查与修复:
- 确认MQTT客户端连接时指定了正确的端口(1883),且用户名密码与
password_file中的内容匹配 - 检查
password_file是否通过mosquitto_passwd工具正确生成,确保myusername的密码哈希有效 - 用
mosquitto_pub工具直接测试MQTT协议发布:
mosquitto_pub -h localhost -p 1883 -u myusername -P mypassword -t mytopic -m "test payload"
如果此操作也无日志记录,需进一步排查:
- 用
netstat -ano | findstr :1883检查1883端口是否被其他进程占用 - 将客户端连接地址改为
127.0.0.1,避免localhost解析到IPv6地址导致的连接异常
3. 协议版本兼容性验证
订阅端PAHO客户端指定mqttVersion:4(对应MQTT 3.1.1),发布端MQTTNet也使用了V311版本,二者协议版本匹配,无需调整。
总结
核心问题是Mosquitto 2.x的默认ACL规则阻止了跨协议客户端的消息转发,添加正确的ACL配置后即可实现互通。同时需确保MQTT客户端真正成功连接到1883端口(可通过Mosquitto日志验证)。
内容的提问来源于stack exchange,提问作者Giorgio Barchiesi

