编译环境
- Qt 5.14.1 for Windows
- MinGW 7.3.0 32-bit
- emqx/qmqtt
问题背景
使用Qt结合第三方MQTT客户端库(emqx/qmqtt)编写MQTT客户端,使用该客户端连接部署于OneNet平台的MQTT服务端时,出现了连接失败的问题。连接客户端所使用的代码如下:
QMQTT::Client *m_client = new QMQTT::Client(QHostAddress(address), port);
m_client->setClientId(client_id);
m_client->setUsername(usrname);
m_client->setPassword(password);
m_client->setKeepAlive(60);
m_client->connectToHost();
问题定位与解决
遇到上述问题后,分别做了如下测试:
- 通过第三方MQTT客户端MQTT X访问OneNet平台的目标MQTT服务端 ---- PASS
- 通过qmqtt客户端访问虚拟机内的mosquitto服务端 ---- PASS
- 绑定qmqtt库的error(QMQTT::ClientError)信号,打印qmqtt库的报错 ---- QMQTT::SocketUnsupportedSocketOperationError
- 改用Qt官方的MQTT库连接OneNet平台的目标MQTT服务端 ---- FAIL
- 通过wireshark在客户端尝试连接服务端时进行抓包 ---- 没有捕获到相关数据
通过上述测试,可以发现在客户端尝试连接时,没有向服务端发送相关请求,所以问题可能是客户端的网络部分存在问题,检查了qmake.pro文件已经配置了QT += network后,仍存在该问题,最后在Qt官方论坛中找到了类似问题的解决方法:QTcpSocket/QMQTT - Strange Error Codes,在这篇帖子的回复中给出了解决方法:禁用代理服务器或在代码的开头部分加上:
/* call before connecting to network. Easiest way is, just call somewhere at the beginning of the program */
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
进行了相应的修改后(顺便把我的v2ray关掉…),再次进行尝试,还是连接不上OneNet的MQTT服务端,重新进行以下测试:
- 绑定qmqtt库的error(QMQTT::ClientError)信号,打印qmqtt库的报错 ---- QMQTT::MqttUnacceptableProtocolVersionError
- 改用Qt官方的MQTT库连接OneNet平台的目标MQTT服务端 ---- PASS
- 通过wireshark在客户端尝试连接服务端时进行抓包 ---- 捕获到了相关请求报文
可以发现相比之前的结果,有了巨大的进展,如果不是实在无聊出于好奇心,我甚至可以直接用Qt官方的MQTT库进行该应用的开发,通过将qmqtt库连接OneNet失败的抓包记录与MQTT X连接OneNet成功的抓包记录进行对比,可以发现在向服务端发送连接的请求中,qmqtt库发送的一段报文数据为“MQIsdp”,而MQTT X发送的该段报文数据为“MQTT”。
经过参考相关资料可知:“MQIsdp”为MQTT3.1.0的CONNECT消息中的可变头部协议名称,在MQTT3.1.1版本中,该字段被修改为“MQTT”。所以尝试在原来qmqtt库的客户端配置中加入:
//使用V3.1.1的MQTT版本的客户端
m_client->setVersion(QMQTT::MQTTVersion::V3_1_1);
加入这行配置后再次进行测试,可以成功连接到OneNet平台的MQTT服务端。