基于Netty实现 mqtt服务器

Mqtt 应用 

物联网和手机app通信等。介绍 略

基于Netty实现 mqtt

  1. Mqtt 有发布订阅。怎么通过netty 实现?

  2. Netty 怎么捕获连接事件。

  3. 基于Netty channel Session管理?

  4. Topic 和 channel之间的关系

  5. 消息 Qos 等级
  6. 怎么集群。

简单的Mqtt 看实现

public static void main(String[] args) {
        MqttTsServer server = new MqttTsServer();
        server.run();
    }

    public void run(){
        // 监听端口号
        int port = 1883;
        // 构建主线程-用于分发socket请求
        EventLoopGroup boosGroup = new NioEventLoopGroup(1);
        // 构建工作线程-用于处理请求处理
        EventLoopGroup workGroup = new NioEventLoopGroup(4);
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boosGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
//                    .childOption(ChannelOption.SO_BACKLOG,1024)     //等待队列
                    .childOption(ChannelOption.SO_REUSEADDR,true)   //快速复用
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 这个地方注意,如果客户端发送请求体超过此设置值,会抛异常
                            socketChannel.pipeline().addLast(new MqttDecoder(1024*1024));
                            //增加Mqtt的 Decoder and Encoder
                            socketChannel.pipeline().addLast( MqttEncoder.INSTANCE);
                            // 加载MQTT编解码协议,包含业务逻辑对象
                            socketChannel.pipeline().addLast(new TestMqttHandler());
                        }
                    });
            
            //启动服务
            serverBootstrap.bind(port).addListener(future -> {
                log.info("服务端成功绑定端口号={}",port);
            });
        }catch (Exception e){
            boosGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
            log.error("mqttServer启动失败:{}",e);
        }
    }

启动类看,初始化channel 增加了MQTT的协议。

注意增加了一个TestMqttHandler() 重要。所有的Mqtt处理都是通过这个Handler

Handler处理类分析

@Slf4j
@ChannelHandler.Sharable
public class TestMqttHandler extends ChannelInboundHandlerAdapter {
    private static final Collection<Channel> clientList = new HashSet();
    private static final Map<String,Object> msgMap = new HashMap<>();
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
       super.channelUnregistered(ctx);
       protocolProcess.disConnect().processDisConnect(ctx.channel(), null);
       logger.info("------disconnection------非正常关闭的连接");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof MqttMessage) {
            Channel channel = ctx.channel();
            MqttMessage message = (MqttMessage) msg;

            MqttMessageType messageType = message.fixedHeader().messageType();
            log.info("MQTT接收到的发送类型===》{}",messageType);

            switch (messageType) {
                // 建立连接
                case CONNECT:
                    try {
                        this.connect(channel, (MqttConnectMessage) message);
                    }catch (Exception e){
                        //如果用户密码,客户端ID校验不成功,会二次建立CONNECT类型连接
                        //但是没有实际意义
                    }
                    break;
                // 发布消息
                case PUBLISH:
                    this.publish(channel, (MqttPublishMessage) message);
                    break;
                // 订阅主题
                case SUBSCRIBE:
                    this.subscribe(channel, (MqttSubscribeMessage) message);
                    break;
                // 退订主题
                case UNSUBSCRIBE:
                    this.unSubscribe(channel, (MqttUnsubscribeMessage) message);
                    break;
                // 心跳包
                case PINGREQ:
                    this.pingReq(channel, message);
                    break;
                // 断开连接
                case DISCONNECT:
                    this.disConnect(channel, message);
                    break;
                // 确认收到响应报文,用于服务器向客户端推送qos1/qos2后,客户端返回服务器的响应
                case PUBACK:
                    this.puback(channel,  message);
                    break;
                // qos2类型,发布收到
                case PUBREC:
                    this.pubrec(channel, message);
                    break;
                // qos2类型,发布释放响应
                case PUBREL:
                    this.pubrel(channel, message);
                    break;
                // qos2类型,发布完成
                case PUBCOMP:
                    this.pubcomp(channel, message);
                    break;
                default:
                    if (log.isDebugEnabled()) {
                        log.debug("Nonsupport server message  type of '{}'.", messageType);
                    }
                    break;
            }
        }
    }

继承extends ChannelInboundHandlerAdapter 类 重写channelRead(ChannelHandlerContext ctx, Object msg)

通过判断 if (msg instanceof MqttMessage) { 。。。switch (messageType) { 分析消息事件

  1. 建立连接事件,需要将channel放到List中即Session管理channel.writeAndFlush(okResp); clientList.add(channel); channel需要writeAndFlush 输出应答。

  2. 输出应答的枚举 io.netty.handler.codec.mqtt.MqttConnectReturnCode

  3. MqttMessage 枚举事件 io.netty.handler.codec.mqtt.MqttMessageType

CONNECT(1), CONNACK(2), PUBLISH(3)。。。

连接connect

InetSocketAddress address = (InetSocketAddress)contextBo.getHandlerContext().channel().remoteAddress();
String host = address.getAddress().getHostAddress();
String clientId = msg.payload().clientIdentifier();
String username = msg.payload().userName();
String password = msg.payload().passwordInBytes() == null ? null : new String(msg.payload().passwordInBytes(), CharsetUtil.UTF_8);
  if (!authManager.checkValid(clientId, username, passw
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值