物联网 基于netty构建mqtt协议规范(三种 QoS 等级)

物联网 基于netty构建mqtt协议规范(三种 QoS 等级)

简述

QoS(Quality of Service)是 MQTT 协议的核心特性,定义了消息传递的可靠性等级

源码(netty-sample-03-qos)

https://gitee.com/kcnf-iot/iot-sample/tree/master/netty/netty-sample-03

三种 QoS 等级

等级 名称 消息交付次数 确认机制 适用场景
0 最多一次 最多一次(可能丢失) 无确认,不重传 传感器数据、日志,可容忍少量丢失
1 至少一次 至少一次(可能重复) PUBLISH → PUBACK,超时重传 需要可靠送达但可接受重复的控制指令
2 仅一次 恰好一次(不重不丢) 4 步握手(PUBREC / PUBREL / PUBCOMP) 计费、关键状态同步,不允许重复

协议命令演示

🧩 命令交互流程图
1️⃣ 订阅主题
复制代码
┌────────┐     SUB <topic>      ┌────────┐
│ 客户端 │ ───────────────────► │ Broker │
│  (C)   │                       │  (B)   │
└────────┘                       └────────┘
     ▲                                 │
     └─────────── SUBACK ──────────────┘
2️⃣ 发布消息(按QoS等级)
🔹 QoS 0(最多一次)
复制代码
┌────────┐     PUB 0 <topic> <msg>      ┌────────┐
│ 客户端 │ ───────────────────────────► │ Broker │
└────────┘                               └────────┘
(无确认,消息发出即忘)
🔸 QoS 1(至少一次)
复制代码
┌────────┐     PUB 1 <topic> <msg>      ┌────────┐
│ 客户端 │ ───────────────────────────► │ Broker │
└────────┘                               └────────┘
     ▲                                         │
     └────────────── ACK <msgId> ─────────────┘
🔹 QoS 2(仅一次)
复制代码
┌────────┐     PUB 2 <topic> <msg>      ┌────────┐
│ 客户端 │ ───────────────────────────► │ Broker │
└────────┘                               └────────┘
     ▲                                         │
     │            REC <msgId>                 │
     └────────────────────────────────────────┘
     
┌────────┐     REL <msgId>            ┌────────┐
│ 客户端 │ ───────────────────────────► │ Broker │
└────────┘                               └────────┘
     ▲                                         │
     │            COMP <msgId>                │
     └────────────────────────────────────────┘
3️⃣ Broker 推送消息给订阅者
复制代码
┌────────┐
│ Broker │
└────────┘
    │
    │ MSG <topic> <msg> (含msgId)
    ▼
┌────────┐
│ 客户端 │
└────────┘
    │
    │ ACK <msgId> (仅QoS 1/2)
    ▼
┌────────┐
│ Broker │
└────────┘

server代码

复制代码
package com.jysemel.iot;

import com.jysemel.iot.handler.SimpleBrokerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.StandardCharsets;

/**
 * QoS Broker - 简化版消息服务器
 *
 * 学习目标:
 * 1. 理解Pub/Sub(发布/订阅)模式
 * 2. 理解三种QoS级别的区别
 * 3. 掌握基本的消息确认机制
 *
 * 使用方法:
 * 1. 运行此程序启动Broker
 * 2. 运行QosClient连接到此Broker
 * 3. 使用SUB和PUB命令测试
 */
public class QosBroker {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("╔════════════════════════════════════╗");
        System.out.println("║     QoS Broker 启动中...          ║");
        System.out.println("║     端口: 8888                     ║");
        System.out.println("║     支持: QoS 0 / 1 / 2           ║");
        System.out.println("╚════════════════════════════════════╝\n");

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline p = ch.pipeline();
                            // 字符串解码和编码
                            p.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            p.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            // 业务处理器
                            p.addLast(new SimpleBrokerHandler());
                        }
                    });

            ChannelFuture future = bootstrap.bind(8888).sync();
            System.out.println("✓ Broker已就绪,等待客户端连接...\n");

            future.channel().closeFuture().sync();

        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

client代码

复制代码
package com.jysemel.iot;

import com.jysemel.iot.handler.SimpleClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class QosClient {
    private String host = "127.0.0.1";
    private int port = 8888;
    private Channel channel;
    private ScheduledExecutorService scheduler;
    private boolean autoTestMode = false;

    public static void main(String[] args) throws InterruptedException {
        QosClient client = new QosClient();
        client.autoTestMode = true;
        client.connect();
    }

    public void connect() throws InterruptedException {
        System.out.println("╔════════════════════════════════════╗");
        System.out.println("║     MQTT QoS 客户端               ║");
        System.out.println("║     连接到: " + host + ":" + port);
        if (autoTestMode) {
            System.out.println("║     模式: 自动测试                  ║");
        }
        System.out.println("╚════════════════════════════════════╝\n");
        EventLoopGroup group = new NioEventLoopGroup();
        scheduler = Executors.newScheduledThreadPool(2);
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            p.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            p.addLast(new SimpleClientHandler());
                        }
                    });
            ChannelFuture future = bootstrap.connect(host, port).sync();
            channel = future.channel();
            System.out.println("✅ 连接成功!\n");
            startAutoTest();
            channel.closeFuture().sync();
        } finally {
            if (scheduler != null) {
                scheduler.shutdown();
            }
            group.shutdownGracefully();
        }
    }

    private void startAutoTest() {
        System.out.println("🚀 启动自动测试模式...\n");

        scheduler.schedule(() -> {
            System.out.println("📝 [测试1]     主题订阅: news");
            sendCommand("SUB news");
        }, 2, TimeUnit.SECONDS);

//        scheduler.schedule(() -> {
//            System.out.println("📝 [测试2]     向服务端发布 QoS 0 消息");
//            sendCommand("PUB 0 news Hello_QoS0");
//        }, 20, TimeUnit.SECONDS);

        scheduler.schedule(() -> {
            System.out.println("📝 [测试3]     向服务端发布 QoS 1 消息");
            sendCommand("PUB 1 news Important_QoS1");
        }, 30, TimeUnit.SECONDS);
//
//        scheduler.schedule(() -> {
//            System.out.println("📝 [测试4]   向服务端发布 QoS 2 消息");
//            sendCommand("PUB 2 news Critical_QoS2");
//        }, 40, TimeUnit.SECONDS);
//
//        scheduler.schedule(() -> {
//            System.out.println("✅ 自动测试完成!");
//            if (channel != null && channel.isActive()) {
//                channel.close();
//            }
//        }, 50, TimeUnit.SECONDS);
    }

    private void sendCommand(String command) {
        channel.writeAndFlush(command + "\n");
        System.out.println("📤 已发送命令: " + command);
    }
}

运行结果

相关推荐
NiceCloud喜云3 小时前
Claude API 流式输出(SSE)实战:从打字机效果到工具调用全流程
java·前端·ide·人工智能·chrome·intellij-idea·状态模式
故事和你913 小时前
洛谷-【图论2-3】最小生成树2
开发语言·数据结构·c++·算法·动态规划·图论
甲方大人请饶命3 小时前
Java-IO流
java·开发语言
SimonKing3 小时前
别再死磕 Elasticsearch 了,这个轻量级搜索引擎更香
java·后端·程序员
asdfg12589633 小时前
一文理解“工程化思维”
java·编程思想
阿昌喜欢吃黄桃3 小时前
并发线程工具类分享
java·线程池·多线程·并发·juc
Rsun045513 小时前
try-with-resources跟try-catch-finally的区别
java
计算机安禾3 小时前
【c++面向对象编程】第35篇:构造函数与异常:如何避免资源泄露?
开发语言·javascript·c++·算法·性能优化
桀人3 小时前
类和对象——下
开发语言·c++