当我发布或接收消息时,Paho MQTT JS 客户端失去与 Mosquitto 代理的连接(错误 AMQJS0005E)

Paho MQTT JS client loses connection to Mosquitto broker when I publish or receive message (error AMQJS0005E)

前面的底线: Paho MQTT 客户端成功连接到我的 Mosquitto 代理,但在我尝试发布消息或从主题接收消息时立即断开连接它已订阅。我试过更改 Mosquitto 的监听端口和身份验证设置,并使用两个不同版本的 Paho MQTT,我仍然遇到同样的问题。

现在让我们进入细节。

简介: 我正在为一些通过 MQTT 通信的面部识别设备制作仪表板。我设置了一个 Mosquitto 代理,连接到它并使用 Python 的 Paho MQTT 客户端与设备通信没有任何问题(我制作了一种服务器来将设备的信息同步到数据库)。现在我正在制作 Web 界面,所以我向我的 mosquitto.conf 添加了一个 WebSockets 侦听器,并使用 Paho MQTT 库编写了一个脚本 Javascript 来连接它,订阅主题 sgdrf/out , 向主题 sgdrf/in 发送一条简单的 JSON 消息以获取在线设备列表,并在到达后处理 Python 服务器的响应。

问题及尝试的解决方案:我运行Django服务器,加载网页打开JS控制台发现MQTT客户端成功连接到broker但当它试图将消息发布到主题 sgdrf/in 时立即断开连接。这是控制台输出的每一行及其解释:

  1. onSuccess函数产生的消息,表明客户端成功连接到Mosquitto broker:

    Conexión exitosa al broker MQTT.

  2. 在 onConnected 函数中,我添加了 console.log(uri) 以查看客户端连接到代理所使用的 URI。我有:

    ws://localhost:61613/

  3. 在向控制台打印 uri 之后,我让客户端订阅 sgdrf/out 然后向控制台打印 'subscribed' :

    subscribed

  4. 然后我调用 get_online_devices(mqtt_client),一个创建简单 JSON 字符串并将其发布到主题 sgdrf/in 的函数。但首先,它将字符串打印到控制台,以便我可以检查它(以防万一):

    {"operator":"GetOnlineDevices","messageId":96792535859850080000,"info":{}}

  5. 然后,当publish方法真正执行时,是我得到这个错误的时候(由onConnectionLost函数捕获):

    Pérdida de conexión con el broker MQTT: AMQJS0005E Internal error. Error Message: message is not defined, Stack trace: No Error Stack Available (código: 5)

我检查了 Mosquitto 日志文件,它只说明何时连接了新客户端,然后又何时因套接字错误而断开连接(每次重新加载页面时)。 /var/log/mosquitto/mosquitto.log 的尾部:

1614796149: New connection from 127.0.0.1 on port 61612.
1614796149: New client connected from 127.0.0.1 as mqttx_53195902 (p2, c1, k60, u'admin').
1614796182: Socket error on client sgdrf_dashboard_8499, disconnecting.
1614796325: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_1597 (p2, c1, k60, u'admin').
1614796325: Socket error on client sgdrf_dashboard_1597, disconnecting.
1614796336: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_6565 (p2, c1, k60, u'admin').
1614796336: Socket error on client sgdrf_dashboard_6565, disconnecting.
1614796931: New client connected from ::ffff:127.0.0.1 as sgdrf_dashboard_9773 (p2, c1, k60, u'admin').
1614796931: Socket error on client sgdrf_dashboard_9773, disconnecting.
1614797168: Saving in-memory database to /var/lib/mosquitto/mosquitto.db.

我尝试更改 mosquitto.conf 中的侦听端口,并启用和禁用身份验证,但没有任何改变。显然,每次更改配置文件时我都必须重新启动 Mosquito。我不认为问题出在蚊子上。

无论我使用Paho MQTT 1.1.0 还是1.0.3 版本,我都遇到同样的问题

作为实验,我在我的 Javascript 中注释掉了对 get_online_devices 的调用,这样它就不会尝试发布任何内容,重新加载页面并且没有错误,正如预期的那样。然后,我使用 MQTTX 向 MQTT JS 客户端订阅的 sgdrf/out 主题发送 JSON 消息,它立即断开连接并显示相同的错误消息。

代码:在页面底部(index.html)我有如下代码(原代码有Django模板标签填写一些值,所以这是浏览器收到的实际代码):

<!-- Paho MQTT -->
<script src="/static/js/paho-mqtt-min.js"></script>

<!-- Scripts -->
<script src="/static/js/dashboard.js"></script>
    
<script>
function get_online_devices(mqtt_client) {
    cmd = {
        operator: "GetOnlineDevices",
        messageId: generate_random_number_n_exp(20),
        info: {}
    };

    payload_string = JSON.stringify(cmd);
    console.log(payload_string)

    mqtt_client.publish("sgdrf/in", payload_string);
}

function add_device_to_list(device) {
    // Omitted for brevity. It's not being used yet.
}

let mqtt_client = make_mqtt_client("localhost", 61613);
let connection_options = make_connection_options(
    "admin",
    "CENSORED_PASSWORD"
);

mqtt_client.onConnected = function(reconnect, uri) {
    console.log(uri)
    mqtt_client.subscribe("sgdrf/out");
    console.log('subscribed');
    get_online_devices(mqtt_client);
};
mqtt_client.onConnectionLost = mqtt_client_on_connection_lost;
mqtt_client.onMessageDelivered = mqtt_client_on_message_delivered;

mqtt_client.onMessageArrived = function (msg) {
    // Omitted for brevity. Checks if the payload is a
    // JSON object with the right data and calls
    // add_device_to_list for each item of a list in it.
};

$(document).ready(function() {
    mqtt_client.connect(connection_options);

    $("#reload-device-list-btn").click(function() {
        get_online_devices(mqtt_client);
    });
});
</script>

上面提到的dashboard.js文件只是有一些我认为对其他页面有用的功能,所以我把它们分开到一个文件中:

// dashboard.js
function generate_random_number_n_exp(n) {
    return parseInt(Math.random() * Math.pow(10, n), 10)
}

function make_mqtt_client(host, port) {
    let client_id = "sgdrf_dashboard_" + generate_random_number_n_exp(4);
    return new Paho.Client(host, port, '/', client_id);
}

function make_connection_options(user, password) {
    let connection_options = {
        userName: user,
        password: password,
        onSuccess: mqtt_client_on_success,
        onFailure: mqtt_client_on_failure,
    };
    
    return connection_options;
}

function mqtt_client_on_success() {
    console.log('Conexión exitosa al broker MQTT.');
}

function mqtt_client_on_failure(error) {
    console.log(
        'Fallo de conexión con el broker MQTT: ' + error.errorMessage
        + ' (código: ' + error.errorCode + ')'
    );
}

function mqtt_client_on_connection_lost (error) {
    console.log('Pérdida de conexión con el broker MQTT: ' + error.errorMessage
        + ' (código: ' + error.errorCode + ')'
    );
}

function mqtt_client_on_message_delivered(msg) {
    let topic = message.destinationName;
    let payload = message.payloadString;
    console.log("Mensaje enviado a " + topic + ": " + payload);
}

function mqtt_client_on_message_arrived(msg) {
    let topic = message.destinationName;
    let payload = message.payloadString;
    console.log("Mensaje recibido de " + topic + ": " + payload);
}

这是我的 mosquitto.conf 文件的内容:

per_listener_settings true
listener 61612
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt

listener 61613
protocol websockets
allow_anonymous false
password_file /home/s8a/Projects/sgdrf/config/pwdfile.txt

它只是设置了一个 TCP 侦听器和一个 WebSockets 侦听器,两者都不允许匿名连接,并使用密码文件进行身份验证。正如我之前所说,我启用和禁用匿名连接,并将端口号更改为 9001 并更改回 61613,我仍然有同样的错误。

结论:我不知道该做什么,这个项目的截止日期是下周。

我觉得有点傻,因为这真的是一个微不足道的打字错误。问题是 onMessageDelivered 和 onMessageArrived 函数有 msg 作为参数,但出于某种原因我在函数体中写了 message。这就是“消息未定义”错误的意思,message 字面上没有定义。无论如何,我修复了它,现在它可以毫无问题地发送和接收消息。

...

更详细的故事:重要的是我是如何想出来的。

我决定亲力亲为,打开 paho-mqtt.js 的非缩小版。我查找了“无效错误”并找到了错误常量的定义位置,以及它在 catch 块中使用的两个位置。在两个 catch 块中,我注意到有一个三元运算符检查是否 (error.hasOwnProperty("stack") == "undefined") 但 true 和 false 子句被颠倒了,这就是为什么我得到“没有错误堆栈可用”的原因。

所以我颠倒了子句,实际上我在控制台中得到了堆栈跟踪(也许我应该在可能的时候向 Paho 开发团队提交错误报告)。堆栈跟踪在顶部有我的 mqtt_client_on_message_delivered 函数,所以我再次阅读它,突然间一切都变得有意义了。然后觉得浪费一个下午在这上面很蠢