本文介绍云消息队列 MQTT 版客户端连接服务端时的常见问题。
单向认证和双向认证的区别是什么?
单向认证时,设备会验证服务端的证书以确认服务端身份,随后开始加密传输。双向认证在此基础上增加了服务端验证设备证书的过程。如果仅需要加密传输,使用单向认证即可。
单向认证时,阿里云向设备提供的服务端证书由权威机构颁发(使用 GlobalSign 签发,与 aliyun.com 域名的根证书签发机构一致)。该权威机构的根证书通常已内置于操作系统、浏览器和编程语言的运行环境中,因此设备可以直接验证并信任 MQTT 服务端的证书。
配置双向认证有哪些注意事项?
请按照文档依次配置 CA 证书、服务器证书和设备证书,即可正确完成双向认证的证书配置。请参见证书管理。
配置时请注意以下事项:
服务器证书管理中,CN 字段可以不填写,只需确保 server.crt.cfg 中的 alt_names 包含 *.mqtt.aliyuncs.com 即可。
设备证书管理中,文件中的 CN 字段无需修改。
如何安装 MQTT .NET SDK?
目前依赖的 RabbitMQ Client 版本需要为 6.5.0,否则可能存在兼容性问题。请参见版本说明。
所需的安装包可在以下地址搜索安装:https://www.nuget.org/packages。
连接 MQTT 的验证方法请参见以下示例代码:示例代码。
使用 C# .NET 实现 P2P 消息发送时报错如何解决?
请先下载 M2Mqtt 包:https://www.nuget.org/packages/M2Mqtt。
P2P 收发模式的详细说明请参见:P2P消息收发模式(MQTT)。
程序报错 "Too many publishes in progress" 如何解决?
原因:单个设备的发送 TPS 过大。
解决方法:控制单个设备的发送 TPS,可以尝试增大 mqttConnectOptions.setMaxInflight() 参数的值。该参数用于设置单个客户端发送消息的最大并发数。
客户端连接完成后首次接收消息为什么有延迟?
connectComplete回调触发后,首次接收消息可能存在约2秒的延迟。这是因为客户端在连接完成后还需要执行初始化和订阅初始化等操作,属于正常现象。
PHP如何使用MQTT客户端库?
推荐使用php-mqtt/client库。
在src同级目录下执行composer install安装依赖后,即可通过php src/MqttConsumer.php和php src/MqttProducer.php运行示例程序。
Android端使用MQTT客户端库报错如何解决?
org.eclipse.paho:org.eclipse.paho.client.mqttv3库的1.1.1版本不支持Android SDK 33及以上版本。建议使用社区维护的hannesa2/paho.mqtt.android库,该库支持Android SDK 33版本。
请参见paho.mqtt.android releases。
说明:云消息队列MQTT版兼容开源MQTT协议,但不同的第三方SDK可能存在各自的兼容性问题,建议使用上述推荐库进行开发。
如何连接VPC接入点?
同一地域内的ECS实例可通过内网直接连接VPC接入点,无需与MQTT实例在同一阿里云账号下。
云端SDK和终端SDK的接入点有什么不同?
终端SDK接入点格式:
公网接入点:
{MQTT实例ID}.mqtt.aliyuncs.comVPC接入点:
{MQTT实例ID}-internal-vpc.mqtt.aliyuncs.com
云端SDK接入点格式:
公网接入点:
{MQTT实例ID}-server-internet.mqtt.aliyuncs.comVPC接入点:
{MQTT实例ID}-server-internal.mqtt.aliyuncs.com
MQTT服务的心跳检测逻辑是什么?
客户端每次发送心跳包后,服务端开始计时。如果在约1.5倍心跳间隔时间内未收到新的心跳包,服务端将判定连接已断开。如果在该时间内收到了心跳包,则重新开始计时。
什么情况下可以使用代理域名访问MQTT服务?
以下场景支持使用代理域名访问MQTT服务:
单向认证且不使用TLS时:可以直接使用代理域名访问。
双向认证时:支持通过代理域名建立TLS连接。请注意,代理域名需要完成ICP备案,以确保合规性和访问连通性。
关于IPv6的说明:MQTT服务本身不支持IPv6直连。如果需要通过IPv6环境访问MQTT服务,可以将IPv6流量通过代理服务器转发至IPv4域名,再由该IPv4域名指向MQTT服务接入点。
使用安卓的Demo,返回连接失败报错。
问题描述
资源填写正确,且网络可以连通。
AK(AccessKey ID)、SK(AccessKey Secret)填写正确且已授权。
可能原因
接入点格式不正确,正确格式为“tcp://Endpoint:1883”或“tls://Endpoint:8883”。
为什么客户端能连接上,消费端一订阅就马上断连?
可能原因:Topic设置和命名格式不正确。请检查您的父级Topic和子级Topic的设置情况。
父级Topic(Parent Topic)
MQTT协议基于Pub/Sub模型,因此任何消息都属于一个Topic。根据MQTT协议,Topic存在多级,定义第一级Topic为父级Topic,使用云消息队列 MQTT 版前,需先在控制台创建该父级Topic,可以在云消息队列 MQTT 版控制台创建,或者直接在云消息队列 RocketMQ 版的控制台创建。
子级Topic(Subtopic)
MQTT的二级Topic,甚至三级Topic都是父级Topic下的子类。使用时无需在控制台创建,直接在代码中设置即可。命名格式为:父级Topic和各子级Topic间均使用正斜线(/)隔开,<父级Topic名称>/<二级Topic名称>/<三级Topic名称>,例如,SendMessage/demo/producer。需要注意的是云消息队列 MQTT 版限制父级Topic和子级Topic的总长度为64个字符,如果超出长度限制将会导致客户端异常。
为什么控制台显示的订阅关系数比实际使用的高很多?
当cleanSession设置为false时,客户端离线后订阅关系也会一直保留,不会被清理。
若您不需要保留离线订阅关系,您可以使用订阅关系清理功能,自动清理离线客户端的订阅关系。
客户端一直断线重连,并且返回“invalid param”错误。
可能原因:消息体超过限制,服务端拒绝连接。云消息队列 MQTT 版中,消息体不能超过64 KB。
业务侧无改动,客户端和服务端连接突然断开。
可能原因:
设备关机、网络不通导致客户端连接断开。
若您使用的是Token鉴权方式,Token到期后连接会断开,重新建立连接时客户端未获取新的Token也会导致连接失败。
客户端心跳时间在哪里设置?
调用MqttConnectOptions类。
在使用示例代码前,需要配置环境变量,通过环境变量读取访问凭证。关于配置环境变量的方法,请参见配置访问凭证。
云消息队列 MQTT 版的AccessKey ID和AccessKey Secret的环境变量名称分别为MQTT_AK_ENV和MQTT_SK_ENV。
客户端连接时出现“valid owner failed”报错
请保证以下内容是否正确:
实例名称是否填写正确?
Group和Topic是否已在控制台提前创建且名称填写正确?
Group或Topic是填写的指定实例下的资源?
设备偶尔出现“connection reset by peer”错误。
客户端ID(ClientID)重复时会互踢,导致服务端断开连接,而客户端继续往TCP连接中发送数据,会触发对端Socket发送RST报文。
请确保您设置的客户端ID全局唯一。更多信息,请参见客户端限制。
如何配置MQTT开源Java SDK的客户端自动连接
开启SDK客户端自动重连功能
mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);将autoReconnect参数设置为true后,云消息队列 MQTT 版的Java SDK客户端会自动重连。
mqttClient.setCallback(new MqttCallbackExtended() {
@Override
public void connectComplete(boolean reconnect, String serverURI) {
/**
* 客户端连接成功后就需要尽快订阅的Topic。
*/
System.out.println("connect success");
}
@Override
public void connectionLost(Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
/**
* 消费消息的回调接口,需要确保该接口不抛异常,该接口运行返回即代表消息消费成功。
* 消费消息需要保证在规定时间内完成,如果消费耗时超过服务端约定的超时时间,对于可靠传输的模式,服务端可能会重试推送,业务需要做好幂等去重处理。
* 超时时间请参见使用限制。
*/
System.out.println(
"receive msg from topic " + s + " , body is " + new String(mqttMessage.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println("send msg succeed topic is : " + iMqttDeliveryToken.getTopics()[0]);
}
});这样客户端的回调中的connectComplete方法被调用时就代表连接成功或重连成功,业务方无需关心连接情况。这时候重新建立的连接与之前的连接已经不是一个,因此之前客户端的内部连接上的消息发送订阅等行为会失效。
此时需要重新订阅Topic,这样业务侧在新的内部连接中可以继续发送订阅消息。
@Override
public void connectComplete(boolean reconnect, String serverURI) {
/**
* 客户端连接成功后就需要尽快订阅需要的Topic。
*/
System.out.println("connect success");
executorService.submit(new Runnable() {
@Override
public void run() {
try {
final String topicFilter[] = {mq4IotTopic};
final int[] qos = {qosLevel};
mqttClient.subscribe(topicFilter, qos);
} catch (MqttException e) {
e.printStackTrace();
}
}
});
}业务侧手动维护客户端连接
mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(false);当autoReconnect参数设置为false时,云消息队列 MQTT 版的客户端断开后不会自动重连。
当连接断开后,业务侧会抓取到异常,需要手动将旧的连接关闭,然后重新建立新的连接。