物联网 基于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();
        }
    }
}

验证结果

相关推荐
techdashen2 小时前
Rust 泛型 vs Java 泛型:它们看起来相似,但骨子里截然不同
java·开发语言·rust
一只旭宝2 小时前
【C加加入门精讲15】:IO流缓冲区、字符串流、缓冲流及STL vector容器零基础实战教程一、博客前言
开发语言·c++
銳昊城2 小时前
新大陆物联网竞赛经验谈
物联网·iot·新大陆物联网
振浩微433射频芯片2 小时前
工业环境下的“硬核”选择:如何科学评估国产433芯片的可靠性?
网络·人工智能·科技·单片机·物联网·学习
人道领域2 小时前
【LeetCode刷题日记】106.从遍历序列重建二叉树:手撕递归边界,彻底搞懂左闭右闭 vs 左闭右开
java·算法·leetcode
在坚持一下我可没意见2 小时前
Python 修仙修炼录 08:字典秘境,参悟键值玄机
开发语言·笔记·python·入门·字典
luck_bor2 小时前
Map&Stream流
java·开发语言
阿文的代码库2 小时前
如何在C++中使用标准库的智能指针
开发语言·c++·算法