Commit 50a88a9ce76af69bd4b05ceae449a02b91ed1d29
1 parent
6fdd91d0
review:【iot】mqtt websocket 协议
Showing
3 changed files
with
11 additions
and
30 deletions
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/manager/IotMqttWsConnectionManager.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import io.vertx.core.http.ServerWebSocket; |
| 4 | 5 | import lombok.extern.slf4j.Slf4j; |
| 5 | 6 | import org.springframework.stereotype.Component; |
| ... | ... | @@ -187,7 +188,7 @@ public class IotMqttWsConnectionManager { |
| 187 | 188 | */ |
| 188 | 189 | public boolean isSubscribed(String deviceKey, String topic) { |
| 189 | 190 | Set<String> subscriptions = deviceSubscriptions.get(deviceKey); |
| 190 | - if (subscriptions == null || subscriptions.isEmpty()) { | |
| 191 | + if (CollUtil.isEmpty(subscriptions)) { | |
| 191 | 192 | return false; |
| 192 | 193 | } |
| 193 | 194 | |
| ... | ... | @@ -210,6 +211,7 @@ public class IotMqttWsConnectionManager { |
| 210 | 211 | return deviceSubscriptions.get(deviceKey); |
| 211 | 212 | } |
| 212 | 213 | |
| 214 | + // TODO @haohao:这个方法,是不是也可以考虑抽到 IotMqttTopicUtils 里面去哈;感觉更简洁一点? | |
| 213 | 215 | /** |
| 214 | 216 | * MQTT 主题匹配 |
| 215 | 217 | * 支持通配符: |
| ... | ... | @@ -227,25 +229,25 @@ public class IotMqttWsConnectionManager { |
| 227 | 229 | } |
| 228 | 230 | |
| 229 | 231 | // 不包含通配符 |
| 232 | + // TODO @haohao:这里要不要枚举下哈;+ # | |
| 230 | 233 | if (!subscription.contains("+") && !subscription.contains("#")) { |
| 231 | 234 | return false; |
| 232 | 235 | } |
| 233 | 236 | |
| 234 | 237 | String[] subscriptionParts = subscription.split("/"); |
| 235 | 238 | String[] topicParts = topic.split("/"); |
| 236 | - | |
| 237 | 239 | int i = 0; |
| 238 | 240 | for (; i < subscriptionParts.length && i < topicParts.length; i++) { |
| 239 | 241 | String subPart = subscriptionParts[i]; |
| 240 | 242 | String topicPart = topicParts[i]; |
| 241 | 243 | |
| 244 | + // # 匹配剩余所有层级,且必须在末尾 | |
| 242 | 245 | if (subPart.equals("#")) { |
| 243 | - // # 匹配剩余所有层级,且必须在末尾 | |
| 244 | 246 | return i == subscriptionParts.length - 1; |
| 245 | 247 | } |
| 246 | 248 | |
| 249 | + // 不是通配符且不匹配 | |
| 247 | 250 | if (!subPart.equals("+") && !subPart.equals(topicPart)) { |
| 248 | - // 不是通配符且不匹配 | |
| 249 | 251 | return false; |
| 250 | 252 | } |
| 251 | 253 | } | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsDownstreamHandler.java
| ... | ... | @@ -148,7 +148,7 @@ public class IotMqttWsDownstreamHandler { |
| 148 | 148 | try { |
| 149 | 149 | int messageId = qos > 0 ? generateMessageId() : 0; |
| 150 | 150 | |
| 151 | - // 手动编码MQTT PUBLISH消息 | |
| 151 | + // 手动编码 MQTT PUBLISH 消息 | |
| 152 | 152 | io.netty.buffer.ByteBuf byteBuf = io.netty.buffer.Unpooled.buffer(); |
| 153 | 153 | |
| 154 | 154 | // 固定头:消息类型(PUBLISH=3) + DUP(0) + QoS + RETAIN |
| ... | ... | @@ -159,11 +159,11 @@ public class IotMqttWsDownstreamHandler { |
| 159 | 159 | int topicLength = topic.getBytes().length; |
| 160 | 160 | int remainingLength = 2 + topicLength + (qos > 0 ? 2 : 0) + payload.length; |
| 161 | 161 | |
| 162 | - // 写入剩余长度(简化版本,假设小于128字节) | |
| 162 | + // 写入剩余长度(简化版本,假设小于 128 字节) | |
| 163 | 163 | if (remainingLength < 128) { |
| 164 | 164 | byteBuf.writeByte(remainingLength); |
| 165 | 165 | } else { |
| 166 | - // 处理大于127的情况 | |
| 166 | + // 处理大于 127 的情况 | |
| 167 | 167 | int x = remainingLength; |
| 168 | 168 | do { |
| 169 | 169 | int encodedByte = x % 128; |
| ... | ... | @@ -179,7 +179,7 @@ public class IotMqttWsDownstreamHandler { |
| 179 | 179 | byteBuf.writeShort(topicLength); |
| 180 | 180 | byteBuf.writeBytes(topic.getBytes()); |
| 181 | 181 | |
| 182 | - // 可变头:消息ID(仅QoS>0时) | |
| 182 | + // 可变头:消息 ID(仅 QoS > 0 时) | |
| 183 | 183 | if (qos > 0) { |
| 184 | 184 | byteBuf.writeShort(messageId); |
| 185 | 185 | } |
| ... | ... | @@ -191,7 +191,6 @@ public class IotMqttWsDownstreamHandler { |
| 191 | 191 | byte[] bytes = new byte[byteBuf.readableBytes()]; |
| 192 | 192 | byteBuf.readBytes(bytes); |
| 193 | 193 | byteBuf.release(); |
| 194 | - | |
| 195 | 194 | socket.writeBinaryMessage(Buffer.buffer(bytes)); |
| 196 | 195 | |
| 197 | 196 | log.info("[sendMessageToDevice][消息已发送到设备,deviceKey: {},topic: {},qos: {},messageId: {}]", |
| ... | ... | @@ -211,6 +210,7 @@ public class IotMqttWsDownstreamHandler { |
| 211 | 210 | private int generateMessageId() { |
| 212 | 211 | int id = messageIdGenerator.getAndIncrement(); |
| 213 | 212 | // MQTT 消息 ID 范围是 1-65535 |
| 213 | + // TODO @haohao:并发可能有问题; | |
| 214 | 214 | if (id > 65535) { |
| 215 | 215 | messageIdGenerator.set(1); |
| 216 | 216 | return 1; | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsUpstreamHandler.java
| ... | ... | @@ -253,7 +253,6 @@ public class IotMqttWsUpstreamHandler { |
| 253 | 253 | |
| 254 | 254 | // 5. 发送设备上线消息 |
| 255 | 255 | sendOnlineMessage(device); |
| 256 | - | |
| 257 | 256 | } catch (Exception e) { |
| 258 | 257 | log.error("[handleConnect][处理 CONNECT 消息失败,socketId: {}]", socketId, e); |
| 259 | 258 | sendConnAck(socket, MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); |
| ... | ... | @@ -311,7 +310,6 @@ public class IotMqttWsUpstreamHandler { |
| 311 | 310 | sendPubRec(socket, messageId); |
| 312 | 311 | } |
| 313 | 312 | // QoS 0 无需确认 |
| 314 | - | |
| 315 | 313 | } catch (Exception e) { |
| 316 | 314 | log.error("[handlePublish][处理 PUBLISH 消息失败,deviceId: {}]", device.getId(), e); |
| 317 | 315 | } |
| ... | ... | @@ -323,7 +321,6 @@ public class IotMqttWsUpstreamHandler { |
| 323 | 321 | private void handlePubAck(ServerWebSocket socket, MqttMessage message) { |
| 324 | 322 | String socketId = socketIdMap.get(socket); |
| 325 | 323 | IotDeviceRespDTO device = socketDeviceMap.get(socketId); |
| 326 | - | |
| 327 | 324 | if (device == null) { |
| 328 | 325 | log.warn("[handlePubAck][设备未认证,socketId: {}]", socketId); |
| 329 | 326 | socket.close(); |
| ... | ... | @@ -340,7 +337,6 @@ public class IotMqttWsUpstreamHandler { |
| 340 | 337 | private void handlePubRec(ServerWebSocket socket, MqttMessage message) { |
| 341 | 338 | String socketId = socketIdMap.get(socket); |
| 342 | 339 | IotDeviceRespDTO device = socketDeviceMap.get(socketId); |
| 343 | - | |
| 344 | 340 | if (device == null) { |
| 345 | 341 | log.warn("[handlePubRec][设备未认证,socketId: {}]", socketId); |
| 346 | 342 | socket.close(); |
| ... | ... | @@ -349,7 +345,6 @@ public class IotMqttWsUpstreamHandler { |
| 349 | 345 | |
| 350 | 346 | int messageId = ((MqttMessageIdVariableHeader) message.variableHeader()).messageId(); |
| 351 | 347 | log.debug("[handlePubRec][收到 PUBREC,messageId: {},deviceId: {}]", messageId, device.getId()); |
| 352 | - | |
| 353 | 348 | // 发送 PUBREL |
| 354 | 349 | sendPubRel(socket, messageId); |
| 355 | 350 | } |
| ... | ... | @@ -369,7 +364,6 @@ public class IotMqttWsUpstreamHandler { |
| 369 | 364 | |
| 370 | 365 | int messageId = ((MqttMessageIdVariableHeader) message.variableHeader()).messageId(); |
| 371 | 366 | log.debug("[handlePubRel][收到 PUBREL,messageId: {},deviceId: {}]", messageId, device.getId()); |
| 372 | - | |
| 373 | 367 | // 发送 PUBCOMP |
| 374 | 368 | sendPubComp(socket, messageId); |
| 375 | 369 | } |
| ... | ... | @@ -397,7 +391,6 @@ public class IotMqttWsUpstreamHandler { |
| 397 | 391 | private void handleSubscribe(ServerWebSocket socket, MqttSubscribeMessage message) { |
| 398 | 392 | String socketId = socketIdMap.get(socket); |
| 399 | 393 | IotDeviceRespDTO device = socketDeviceMap.get(socketId); |
| 400 | - | |
| 401 | 394 | if (device == null) { |
| 402 | 395 | log.warn("[handleSubscribe][设备未认证,socketId: {}]", socketId); |
| 403 | 396 | socket.close(); |
| ... | ... | @@ -429,7 +422,6 @@ public class IotMqttWsUpstreamHandler { |
| 429 | 422 | |
| 430 | 423 | // 3. 发送 SUBACK |
| 431 | 424 | sendSubAck(socket, messageId, grantedQosList); |
| 432 | - | |
| 433 | 425 | } catch (Exception e) { |
| 434 | 426 | log.error("[handleSubscribe][处理 SUBSCRIBE 消息失败,deviceId: {}]", device.getId(), e); |
| 435 | 427 | } |
| ... | ... | @@ -441,7 +433,6 @@ public class IotMqttWsUpstreamHandler { |
| 441 | 433 | private void handleUnsubscribe(ServerWebSocket socket, MqttUnsubscribeMessage message) { |
| 442 | 434 | String socketId = socketIdMap.get(socket); |
| 443 | 435 | IotDeviceRespDTO device = socketDeviceMap.get(socketId); |
| 444 | - | |
| 445 | 436 | if (device == null) { |
| 446 | 437 | log.warn("[handleUnsubscribe][设备未认证,socketId: {}]", socketId); |
| 447 | 438 | socket.close(); |
| ... | ... | @@ -465,7 +456,6 @@ public class IotMqttWsUpstreamHandler { |
| 465 | 456 | |
| 466 | 457 | // 3. 发送 UNSUBACK |
| 467 | 458 | sendUnsubAck(socket, messageId); |
| 468 | - | |
| 469 | 459 | } catch (Exception e) { |
| 470 | 460 | log.error("[handleUnsubscribe][处理 UNSUBSCRIBE 消息失败,deviceId: {}]", device.getId(), e); |
| 471 | 461 | } |
| ... | ... | @@ -477,7 +467,6 @@ public class IotMqttWsUpstreamHandler { |
| 477 | 467 | private void handlePingReq(ServerWebSocket socket) { |
| 478 | 468 | String socketId = socketIdMap.get(socket); |
| 479 | 469 | IotDeviceRespDTO device = socketDeviceMap.get(socketId); |
| 480 | - | |
| 481 | 470 | if (device == null) { |
| 482 | 471 | log.warn("[handlePingReq][设备未认证,socketId: {}]", socketId); |
| 483 | 472 | socket.close(); |
| ... | ... | @@ -485,7 +474,6 @@ public class IotMqttWsUpstreamHandler { |
| 485 | 474 | } |
| 486 | 475 | |
| 487 | 476 | log.debug("[handlePingReq][收到心跳请求,deviceId: {}]", device.getId()); |
| 488 | - | |
| 489 | 477 | // 发送 PINGRESP |
| 490 | 478 | sendPingResp(socket); |
| 491 | 479 | } |
| ... | ... | @@ -496,7 +484,6 @@ public class IotMqttWsUpstreamHandler { |
| 496 | 484 | private void handleDisconnect(ServerWebSocket socket) { |
| 497 | 485 | String socketId = socketIdMap.get(socket); |
| 498 | 486 | IotDeviceRespDTO device = socketDeviceMap.remove(socketId); |
| 499 | - | |
| 500 | 487 | if (device != null) { |
| 501 | 488 | String deviceKey = device.getProductKey() + ":" + device.getDeviceName(); |
| 502 | 489 | connectionManager.removeConnection(deviceKey); |
| ... | ... | @@ -600,7 +587,6 @@ public class IotMqttWsUpstreamHandler { |
| 600 | 587 | |
| 601 | 588 | // 编码并发送 |
| 602 | 589 | sendMqttMessage(socket, connAckMessage); |
| 603 | - | |
| 604 | 590 | log.debug("[sendConnAck][发送 CONNACK 消息,returnCode: {}]", returnCode); |
| 605 | 591 | } catch (Exception e) { |
| 606 | 592 | log.error("[sendConnAck][发送 CONNACK 消息失败]", e); |
| ... | ... | @@ -620,7 +606,6 @@ public class IotMqttWsUpstreamHandler { |
| 620 | 606 | |
| 621 | 607 | // 编码并发送 |
| 622 | 608 | sendMqttMessage(socket, pubAckMessage); |
| 623 | - | |
| 624 | 609 | log.debug("[sendPubAck][发送 PUBACK 消息,messageId: {}]", messageId); |
| 625 | 610 | } catch (Exception e) { |
| 626 | 611 | log.error("[sendPubAck][发送 PUBACK 消息失败,messageId: {}]", messageId, e); |
| ... | ... | @@ -640,7 +625,6 @@ public class IotMqttWsUpstreamHandler { |
| 640 | 625 | |
| 641 | 626 | // 编码并发送 |
| 642 | 627 | sendMqttMessage(socket, pubRecMessage); |
| 643 | - | |
| 644 | 628 | log.debug("[sendPubRec][发送 PUBREC 消息,messageId: {}]", messageId); |
| 645 | 629 | } catch (Exception e) { |
| 646 | 630 | log.error("[sendPubRec][发送 PUBREC 消息失败,messageId: {}]", messageId, e); |
| ... | ... | @@ -660,7 +644,6 @@ public class IotMqttWsUpstreamHandler { |
| 660 | 644 | |
| 661 | 645 | // 编码并发送 |
| 662 | 646 | sendMqttMessage(socket, pubRelMessage); |
| 663 | - | |
| 664 | 647 | log.debug("[sendPubRel][发送 PUBREL 消息,messageId: {}]", messageId); |
| 665 | 648 | } catch (Exception e) { |
| 666 | 649 | log.error("[sendPubRel][发送 PUBREL 消息失败,messageId: {}]", messageId, e); |
| ... | ... | @@ -680,7 +663,6 @@ public class IotMqttWsUpstreamHandler { |
| 680 | 663 | |
| 681 | 664 | // 编码并发送 |
| 682 | 665 | sendMqttMessage(socket, pubCompMessage); |
| 683 | - | |
| 684 | 666 | log.debug("[sendPubComp][发送 PUBCOMP 消息,messageId: {}]", messageId); |
| 685 | 667 | } catch (Exception e) { |
| 686 | 668 | log.error("[sendPubComp][发送 PUBCOMP 消息失败,messageId: {}]", messageId, e); |
| ... | ... | @@ -701,7 +683,6 @@ public class IotMqttWsUpstreamHandler { |
| 701 | 683 | |
| 702 | 684 | // 编码并发送 |
| 703 | 685 | sendMqttMessage(socket, subAckMessage); |
| 704 | - | |
| 705 | 686 | log.debug("[sendSubAck][发送 SUBACK 消息,messageId: {},主题数量: {}]", messageId, grantedQosList.length); |
| 706 | 687 | } catch (Exception e) { |
| 707 | 688 | log.error("[sendSubAck][发送 SUBACK 消息失败,messageId: {}]", messageId, e); |
| ... | ... | @@ -721,7 +702,6 @@ public class IotMqttWsUpstreamHandler { |
| 721 | 702 | |
| 722 | 703 | // 编码并发送 |
| 723 | 704 | sendMqttMessage(socket, unsubAckMessage); |
| 724 | - | |
| 725 | 705 | log.debug("[sendUnsubAck][发送 UNSUBACK 消息,messageId: {}]", messageId); |
| 726 | 706 | } catch (Exception e) { |
| 727 | 707 | log.error("[sendUnsubAck][发送 UNSUBACK 消息失败,messageId: {}]", messageId, e); |
| ... | ... | @@ -740,7 +720,6 @@ public class IotMqttWsUpstreamHandler { |
| 740 | 720 | |
| 741 | 721 | // 编码并发送 |
| 742 | 722 | sendMqttMessage(socket, pingRespMessage); |
| 743 | - | |
| 744 | 723 | log.debug("[sendPingResp][发送 PINGRESP 消息]"); |
| 745 | 724 | } catch (Exception e) { |
| 746 | 725 | log.error("[sendPingResp][发送 PINGRESP 消息失败]", e); | ... | ... |