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 CollectionclientList = 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);
}
}
浙公网安备 33010602011771号