I. Introducción
La mayoría de los blogs anteriores introdujeron middleware basado en EMQ, el protocolo de comunicación usa MQTT y los datos transmitidos son datos de texto sin formato en formato JSON. De esta manera, a la mayoría de las personas que están familiarizadas con el desarrollo web y el desarrollo de software les gusta usarlo a primera vista. Dado que también estoy desarrollando software web, prefiero este enfoque. La plataforma IoT de Ali también recomienda este enfoque. Sin embargo, si está acostumbrado al desarrollo de hardware, el desarrollo integrado prefiere utilizar una conexión TCP-Socket desnuda. Se utiliza el protocolo binario. En base a esto, en la mayoría de las aplicaciones, para ser compatible con dispositivos antiguos, es necesario desarrollar una puerta de enlace de servidor TCP separada. Aquí está el marco de trabajo de Netty más popular que he aprendido antes.
No hay mucho que decir, empecemos.
2. Acuerdo
definición |
descripción |
|
Carácter de inicio '@@' (2 bytes) |
El primer y segundo bytes del paquete de datos son valores fijos de 64 y 64. |
|
unidad de control
|
Número de serie comercial (2 bytes) |
El tercer y cuarto bytes del paquete de datos. En el modo de envío / confirmación, el remitente incrementa el número de serie del servicio en uno en orden al enviar un nuevo paquete de datos, y el confirmador regresa de acuerdo con el número de serie del servicio del paquete enviado; en el modo de solicitud / respuesta, el solicitante envía el número de serie del servicio en el nuevo paquete de datos. Cuando el comando de solicitud se agrega en uno en orden, el respondedor regresa de acuerdo con el número de serie comercial del paquete de solicitud. El byte bajo se transmite primero. El número de serie del servicio es un entero positivo de 2 bytes, que se determina cuando las partes de la comunicación establecen una conexión de red por primera vez. El valor inicial es 0. El iniciador del servicio gestiona de forma independiente el número de serie del servicio (el iniciador del servicio se refiere al remitente en el modo de envío / confirmación o al solicitante en el modo de solicitud / respuesta). El iniciador comercial es responsable de la asignación y recuperación del número de serie comercial para garantizar la unicidad del número de serie comercial durante la duración del negocio. |
Número de versión del protocolo (2 bytes) |
El número de versión del protocolo incluye el número de versión principal (el quinto byte) y el número de versión del usuario (el sexto byte). El número de versión principal es un valor fijo de 1 y el número de versión de usuario lo define el usuario. |
|
Sello de tiempo (6 bytes) |
Los bytes 7 a 12 del paquete de datos son el momento en que se envía el paquete de datos, como se define en la Tabla 2. |
|
Dirección de la fuente (6 bytes) |
Los bytes 13 al 18 del paquete de datos son la dirección de origen del paquete de datos (la dirección del centro de monitoreo o del dispositivo de transmisión de información del usuario). El byte bajo se transmite primero. |
|
Dirección de destino (6 bytes) |
Los bytes 19 al 24 del paquete de datos son la dirección de destino del paquete de datos (la dirección del centro de monitoreo o del dispositivo de transmisión de información del usuario). El byte bajo se transmite primero. |
|
Longitud de la unidad de datos de aplicación (2 bytes) |
Los bytes 25 y 26 del paquete de datos son la longitud de la unidad de datos de la aplicación, y la longitud no debe ser superior a 1024; el byte bajo se transmite primero. |
|
Byte de comando (1 byte) |
El byte 27 del paquete de datos es el byte de comando de la unidad de control Consulte la Tabla 3 para obtener definiciones específicas. |
|
Unidad de datos de aplicación (Máximo 1024 bytes) |
El formato básico de la unidad de datos de la aplicación se muestra en la Tabla 3. Para paquetes de comando como confirmación / rechazo, esta unidad puede estar vacía. |
|
Suma de comprobación (1 byte) |
La suma de verificación aritmética de cada byte de datos en la unidad de control (del 3º al 27º byte) y la unidad de datos de la aplicación es un número binario de 1 byte formado descartando más de 8 bits de acarreo. |
|
Carácter final '##' (2 bytes) |
El valor fijo es 35,35. |
Lo anterior es el formato de datos binarios que debe procesarse esta vez.
Tres, la parte del código
3,0 pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.1.1.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.wunaozai.iot.nettyplatform</groupId> 12 <artifactId>NettyPlatform</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>IoTNettyPlatForm</name> 15 <description>基于自定义协议,使用Netty,物联网通信平台</description> 16 17 <properties> 18 <java.version>1.8</java.version> 19 </properties> 20 21 <dependencies> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter</artifactId> 25 </dependency> 26 27 <!-- https://mvnrepository.com/artifact/io.netty/netty-all --> 28 <dependency> 29 <groupId>io.netty</groupId> 30 <artifactId>netty-all</artifactId> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-configuration-processor</artifactId> 35 <optional>true</optional> 36 </dependency> 37 38 <!-- web项目必要的依赖 --> 39 <dependency> 40 <groupId>org.springframework.boot</groupId> 41 <artifactId>spring-boot-starter-web</artifactId> 42 </dependency> 43 44 <!-- 热启动devtools --> 45 <dependency> 46 <groupId>org.springframework.boot</groupId> 47 <artifactId>spring-boot-devtools</artifactId> 48 <optional>true</optional> 49 <scope>true</scope> 50 </dependency> 51 52 <dependency> 53 <groupId>org.springframework.boot</groupId> 54 <artifactId>spring-boot-starter-test</artifactId> 55 <scope>test</scope> 56 </dependency> 57 </dependencies> 58 59 <build> 60 <plugins> 61 <plugin> 62 <groupId>org.springframework.boot</groupId> 63 <artifactId>spring-boot-maven-plugin</artifactId> 64 <configuration> 65 <fork>true</fork> 66 </configuration> 67 </plugin> 68 </plugins> 69 </build> 70 71 </project>
3.1 SmartIotProtocol.java
这个主要对通信协议模型进行简单封装
1 package com.wunaozai.iot.nettyplatform.code; 2 3 /** 4 * 自定义协议 5 * @author Administrator 6 * @see https://www.cnblogs.com/sidesky/p/6913109.html 7 */ 8 public class SmartIotProtocol { 9 10 /** 11 * 协议最短长度 30 字节 12 */ 13 public static int MIN_LEN = 30; 14 15 /** 16 * 数据包启动符号 @@ 17 */ 18 public static short START = 25700; 19 20 /** 21 * 业务流水号 22 */ 23 private short flowid; 24 /** 25 * 主版本 26 */ 27 private byte version_major; 28 /** 29 * 次版本 30 */ 31 private byte version_minor; 32 /** 33 * 秒 34 */ 35 private byte second; 36 /** 37 * 分钟 38 */ 39 private byte minute; 40 /** 41 * 小时 42 */ 43 private byte hour; 44 /** 45 * 日 46 */ 47 private byte day; 48 /** 49 * 月 50 */ 51 private byte month; 52 /** 53 * 年 54 */ 55 private byte year; 56 /** 57 * 数据包的源地址 58 */ 59 private byte[] src; 60 /** 61 * 数据包的目的地址 62 */ 63 private byte[] dest; 64 /** 65 * 应用数据单元长度 长度不应大于1024;低字节传输在前 66 */ 67 private short data_len; 68 /** 69 * 命令字节 为控制单元的命令字节 70 */ 71 private byte cmd; 72 /** 73 * 应用数据单元 对于确认/否认等命令包,此单元可为空 74 */ 75 private byte[] data; 76 /** 77 * 校验和 控制单元中各字节数据(第3~第27字节)及应用数据单元的算术校验和,舍去8位以上的进位位后所形成的1字节二进制数 78 */ 79 private byte checksum; 80 /** 81 * 协议结束符号 ## 82 */ 83 public static short END = 13621; 84 85 /** 86 * 打印调试信息 87 */ 88 public void printDebugInfo(){ 89 System.out.println("---------完整数据包开始------------"); 90 System.out.println("|开始标志: " + printHexShort(START)); 91 System.out.println("|业务流水: " + printHexShort(flowid) + "\tFlowID:" + flowid); 92 System.out.println("|协议版本: " + printHexByte(version_major) + printHexByte(version_minor)); 93 System.out.println("|时间标签: " + "20" + year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second); 94 System.out.println("|源地址 : " + printHexBytes(src)); 95 System.out.println("|目的地址: " + printHexBytes(dest)); 96 System.out.println("|数据长度: " + data_len); 97 System.out.println("|命令字节: " + printHexByte(cmd)); 98 System.out.println("|应用数据: " + printHexBytes(data)); 99 System.out.println("|校验字节: " + printHexByte(checksum)); 100 System.out.println("|结束标志: " + printHexShort(END)); 101 System.out.println("---------------------------------"); 102 } 103 private String printHexByte(byte b){ 104 return String.format("%02X", b); 105 } 106 private String printHexBytes(byte[] bytes){ 107 String str = ""; 108 for(int i=0; i<bytes.length; i++){ 109 str += String.format("%02X", bytes[i]); 110 } 111 return str; 112 } 113 private String printHexShort(int s){ 114 byte[] bytes = hexShort(s); 115 return printHexBytes(bytes); 116 } 117 private byte[] hexShort(int s){ 118 byte[] bytes = new byte[2]; 119 bytes[0] = (byte)((s << 24) >> 24); 120 bytes[1] = (byte)((s << 16) >> 24); 121 return bytes; 122 } 123 private byte[] hexInt(int n){ 124 byte[] bytes = new byte[4]; 125 bytes[3] = (byte) ((n ) >> 24); 126 bytes[2] = (byte) ((n << 8) >> 24); 127 bytes[1] = (byte) ((n << 16) >> 24); 128 bytes[0] = (byte) ((n << 24) >> 24); 129 return bytes; 130 } 131 132 public short getFlowid() { 133 return flowid; 134 } 135 public void setFlowid(short flowid) { 136 this.flowid = flowid; 137 } 138 public byte getVersion_major() { 139 return version_major; 140 } 141 public void setVersion_major(byte version_major) { 142 this.version_major = version_major; 143 } 144 public byte getVersion_minor() { 145 return version_minor; 146 } 147 public void setVersion_minor(byte version_minor) { 148 this.version_minor = version_minor; 149 } 150 public byte getSecond() { 151 return second; 152 } 153 public void setSecond(byte second) { 154 this.second = second; 155 } 156 public byte getMinute() { 157 return minute; 158 } 159 public void setMinute(byte minute) { 160 this.minute = minute; 161 } 162 public byte getHour() { 163 return hour; 164 } 165 public void setHour(byte hour) { 166 this.hour = hour; 167 } 168 public byte getDay() { 169 return day; 170 } 171 public void setDay(byte day) { 172 this.day = day; 173 } 174 public byte getMonth() { 175 return month; 176 } 177 public void setMonth(byte month) { 178 this.month = month; 179 } 180 public byte getYear() { 181 return year; 182 } 183 public void setYear(byte year) { 184 this.year = year; 185 } 186 public byte[] getSrc() { 187 return src; 188 } 189 public void setSrc(byte[] src) { 190 this.src = src; 191 } 192 public byte[] getDest() { 193 return dest; 194 } 195 public void setDest(byte[] dest) { 196 this.dest = dest; 197 } 198 public short getData_len() { 199 return data_len; 200 } 201 public void setData_len(short data_len) { 202 this.data_len = data_len; 203 } 204 public byte getCmd() { 205 return cmd; 206 } 207 public void setCmd(byte cmd) { 208 this.cmd = cmd; 209 } 210 public byte[] getData() { 211 return data; 212 } 213 public void setData(byte[] data) { 214 this.data = data; 215 } 216 public byte getChecksum() { 217 return checksum; 218 } 219 public void setChecksum(byte checksum) { 220 this.checksum = checksum; 221 } 222 223 }
3.2 SmartIotDecoder.java
解码器,这个是本次的重点,这个解码器最主要是解决TCP粘包拆包问题,如果有不清楚的,要重点理解一下。
1 package com.wunaozai.iot.nettyplatform.code; 2 3 import java.util.List; 4 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 8 import io.netty.buffer.ByteBuf; 9 import io.netty.channel.ChannelHandlerContext; 10 import io.netty.handler.codec.ByteToMessageDecoder; 11 12 /** 13 * 自定义协议解析 14 * @author Administrator 15 * 16 */ 17 public class SmartIotDecoder extends ByteToMessageDecoder { 18 19 20 private static final Logger log = LoggerFactory.getLogger(SmartIotDecoder.class); 21 22 @Override 23 protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception { 24 log.debug("启动解码器..."); 25 log.debug("目前数据缓存大小: " + buffer.readableBytes()); 26 // 刻度长度必须大于基本最小长度 27 if(buffer.readableBytes() >= SmartIotProtocol.MIN_LEN){ 28 log.debug("符合最小长度,进行解析"); 29 //防止socket字节流攻击、客户端传来的数据过大,这里需要对数据进行过滤掉 30 if(buffer.readableBytes() >= 4096){ 31 buffer.skipBytes(buffer.readableBytes()); 32 return ; 33 } 34 35 //记录包头开始位置 36 int beginReader = 0; 37 while(true){ 38 beginReader = buffer.readerIndex(); //记录包头开始位置 39 buffer.markReaderIndex(); //标记包头开始index 40 //读取协议开始标志 41 if(buffer.readShort() == SmartIotProtocol.START){ 42 break; //如果是开始标记,那么就结束查找 43 } 44 45 //如果找不到包头,这里要一个一个字节跳过 46 buffer.resetReaderIndex(); 47 buffer.readByte(); 48 49 //当跳过后,如果数据包又不符合长度的,结束本次协议解析 50 if(buffer.readableBytes() < SmartIotProtocol.MIN_LEN){ 51 return ; 52 } 53 } 54 55 short flowid = buffer.readShort(); 56 byte version_major = buffer.readByte(); 57 byte version_minor = buffer.readByte(); 58 byte second = buffer.readByte(); 59 byte minute = buffer.readByte(); 60 byte hour = buffer.readByte(); 61 byte day = buffer.readByte(); 62 byte month = buffer.readByte(); 63 byte year = buffer.readByte(); 64 byte[] src = new byte[6]; 65 src[0] = buffer.readByte(); 66 src[1] = buffer.readByte(); 67 src[2] = buffer.readByte(); 68 src[3] = buffer.readByte(); 69 src[4] = buffer.readByte(); 70 src[5] = buffer.readByte(); 71 byte[] dest = new byte[6]; 72 dest[0] = buffer.readByte(); 73 dest[1] = buffer.readByte(); 74 dest[2] = buffer.readByte(); 75 dest[3] = buffer.readByte(); 76 dest[4] = buffer.readByte(); 77 dest[5] = buffer.readByte(); 78 short data_len = buffer.readShort(); 79 if(buffer.readableBytes() < data_len + 4){ 80 //还原读指针 81 buffer.readerIndex(beginReader); 82 return ; 83 } 84 byte cmd = buffer.readByte(); 85 byte[] data = null; 86 if(data_len > 0){ 87 //读取应用数据单元 88 data = new byte[data_len]; 89 buffer.readBytes(data); 90 } 91 92 byte checksum = buffer.readByte(); 93 short end = buffer.readShort(); 94 95 if(end == SmartIotProtocol.END){ 96 log.debug("完成解析,并输出."); 97 SmartIotProtocol iot = new SmartIotProtocol(); 98 iot.setFlowid(flowid); 99 iot.setVersion_major(version_major); 100 iot.setVersion_minor(version_minor); 101 iot.setSecond(second); 102 iot.setMinute(minute); 103 iot.setHour(hour); 104 iot.setDay(day); 105 iot.setMonth(month); 106 iot.setYear(year); 107 iot.setSrc(src); 108 iot.setDest(dest); 109 iot.setData_len(data_len); 110 iot.setCmd(cmd); 111 if(data_len > 0){ 112 iot.setData(data); 113 }else{ 114 iot.setData(null); 115 } 116 iot.setChecksum(checksum); 117 out.add(iot); 118 } 119 } 120 } 121 122 }
3.3 SmartIotEncoder.java
相对于解码,这个编码器,就相对简单了,按照协议,一个byte一本byte进行发送即可。
1 package com.wunaozai.iot.nettyplatform.code; 2 3 import io.netty.buffer.ByteBuf; 4 import io.netty.channel.ChannelHandlerContext; 5 import io.netty.handler.codec.MessageToByteEncoder; 6 7 /** 8 * 自定义协议数据解析 9 * @author Administrator 10 * 11 */ 12 public class SmartIotEncoder extends MessageToByteEncoder<SmartIotProtocol> { 13 14 @Override 15 protected void encode(ChannelHandlerContext ctx, SmartIotProtocol msg, ByteBuf out) throws Exception { 16 //写入消息SmartIot具体内容 17 out.writeShort(SmartIotProtocol.START); 18 out.writeShort(msg.getFlowid()); 19 out.writeByte(msg.getVersion_major()); 20 out.writeByte(msg.getVersion_minor()); 21 out.writeByte(msg.getSecond()); 22 out.writeByte(msg.getMinute()); 23 out.writeByte(msg.getHour()); 24 out.writeByte(msg.getDay()); 25 out.writeByte(msg.getMonth()); 26 out.writeByte(msg.getYear()); 27 out.writeBytes(msg.getSrc()); 28 out.writeBytes(msg.getDest()); 29 out.writeShort(msg.getData_len()); 30 out.writeByte(msg.getCmd()); 31 out.writeBytes(msg.getData()); 32 out.writeByte(msg.getChecksum()); 33 out.writeShort(SmartIotProtocol.END); 34 } 35 36 }
3.4 SmartIotHandler.java
这个是工程里面的主要业务操作类,用户Handler处理所有业务操作,这里也可以理解为是一个入口、网关。所有命令都从这里进行分发到子模块。
1 package com.wunaozai.iot.nettyplatform.code; 2 3 import java.net.InetSocketAddress; 4 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 8 import io.netty.channel.ChannelHandlerContext; 9 import io.netty.channel.SimpleChannelInboundHandler; 10 11 /** 12 * 服务Handler 处理 13 * @author Administrator 14 * 15 */ 16 public class SmartIotHandler extends SimpleChannelInboundHandler<SmartIotProtocol> { 17 18 19 private static final Logger log = LoggerFactory.getLogger(SmartIotHandler.class); 20 21 @Override 22 protected void channelRead0(ChannelHandlerContext ctx, SmartIotProtocol iot) 23 throws Exception { 24 log.info("收到设备数据包: " + iot.getFlowid()); 25 iot.printDebugInfo(); 26 ctx.write("ok"); 27 } 28 29 @Override 30 public void channelActive(ChannelHandlerContext ctx) throws Exception { 31 InetSocketAddress socket = (InetSocketAddress) ctx.channel().remoteAddress(); 32 String ip = socket.getAddress().getHostAddress(); 33 log.info("收到客户端IP: " + ip); 34 } 35 36 @Override 37 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 38 ctx.close(); 39 } 40 }
3.5 NettyServerInitializer.java
这个就是初始化本次Netty框架中,使用的编解码器,还有对应的处理类。
1 package com.wunaozai.iot.nettyplatform.config; 2 3 import com.wunaozai.iot.nettyplatform.code.SmartIotDecoder; 4 import com.wunaozai.iot.nettyplatform.code.SmartIotEncoder; 5 import com.wunaozai.iot.nettyplatform.code.SmartIotHandler; 6 7 import io.netty.channel.ChannelInitializer; 8 import io.netty.channel.ChannelPipeline; 9 import io.netty.channel.socket.SocketChannel; 10 11 /** 12 * 服务器初始化 13 * @author Administrator 14 * 15 */ 16 public class NettyServerInitializer extends ChannelInitializer<SocketChannel> { 17 18 @Override 19 protected void initChannel(SocketChannel ch) throws Exception { 20 // ChannelPipeline pipeline = ch.pipeline(); 21 // //自定义切割符 22 // //ByteBuf delimiter = Unpooled.copiedBuffer(new byte[] {16}); 23 // ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes()); 24 // 25 // pipeline.addLast(new DelimiterBasedFrameDecoder(8192, delimiter)); 26 // pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); 27 // pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); 28 // pipeline.addLast(new NettyServerHandler()); 29 30 ChannelPipeline pipeline = ch.pipeline(); 31 //添加自定义编解码器 32 pipeline.addLast(new SmartIotEncoder()); 33 pipeline.addLast(new SmartIotDecoder()); 34 //处理网络IO 35 pipeline.addLast(new SmartIotHandler()); 36 } 37 38 }
3.6 NettyServer.java
Netty功能的入口类,所有Netty框架初始化步骤都在这里进行简单处理。
1 package com.wunaozai.iot.nettyplatform.config; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.stereotype.Component; 6 7 import io.netty.bootstrap.ServerBootstrap; 8 import io.netty.channel.ChannelFuture; 9 import io.netty.channel.ChannelOption; 10 import io.netty.channel.EventLoopGroup; 11 import io.netty.channel.nio.NioEventLoopGroup; 12 import io.netty.channel.socket.nio.NioServerSocketChannel; 13 import io.netty.handler.logging.LogLevel; 14 import io.netty.handler.logging.LoggingHandler; 15 16 /** 17 * Netty 服务器 18 * @author Administrator 19 * 20 */ 21 @Component 22 public class NettyServer { 23 24 private static final Logger log = LoggerFactory.getLogger(NettyServer.class); 25 26 private int port = 7777; 27 28 public void run(){ 29 EventLoopGroup bossGroup = new NioEventLoopGroup(); 30 EventLoopGroup workerGroup = new NioEventLoopGroup(); 31 try { 32 ServerBootstrap serverBootstrap = new ServerBootstrap(); 33 serverBootstrap.group(bossGroup, workerGroup); 34 serverBootstrap.channel(NioServerSocketChannel.class); 35 serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024); 36 serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); 37 serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true); 38 serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); 39 serverBootstrap.childHandler(new NettyServerInitializer()); 40 // 绑定端口,开始接收进来的连接 41 ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); 42 log.info("netty服务启动: [port:" + port + "]"); 43 // 等待服务器socket关闭 44 channelFuture.channel().closeFuture().sync(); 45 } catch (Exception e) { 46 log.error("Netty 服务启动失败: " + e.getMessage()); 47 }finally { 48 bossGroup.shutdownGracefully(); 49 workerGroup.shutdownGracefully(); 50 } 51 } 52 }
3.7 IotNettyPlatFormApplication.java
这个是Spring Boot项目的入口函数。在这里调用Netty的入口函数。
1 package com.wunaozai.iot.nettyplatform; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.boot.SpringApplication; 6 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 import org.springframework.context.annotation.ComponentScan; 8 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 10 import com.wunaozai.iot.nettyplatform.config.NettyServer; 11 12 @SpringBootApplication 13 public class IoTNettyPlatFormApplication { 14 15 private static final Logger log = LoggerFactory.getLogger(IoTNettyPlatFormApplication.class); 16 17 18 public static void main(String[] args) { 19 SpringApplication.run(IoTNettyPlatFormApplication.class, args); 20 run(); 21 } 22 23 private static NettyServer nettyServer = new NettyServer(); 24 25 private static void run(){ 26 Thread thread = new Thread(new Runnable() { 27 @Override 28 public void run() { 29 nettyServer.run(); 30 } 31 }); 32 thread.start(); 33 } 34 35 }
我这里通过在@SpringBootApplication 这里调用NettyServer。同时还有其他方式:
1) 通过实现ApplicationListener
1 import org.slf4j.Logger; 2 import org.slf4j.LoggerFactory; 3 import org.springframework.context.ApplicationListener; 4 import org.springframework.context.event.ContextRefreshedEvent; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 项目初始化 9 * @author wunaozai 10 * @date 2018-05-24 11 */ 12 @Component 13 public class OnStartListener implements ApplicationListener<ContextRefreshedEvent> { 14 15 private static final Logger log = LoggerFactory.getLogger(OnStartListener.class); 16 17 @Override 18 public void onApplicationEvent(ContextRefreshedEvent arg0) { 19 log.info("Run on Start Listener."); 20 } 21 22 }
2) 通过实现CommandLineRunner
1 import org.slf4j.Logger; 2 import org.slf4j.LoggerFactory; 3 import org.springframework.boot.CommandLineRunner; 4 import org.springframework.core.annotation.Order; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 项目启动时初始化资源<br> 9 * 如 一些初始化操作,提前加载加密证书,初始化线程池等 10 * @author wunaozai 11 * @date 2018-05-24 12 */ 13 @Component 14 @Order(value = 1) //执行顺序 15 public class Runner implements CommandLineRunner { 16 17 private static final Logger log = LoggerFactory.getLogger(Runner.class); 18 19 @Override 20 public void run(String... args) throws Exception { 21 log.info("The Runner start to Initialize."); 22 } 23 24 }
三、协议测试
四、简单架构
由于引入了自定义协议,所以需要对原先的流程进行简单的改造,下面这个图是某项目的架构图。
参考资料:
https://www.cnblogs.com/sidesky/p/6913109.html