¡Comienza el viaje de crecimiento de los Nuggets! Este es el tercer día de mi participación en el "Desafío de actualización de diciembre del nuevo plan diario de Nuggets", haga clic para ver los detalles del evento
escrito en frente
Esta es la segunda parte del aprendizaje del protocolo MQTT La parte anterior introdujo el formato de mensaje del protocolo MQTT a través de la formulación e implementación del protocolo de aprendizaje MQTT (1) .
Cabecera fija Cabecera fija, todos los paquetes de control contienen |
---|
Cabecera variable Cabecera variable, algunos paquetes de control contienen |
Carga útil Carga útil, parte del mensaje de control contiene |
Este artículo simplemente implementa un servidor MQTT a través de Netty, usa el cliente MQTTX para enviar mensajes de control y usa Wiresharks para capturar paquetes para analizar diferentes tipos de mensajes de control, encabezados fijos, encabezados variables y partes de carga útil.
Implementación sencilla del servidor MQTT
Definición del servidor Netty MQTT
Defina un servidor Netty MQTT, que se implementa a través de ServerBootstrap. Acerca de Netty, creo que escribiré un artículo sistemáticamente más adelante. Para los estudiantes que no conocen a Netty, primero pueden considerar a Netty como una caja negra y simplemente entenderlo como un Servidor que vincula el puerto 60000.
MqttEncoder.INSTANCE
Aquí nos enfocamos en inicializar el canal, agregar , MqttDecoder
, a la canalización del canal, mqttHander
codificar, decodificar y procesar la lógica comercial del flujo de bytes MQTT transmitido en el canal de red.
@Component
@RequiredArgsConstructor
public class MqttAgent {
?
private final MqttHandler mqttHandler;
private EventLoopGroup _bossGroup;
private EventLoopGroup _workGroup;
?
@PostConstruct
public void start() throws Exception {
try {
_bossGroup = new NioEventLoopGroup(1);
_workGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(_bossGroup, _workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast("mqttEncoder", MqttEncoder.INSTANCE);
ch.pipeline().addLast("mqttDecoder", new MqttDecoder());
ch.pipeline().addLast("mqttHandler", mqttHandler);
}
});
serverBootstrap.bind(
"0.0.0.0",
60000
).sync();
} catch (Exception e) {
try {
_bossGroup.shutdownGracefully();
_workGroup.shutdownGracefully();
} catch (Exception shutdownException) {
// ignore
}
// rethrow exception while agent does not start up normally.
throw e;
}
}
}
Implementación del controlador MQTT
MqttHandler maneja principalmente la lógica de procesamiento de los mensajes Mqtt, como verificar la contraseña de la cuenta de usuario y otros permisos cuando se establece la conexión. El procesamiento más simple aquí es imprimir el mensaje Mqtt y luego responder uno. El código es el siguiente MqttConnAckMessage
:
@Component
@Slf4j
@ChannelHandler.Sharable
public class MqttHandler extends SimpleChannelInboundHandler<MqttMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MqttMessage mqttMessage) throws Exception {
//打印mqtt 消息
log.info("mqtt message:{}", mqttMessage);
//回复 connect ack 控制报文
ctx.channel().writeAndFlush(connAckMessageInstance(MqttConnectReturnCode.CONNECTION_ACCEPTED));
}
?
//构造一个 MqttConnAckMessage
public static MqttConnAckMessage connAckMessageInstance(MqttConnectReturnCode returnCode) {
//连接确认标志+连接返回码(2位)
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false,
MqttQoS.AT_MOST_ONCE, false, 2);
MqttConnAckVariableHeader variableHeader = new MqttConnAckVariableHeader(returnCode, false);
return new MqttConnAckMessage(fixedHeader, variableHeader);
}
}
El mensaje MqttConnAckMessage solo tiene encabezados fijos y variables, y no tiene carga útil.
- Encabezado fijo: el valor del tipo de mensaje de control es 2, el bit de indicador es 0 y la longitud restante es un encabezado variable de dos bytes
- Encabezado variable: 2 bytes, indicador de confirmación de conexión de un byte, el segundo byte es el código de retorno de la conexión
programa de verificación
Utilice el cliente MQTTX para editar la información de conexión, host del servidor mqtt: 127.0.0.1, puerto: 60000 e inicie el servidor.
-
establecer conexión
-
La conexión es exitosa, envía un mensaje
El servidor recibió correctamente el mensaje.
2022-11-24 22:39:49.304 INFO 8299 --- [ntLoopGroup-3-1] com.demo.mqttdemo.MqttHandler: mqtt message:MqttFixedHeader[messageType=PUBLISH, isDup=false, qosLevel=AT_MOST_ONCE, isRetain=false, remainingLength=22]
?
Captura de paquetes de Wiresharks
Abra el cliente de Wiresharks, MQTTX envía una solicitud de conexión y verifique los paquetes capturados de Wiresharks: como se muestra en la figura, No61, No62, No63 son el famoso protocolo de enlace de tres vías, No67, No69 son el mensaje de control MQTT Connect y el mensaje de control ConnAck .
Conectar mensaje de control
Se puede ver en la parte de DATOS del mensaje.
Dos bytes de encabezado fijo: 10 1a, 10 es el tipo de mensaje de control de conexión, 1a es la longitud restante
El encabezado variable del mensaje CONNECT: Contiene cuatro campos en el siguiente orden: Nombre de protocolo, Nivel de protocolo, Indicadores de conexión y Mantener vivo.
00 04 4d 51 54 54 Estos 6 bytes fijos indican el nombre del protocolo MQTT
ilustrar | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
nombre del protocolo | |||||||||
byte 1 | Longitud MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | LongitudLSB (4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte 3 | 'METRO' | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte 4 | 'Q' | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte 5 | 'T' | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte 6 | 'T' | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
Mensaje de control de ConnAck
Encabezado fijo: 20 02 dos bytes indican el tipo de mensaje de control y la longitud restante, el formato del encabezado fijo de ConnAck es:
Poco | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
byte 2 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
Encabezado variable: 00 00 dos bytes representan el indicador de confirmación de la conexión y el código de retorno de la conexión, el código de retorno de la conexión recibida es 0x00, por lo que los dos bytes son 00 00