物联网 基于netty构建mqtt协议规范(发布/订阅模式)

物联网 基于netty构建mqtt协议规范(发布/订阅模式)

简述

提供一对多的消息分发和消息生产/消费方的解耦,发布者无需知道订阅者的存在,两者通过 Broker 间接通信

源码(netty-sample-03-PubSub)

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

核心角色与工作流程

角色 职责
订阅者(Subscriber) 向 Broker 发送 SUBSCRIBE 请求,注册对某个主题的兴趣;接收来自 Broker 的推送消息。
发布者(Publisher) 向 Broker 发送 PUBLISH 请求,将消息发送到指定主题。
Broker(代理) 维护主题与订阅者列表的映射;接收发布的消息,并转发给对应主题的所有订阅者。

整体流程

复制代码
┌─────────────┐         ┌──────────────┐         ┌──────────────┐
│  Publisher  │ ──────► │   Broker     │ ◄────── │ Subscriber   │
│  (8888)     │  发布    │  (消息总线)   │  订阅    │  (9999)      │
└─────────────┘         └──────────────┘         └──────────────┘
                        端口: 7777

sub代码

复制代码
package com.jysemel.iot.subscriber;

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.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class SubscriberServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();

        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 LineBasedFrameDecoder(1024));
                            p.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            p.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            p.addLast(new SubscriberHandler());
                        }
                    });

            ChannelFuture future = bootstrap.connect("127.0.0.1", 7777).sync();

            System.out.println("===========================================");
            System.out.println("   Subscriber 客户端已启动");
            System.out.println("===========================================");
            System.out.println("已连接到 Broker: 127.0.0.1:7777");
            System.out.println("功能: 订阅主题并接收消息");
            System.out.println("命令格式: SUB <topic>");
            System.out.println("示例: SUB news");
            System.out.println("输入 'quit' 退出");
            System.out.println("===========================================\n");

            Channel channel = future.channel();
            Scanner scanner = new Scanner(System.in);

            while (true) {
                String input = scanner.nextLine();
                if ("quit".equalsIgnoreCase(input)) {
                    break;
                }
                channel.writeAndFlush(input + "\n");
            }

        } finally {
            group.shutdownGracefully();
        }
    }
}

broker代码

复制代码
package com.jysemel.iot.broker;

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.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.StandardCharsets;

public class BrokerServer {

    public void start() throws InterruptedException {
        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 LineBasedFrameDecoder(1024));
                            p.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            p.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            p.addLast(new BrokerHandler());
                        }
                    });

            ChannelFuture future = bootstrap.bind(7777).sync();
            System.out.println("===========================================");
            System.out.println("   Broker 服务器启动");
            System.out.println("===========================================");
            System.out.println("端口: 7777");
            System.out.println("功能: 消息路由和分发(充当 Redis 角色)");
            System.out.println("支持命令:");
            System.out.println("  - PUB <topic> <message>  : 发布消息");
            System.out.println("  - SUB <topic>            : 订阅主题");
            System.out.println("  - UNSUB <topic>          : 取消订阅");
            System.out.println("===========================================\n");

            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new BrokerServer().start();
    }
}

pub代码

复制代码
package com.jysemel.iot.publisher;

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.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class PublisherServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();

        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 LineBasedFrameDecoder(1024));
                            p.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            p.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            p.addLast(new PublisherHandler());
                        }
                    });

            ChannelFuture future = bootstrap.connect("127.0.0.1", 7777).sync();

            System.out.println("===========================================");
            System.out.println("   Publisher 客户端已启动");
            System.out.println("===========================================");
            System.out.println("已连接到 Broker: 127.0.0.1:7777");
            System.out.println("功能: 发布消息到指定主题");
            System.out.println("命令格式: PUB <topic> <message>");
            System.out.println("示例: PUB news Hello World");
            System.out.println("输入 'quit' 退出");
            System.out.println("===========================================\n");

            Channel channel = future.channel();
            Scanner scanner = new Scanner(System.in);

            while (true) {
                String input = scanner.nextLine();
                if ("quit".equalsIgnoreCase(input)) {
                    break;
                }
                channel.writeAndFlush(input + "\n");
            }

        } finally {
            group.shutdownGracefully();
        }
    }
}

验证结果

相关推荐
xieliyu.2 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
明夜之约2 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee2 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Jinkxs2 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
辣机小司2 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录
CryptoPP3 小时前
快速对接东京证券交易所API数据:实战指南与代码示例
开发语言·人工智能·windows·python·信息可视化·区块链
ZC跨境爬虫3 小时前
跟着 MDN 学JavaScript day_7:数学运算与逻辑判断实战测试
开发语言·前端·javascript·学习·ecmascript
fangdengfu1233 小时前
ES分析系统各个服务日志占用量
java·前端·elasticsearch
云烟成雨TD3 小时前
Spring AI 1.x 系列【51】可观测性技术选型
java·人工智能·spring
星越华夏3 小时前
ESP32-CAM图像传输项目说明文档
java·后端·struts·esp32