Netty服务端

public static void main(String[] args) {
// 监听端口号
int port = 8080;
// 构建主线程-用于分发socket请求
EventLoopGroup boosGroup = new NioEventLoopGroup(1);
// 构建工作线程-用于处理请求处理
EventLoopGroup workGroup = new NioEventLoopGroup(400);
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() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 这个地方注意,如果客户端发送请求体超过此设置值,会抛异常
socketChannel.pipeline().addLast(new MqttDecoder(1024*1024));
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);
}
}
package pro.nbbt.xulian.business.devicelog.service.impl;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.;
import io.netty.handler.codec.mqtt.
;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;

/**

  • @author: liyang

  • @date: 2020/7/29 13:22

  • @description: MQTT业务类
    */
    @Slf4j
    @ChannelHandler.Sharable
    public class TestMqttHandler extends ChannelInboundHandlerAdapter {
    private static final Collection clientList = new HashSet();
    private static final Map<String,Object> msgMap = new HashMap<>();

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof MqttMessage) {
    Channel channel = ctx.channel();
    if(!channel.isActive()){
    log.info("=未连接=");
    }
    log.info("============channel"+channel);
    MqttMessage message = (MqttMessage) msg;

    log.info("start=");
    log.info("=="+message);

    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;
    }
    }
    }

    /**

    • 创建连接时,需要响应对应的ACK包
    • @param channel:
    • @param msg:
    • @author liyang
    • @since 2022/3/13 23:12
      */
      public void connect(Channel channel, MqttConnectMessage msg) {
      //连接需要答复
      MqttConnAckMessage okResp = (MqttConnAckMessage) MqttMessageFactory.newMessage(new MqttFixedHeader(
      MqttMessageType.CONNACK, false, AT_LEAST_ONCE, false, 0),
      new MqttConnAckVariableHeader(MqttConnectReturnCode.CONNECTION_ACCEPTED, true), null);
      channel.writeAndFlush(okResp);
      clientList.add(channel);
      }

    /**

    • 响应ping心跳ACK包
    • @param channel:
    • @param msg:
    • @author liyang
    • @since 2022/3/13 23:13
      */
      public void pingReq(Channel channel, MqttMessage msg) {
      if (log.isDebugEnabled()) {
      log.debug("MQTT pingReq received.");
      }
      MqttMessage pingResp = new MqttMessage(new MqttFixedHeader(MqttMessageType.PINGRESP, false,
      AT_LEAST_ONCE, false, 0));
      channel.writeAndFlush(pingResp);
      }

    /**

    • 服务端主动断开连接
    • @param channel:
    • @param msg:
    • @author liyang
    • @since 2022/3/13 23:13
      */
      public void disConnect(Channel channel, MqttMessage msg) {
      clientList.remove(channel);
      if (channel.isActive()) {
      channel.close();
      if (log.isDebugEnabled()) {
      log.debug("MQTT channel '{}' was closed.", channel.id().asShortText());
      }
      }
      }

    /**

    • qos2中使用,发布确认
    • @param channel:
    • @param msg:
    • @author liyang
    • @since 2022/3/13 23:14
      */
      public void puback(Channel channel, MqttMessage msg){

    }
    /**

    • qos2中发布释放ACK包

    • @param channel:

    • @param msg:

    • @author liyang

    • @since 2022/3/13 23:13
      */
      public void pubrel(Channel channel, MqttMessage msg){
      Object mqttMessageIdVariableHeader = msg.variableHeader();
      if (mqttMessageIdVariableHeader instanceof MqttPubReplyMessageVariableHeader) {
      log.info("qos2客户端返回确认的消息:{}",msg);

      // qos2类型,接收发布者消息
      log.info("qos2客户端返回确认的消息包:{}",msg.payload());
      MqttPubReplyMessageVariableHeader header = (MqttPubReplyMessageVariableHeader) mqttMessageIdVariableHeader;
      MqttMessage mqttMessage = MqttMessageFactory.newMessage(
      new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.EXACTLY_ONCE, false, 0),
      MqttMessageIdVariableHeader.from(header.messageId()),
      0);
      // channel.writeAndFlush(mqttMessage);
      for (Channel channel1 : clientList) {
      try {
      send(channel1,"aaa",MqttQoS.EXACTLY_ONCE,"我收到那");
      } catch (InterruptedException e) {
      log.error("该通道推送消息失败,可加入容错机制,channel:{}",channel1);
      }
      }
      }
      }
      /**

    • qos2:发布收到ACK包

    • @param channel:

    • @param msg:

    • @author liyang

    • @since 2022/3/14 0:09
      */
      public void pubrec(Channel channel, MqttMessage msg) throws InterruptedException {
      Object mqttMessageIdVariableHeader = msg.variableHeader();
      if (mqttMessageIdVariableHeader instanceof MqttPubReplyMessageVariableHeader) {
      // qos2类型,接收发布者消息
      log.info("qos2客户端返回确认的消息包:{}",msg.payload());
      MqttPubReplyMessageVariableHeader header = (MqttPubReplyMessageVariableHeader) mqttMessageIdVariableHeader;
      MqttMessage mqttMessage = MqttMessageFactory.newMessage(
      new MqttFixedHeader(MqttMessageType.PUBREL, false, MqttQoS.EXACTLY_ONCE, false, 0),
      MqttMessageIdVariableHeader.from(header.messageId()),
      0);
      channel.writeAndFlush(mqttMessage).sync();

      }
      }
      /**

    • qos2发布完成

    • @param channel:

    • @param msg:

    • @author liyang

    • @since 2022/3/14 0:11
      */
      public void pubcomp(Channel channel, MqttMessage msg) {
      Object mqttMessageIdVariableHeader = msg.variableHeader();
      if (mqttMessageIdVariableHeader instanceof MqttPubReplyMessageVariableHeader) {
      log.info("qos2收到消息时间戳:{}",System.currentTimeMillis());

      // qos2类型,接收发布者消息
      // log.info("qos2客户端返回确认的消息包:{}",msg.payload());
      // MqttPubReplyMessageVariableHeader header = (MqttPubReplyMessageVariableHeader) mqttMessageIdVariableHeader;
      // MqttMessage mqttMessage = MqttMessageFactory.newMessage(
      // new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.EXACTLY_ONCE, false, 0),
      // MqttMessageIdVariableHeader.from(header.messageId()),
      // 0);
      // channel.writeAndFlush(mqttMessage);
      }
      }

    /**

    • 客户端发布消息时使用

    • @param channel:

    • @param msg:

    • @author liyang

    • @since 2022/3/13 23:14
      */
      public void publish(Channel channel, MqttPublishMessage msg) {

      log.info("qos类型是{}",msg.fixedHeader().qosLevel());
      String topic = msg.variableHeader().topicName();
      log.info("订阅主题:{}",topic);
      ByteBuf buf = msg.content().duplicate();
      byte[] tmp = new byte[buf.readableBytes()];
      buf.readBytes(tmp);
      String content = null;
      try {
      content = new String(tmp,"UTF-8");
      } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
      }
      //校验传入的数据是否符合要求
      if(StringUtils.isBlank(content)){
      log.error("MQTT接收到的数据包为空===》{}",content);
      puback(channel,msg,"MQTT接收到的数据包为空");
      return;
      }

      log.info("MQTT读取到的客户端发送信息===>{}",content);
      // 如果是qos1或者qos2类型都需要响应
      puback(channel,msg,content);
      log.info("推送客户端客户端消息时间戳:{}",System.currentTimeMillis());

      // 推送主题消息
      log.info("推送客户端客户端消息:{}",content);
      if (AT_LEAST_ONCE == msg.fixedHeader().qosLevel() || AT_MOST_ONCE == msg.fixedHeader().qosLevel()) {
      for (Channel channel1 : clientList) {
      try {
      send(channel1,topic,msg.fixedHeader().qosLevel(),content);
      } catch (InterruptedException e) {
      log.error("该通道推送消息失败,可加入容错机制,channel:{}",channel1);
      }
      }
      }
      }

    /**

    • 客户端订阅消息ACK包
    • @param channel:
    • @param msg:
    • @author liyang
    • @since 2022/3/13 23:14
      */
      public void subscribe(Channel channel, MqttSubscribeMessage msg) {
      MqttQoS mqttQoS = msg.fixedHeader().qosLevel();
      // mqttQoS = MqttQoS.EXACTLY_ONCE;
      MqttSubAckMessage subAckMessage = (MqttSubAckMessage) MqttMessageFactory.newMessage(
      new MqttFixedHeader(MqttMessageType.SUBACK, false, mqttQoS, false, 0),
      MqttMessageIdVariableHeader.from(msg.variableHeader().messageId()),
      new MqttSubAckPayload(0));
      channel.writeAndFlush(subAckMessage);
      }

    /**

    • 客户端取消订阅ACK包

    • @param channel:

    • @param msg:

    • @author liyang

    • @since 2022/3/13 23:15
      */
      public void unSubscribe(Channel channel, MqttUnsubscribeMessage msg) {

      MqttUnsubAckMessage unSubAckMessage = (MqttUnsubAckMessage) MqttMessageFactory.newMessage(
      new MqttFixedHeader(MqttMessageType.UNSUBACK, false, AT_LEAST_ONCE, false, 0),
      MqttMessageIdVariableHeader.from(msg.variableHeader().messageId()), null);
      channel.writeAndFlush(unSubAckMessage);
      disConnect(channel,msg);
      }

    /**

    • 捕获异常状态,客户端断开钩子函数
    • @param ctx:
    • @param cause:
    • @author liyang
    • @since 2022/3/13 23:15
      */
      @Override
      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
      log.error("MQTT客户端被强制关闭:{}:{}",ctx.channel().id().asShortText(),cause);
      if (ctx.channel().isActive()) {
      ctx.channel().writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
      ctx.channel().close();
      }
      }

    /**

    • qos1中响应客户端ACK包

    • @param channel:

    • @param msg:

    • @param payLoad:

    • @author liyang

    • @since 2022/3/13 23:16
      */
      // 客户端QOS1消息类型( MqttQoS.AT_LEAST_ONCE = qos1),需要服务器响应包
      private void puback(Channel channel, MqttPublishMessage msg, String payLoad){

      if (MqttQoS.AT_MOST_ONCE == msg.fixedHeader().qosLevel()) {
      // qos0消息类型,不需要ACK客户端
      return;
      }
      if (MqttQoS.AT_LEAST_ONCE == msg.fixedHeader().qosLevel()) {
      // qos1消息类型,需要向客户端返回MqttMessageType.PUBACK 类型ACK应答
      MqttPubAckMessage sendMessage = (MqttPubAckMessage) MqttMessageFactory.newMessage(
      new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_LEAST_ONCE, false, 0),
      MqttMessageIdVariableHeader.from(msg.variableHeader().packetId()),
      payLoad);
      channel.writeAndFlush(sendMessage);
      return;
      }
      if (MqttQoS.EXACTLY_ONCE == msg.fixedHeader().qosLevel()) {
      // qos2消息类型
      MqttMessage mqttMessage = MqttMessageFactory.newMessage(
      new MqttFixedHeader(MqttMessageType.PUBREC, false, MqttQoS.EXACTLY_ONCE, false, 0),
      MqttMessageIdVariableHeader.from(msg.variableHeader().packetId()),
      payLoad);

      channel.writeAndFlush(mqttMessage);
      }
      }

    /**

    • 向客户端发布订阅主题消息

    • @param channel:

    • @param topic:

    • @param qos:

    • @param sendMessage:

    • @return

    • @author liyang

    • @since 2022/3/13 23:16
      */
      public void send(Channel channel, String topic,MqttQoS qos ,String sendMessage ) throws InterruptedException {
      ChannelFuture channelFuture=null;
      MqttRequest request = new MqttRequest((sendMessage.getBytes()));
      MqttPublishMessage pubMessage = (MqttPublishMessage) MqttMessageFactory.newMessage(
      new MqttFixedHeader(MqttMessageType.PUBLISH,
      request.isDup(),
      qos,
      request.isRetained(),
      0),
      new MqttPublishVariableHeader(topic, 0),
      Unpooled.buffer().writeBytes(request.getPayload()));
      // msgMap.put(pubMessage.variableHeader().messageId()+"",pubMessage.variableHeader().messageId()+"");
      // 超过高水位,则采取同步模式
      if (channel.isWritable()) {
      log.info("=进入高水平");
      log.info("
      =进入高水平"+channel);

      channel.writeAndFlush(pubMessage).sync();

      }else {
      channel.writeAndFlush(pubMessage).addListener((ChannelFuture future) -> {
      if (!future.isSuccess()) {
      // 处理错误
      Throwable cause = future.cause();
      cause.printStackTrace();
      }
      });
      }
      // return channelFuture;
      }
      }

package pro.nbbt.xulian.business.devicelog.service.impl;

import io.netty.handler.codec.mqtt.MqttQoS;

/**

  • @author: liyang

  • @date: 2020-11-04 15:58

  • @description: 请求消息体
    **/
    public class MqttRequest {
    private boolean mutable = true;
    private byte[] payload;
    private MqttQoS qos = MqttQoS.AT_LEAST_ONCE;
    private boolean retained = false;
    private boolean dup = false;
    private int messageId;

    public MqttRequest() {
    this.setPayload(new byte[0]);
    }

    public MqttRequest(byte[] payload) {
    this.setPayload(payload);
    }
    public MqttRequest(byte[] payload,MqttQoS qos) {
    this.setPayload(payload);
    this.setQos(qos);
    }

    public byte[] getPayload() {
    return this.payload;
    }

    public void clearPayload() {
    this.checkMutable();
    this.payload = new byte[0];
    }

    public void setPayload(byte[] payload) {
    this.checkMutable();
    if (payload == null) {
    throw new NullPointerException();
    } else {
    this.payload = payload;
    }
    }

    public boolean isRetained() {
    return this.retained;
    }

    public void setRetained(boolean retained) {
    this.checkMutable();
    this.retained = retained;
    }

    public MqttQoS getQos() {
    return qos;
    }

    public void setQos(MqttQoS qos) {
    this.qos = qos;
    }

    public boolean isMutable() {
    return mutable;
    }

    public void setMutable(boolean mutable) {
    this.mutable = mutable;
    }

    protected void checkMutable() throws IllegalStateException {
    if (!this.mutable) {
    throw new IllegalStateException();
    }
    }

    public boolean isDup() {
    return dup;
    }

    public void setDup(boolean dup) {
    this.dup = dup;
    }

    public int getMessageId() {
    return messageId;
    }

    public void setMessageId(int messageId) {
    this.messageId = messageId;
    }

    @Override
    public String toString() {
    return new String(this.payload);
    }
    }

posted @ 2025-07-30 09:20  kevinWwm  阅读(21)  评论(0)    收藏  举报