Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-云服务端(IOT平台)

news2024/11/25 4:41:23

之前有文章用java实现了设备端和应用订阅端,那么我根据AIOT的协议也可以实现一个demo物联网平台端,这种简易的平台是实现自己搭建物联网平台的基础。

直接用代码

新建Springboot的maven项目,pom.xml文件导入依赖包(用到了swagger来测试发送数据)

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>boot.ctwing.tcp.server</groupId>
    <artifactId>boot-example-ctwing-tcp-server-2.0.5</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>boot-example-ctwing-tcp-server-2.0.5</name>
    <url>http://maven.apache.org</url>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.29.Final</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 打包成一个可执行jar -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
SwaggerConfig配置(和之前的没多大区别)
package boot.ctwing.tcp.server.config;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 *  蚂蚁舞
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.any()).paths(PathSelectors.any())
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .paths(PathSelectors.regex("/.*"))
                .build().apiInfo(apiInfo());
    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("天翼物联网CtWing模拟云端")
                .description("模拟AIOT平台Demo")
                .version("0.01")
                .build();
    }

    /**
     * http://localhost:8177/doc.html  地址和端口根据实际项目查看
     */


}
CtWingConstant
package boot.ctwing.tcp.server.config;

/**
 *  蚂蚁舞
 */
public class CtWingConstant {

    public static final int port = 8996;

    //  登录认证
    public static final String tcp_hex_01 = "01";

    //  上行数据报文
    public static final String tcp_hex_02 = "02";

    //  下行数据报文
    public static final String tcp_hex_03 = "03";

    //  上行心跳
    public static final String tcp_hex_04 = "04";

    //  登录响应
    public static final String tcp_hex_05 = "05";

    //  心跳响应
    public static final String tcp_hex_06 = "06";

    public static long READ_TIME_OUT = 5*60;

    public static long WRITE_TIME_OUT = 5*60;

    public static long ALL_TIME_OUT = 5*60;


}
TcpServer服务端Netty核心代码
package boot.ctwing.tcp.server.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 *  蚂蚁舞
 */
public class TcpServer {

    /**
     * 	启动服务
     */
    public void startup(int port) {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap = serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap = serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap = serverBootstrap.option(ChannelOption.SO_BACKLOG, 10240).option(ChannelOption.SO_SNDBUF, 32 * 10240);
            serverBootstrap = serverBootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576));
            serverBootstrap = serverBootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576));

            serverBootstrap = serverBootstrap.childHandler(new TcpChannelInitializer<SocketChannel>());

            System.out.println(port + " netty start success!");


            ChannelFuture f = serverBootstrap.bind(port).sync();

            f.channel().closeFuture().sync();
        } catch (Exception e) {
            System.out.println(e.toString());
        } finally {
            /**
             * 	退出,释放线程池资源
             */
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }


}
TcpChannelInboundHandlerAdapter
package boot.ctwing.tcp.server.netty;

import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import boot.ctwing.tcp.server.config.CtWingConstant;
import boot.ctwing.tcp.server.utils.CtWingUtils;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *  蚂蚁舞
 */

public class TcpChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter{
    private final Logger log =  LoggerFactory.getLogger(this.getClass());

    protected static AttributeKey<String> _channelId = AttributeKey.valueOf("deviceId");

    protected void setChannelDeviceId(Channel channel, String deviceId) {
        channel.attr(_channelId).set(deviceId);
    }

    protected String getChannelDeviceId(Channel channel) {
        return Optional.ofNullable(channel).map(ch -> ch.attr(_channelId).get()).orElse(null);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        log.info("--channelRegistered--"+ctx.channel().id().toString());
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
        log.info("--channelUnregistered--"+ctx.channel().id().toString());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            byte[] req = (byte[]) msg;
            if(req.length > 0){
                String hex = CtWingUtils.bytesToHexStr(req);
                log.info("data--"+hex);
                String soh = hex.substring(0,2);
                switch (soh) {
                    case CtWingConstant.tcp_hex_04:
                        // 终端的心跳上来,平台回复
                        String back = CtWingConstant.tcp_hex_06;
                        byte[] data = CtWingUtils.hexStrToBytes(back);
                        ctx.channel().writeAndFlush(Unpooled.buffer().writeBytes(data));
                        break;
                    case CtWingConstant.tcp_hex_02:
                        // 终端设备发送的上行数据,可返回03协议的数据表示收到消息,也可以不用返回
                        String dataHex = hex.substring(6);
                        log.info("hexStr--"+dataHex);
                        // 如果是字符串 16进制字符串转字符串
                        log.info("str--"+CtWingUtils.hexStrToStr(dataHex));
                        break;
                    case CtWingConstant.tcp_hex_01:
                        // 0x01 登录认证消息,认证成功响应 05 00 00 (错误按照CtWing的tcp协议返回)
                        try {
                            String dataAuth = hex.substring(2);
                            int len_d = Integer.parseInt(dataAuth.substring(0,4), 16) * 2;
                            String deviceId = CtWingUtils.hexStrToStr(dataAuth.substring(4,4+len_d));
                            System.out.println("str--deviceId--"+deviceId);

                            dataAuth = dataAuth.substring(4+len_d);
                            int len_s = Integer.parseInt(dataAuth.substring(0, 4), 16) * 2;
                            String password = CtWingUtils.hexStrToStr(dataAuth.substring(4,len_s+4));
                            System.out.println("str-password--"+password);

                            String imei = deviceId.substring(8);
                            System.out.println("str-imei--"+imei);
                            TcpChannelMap map = new TcpChannelMap();
                            map.setChannel(ctx.channel());
                            map.setDeviceId(imei);
                            setChannelDeviceId(ctx.channel(),imei);
                            TcpChannelMapCache.add(imei, map); // demo 暴力解决,直接覆盖

                            // 直接返回连接成功 纯demo不需要验证
                            String authSuccess = "050000";
                            byte[] authSuccessData = CtWingUtils.hexStrToBytes(authSuccess);
                            ctx.channel().writeAndFlush(Unpooled.buffer().writeBytes(authSuccessData));

                        } catch (Exception e){
                            //  有问题直接关闭,暴力解决
                            ctx.channel().close();
                        }

                    default:
                        break;
                }
            }
        } catch (Exception e) {
            System.out.println("channelRead--"+e.toString());
        }


    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
        log.info("--channelReadComplete--"+ctx.channel().id().toString());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.info("--exceptionCaught--"+ctx.channel().id().toString());
        try {
            String imei = getChannelDeviceId(ctx.channel());
            if(imei != null){
                setChannelDeviceId(ctx.channel(), null);
                TcpChannelMapCache.remove(imei);
            }
            ctx.close();
        } catch (Exception e) {
            System.out.println("exceptionCaught--"+e.toString());
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.channel().read();
        log.info("--channelActive--"+ctx.channel().id().toString());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        log.info("--channelInactive--"+ctx.channel().id().toString());
        String imei = getChannelDeviceId(ctx.channel());
        if(imei != null){
            setChannelDeviceId(ctx.channel(), null);
            TcpChannelMapCache.remove(imei);
        }
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException {
        super.userEventTriggered(ctx, evt);
        log.info("--userEventTriggered--"+ctx.channel().id().toString());
        ctx.close();
    }

}
TcpChannelInitializer
package boot.ctwing.tcp.server.netty;

import boot.ctwing.tcp.server.config.CtWingConstant;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;

/**
 *  蚂蚁舞
 */

public class TcpChannelInitializer<SocketChannel> extends ChannelInitializer<Channel>{

    @Override
    protected void initChannel(Channel ch) throws Exception {

        ch.pipeline().addLast(new IdleStateHandler(CtWingConstant.READ_TIME_OUT, CtWingConstant.WRITE_TIME_OUT, CtWingConstant.ALL_TIME_OUT, TimeUnit.SECONDS));

        //  二者选择一个就可以
        //  使用netty自带的
//        ch.pipeline().addLast("decoder", new ByteArrayDecoder());
//        ch.pipeline().addLast("encoder", new ByteArrayEncoder());

        // 使用自定义的
        ch.pipeline().addLast(new TcpMessageCodec());


        ch.pipeline().addLast(new TcpChannelInboundHandlerAdapter());

    }

}
TcpMessageCodec
package boot.ctwing.tcp.server.netty;


import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

import java.util.List;

/**
 *  蚂蚁舞
 */
@ChannelHandler.Sharable
public class TcpMessageCodec extends MessageToMessageCodec<ByteBuf, ByteBuf> {


    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        byte[] array = new byte[msg.readableBytes()];
        msg.getBytes(0, array);
        out.add(Unpooled.wrappedBuffer(array));
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        byte[] array = new byte[msg.readableBytes()];
        msg.getBytes(0, array);
        out.add(array);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        System.out.println("OutIn异常!"+cause);
    }

}

TcpChannelMap用来保活终端连接的对象
package boot.ctwing.tcp.server.netty;

import io.netty.channel.Channel;

/**
 * 	蚂蚁舞
 */
public class TcpChannelMap {

	private String deviceId;
	
	private transient volatile Channel channel;

	public String getDeviceId() {
		return deviceId;
	}

	public void setDeviceId(String deviceId) {
		this.deviceId = deviceId;
	}

	public Channel getChannel() {
		return channel;
	}

	public void setChannel(Channel channel) {
		this.channel = channel;
	}

	@Override
	public String toString() {
		return "IotTcpChannel [deviceId=" + deviceId + "]";
	}
	
	
	
	
	
	
}
TcpChannelMapCache
package boot.ctwing.tcp.server.netty;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *  蚂蚁舞
 */
public class TcpChannelMapCache {

    public static volatile Map<String, TcpChannelMap> channelMapCache = new ConcurrentHashMap<String, TcpChannelMap>();

    public static void add(String code, TcpChannelMap channel){
    	channelMapCache.put(code,channel);
    }

    public static TcpChannelMap get(String code){
        return channelMapCache.get(code);
    }

    public static void remove(String code){
    	channelMapCache.remove(code);
    }

    public static void save(String code, TcpChannelMap channel) {
        if(channelMapCache.get(code) == null) {
            add(code,channel);
        }
    }

    public static Map<String, TcpChannelMap> getMap() {
        return channelMapCache;
    }

    public static int size() {
        return channelMapCache.size();
    }

    public static void setMap(Map<String, TcpChannelMap> channelMap) {
        TcpChannelMapCache.channelMapCache = channelMap;
    }

    public static void list() {
        for (Map.Entry<String, TcpChannelMap> entry : channelMapCache.entrySet()) {
            System.out.println("key= " + entry.getKey() + " and value= "+ entry.getValue().toString());
        }
    }

}
TcpServerThread启动线程
package boot.ctwing.tcp.server.netty;

/**
 *  蚂蚁舞
 */
public class TcpServerThread extends Thread {


    private final int port;

    public TcpServerThread(int port){
        this.port = port;
    }

    public void run() {
        TcpServer tcpServer =  new TcpServer();
        tcpServer.startup(port);
    }
}
CtWingUtils
package boot.ctwing.tcp.server.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;

/**
 *  蚂蚁舞
 */
public class CtWingUtils {

    private static final Logger log = LoggerFactory.getLogger(CtWingUtils.class);

    private static final char[] HEXES = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public static String bytesToHexStr(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        StringBuilder hex = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            hex.append(HEXES[(b >> 4) & 0x0F]);
            hex.append(HEXES[b & 0x0F]);
        }
        return hex.toString().toUpperCase();
    }

    public static byte[] hexStrToBytes(String hex) {
        if (hex == null || hex.length() == 0) {
            return null;
        }
        char[] hexChars = hex.toCharArray();
        byte[] bytes = new byte[hexChars.length / 2];   // 如果 hex 中的字符不是偶数个, 则忽略最后一个
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16);
        }
        return bytes;
    }

    public static String strToHexStr(String str) {
        StringBuilder sb = new StringBuilder();
        byte[] bs = str.getBytes();
        int bit;
        for (int i = 0; i < bs.length; i++) {
            bit = (bs[i] & 0x0f0) >> 4;
            sb.append(HEXES[bit]);
            bit = bs[i] & 0x0f;
            sb.append(HEXES[bit]);
        }
        return sb.toString().trim();
    }

    public static String hexStrToStr(String hexStr) {
        //能被16整除,肯定可以被2整除
        byte[] array = new byte[hexStr.length() / 2];
        try {
            for (int i = 0; i < array.length; i++) {
                array[i] = (byte) (0xff & Integer.parseInt(hexStr.substring(i * 2, i * 2 + 2), 16));
            }
            hexStr = new String(array, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        return hexStr;
    }

    public static String hexLen4Calc(int fixed, int len) {
        StringBuilder x = new StringBuilder(Integer.toHexString(len));
        int xC = fixed - x.length();
        for (int i = 0; i < xC; i++) {
            x.insert(0, "0");
        }
        return x.toString();
    }



}

TcpServerController

package boot.ctwing.tcp.server.controller;


import boot.ctwing.tcp.server.config.CtWingConstant;
import boot.ctwing.tcp.server.netty.TcpChannelMap;
import boot.ctwing.tcp.server.netty.TcpChannelMapCache;
import boot.ctwing.tcp.server.utils.CtWingUtils;
import io.netty.buffer.Unpooled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 	蚂蚁舞
 */
@RestController
public class TcpServerController {

	@GetMapping(value = {"", "/"})
	public String index() {
		return "天翼物联网CtWing终端模拟mock";
	}

	@GetMapping("/terminalList")
	public List<Map<String,String>> terminalList() {
		List<Map<String,String>> list = new ArrayList<>();
		for (Map.Entry<String, TcpChannelMap> entry : TcpChannelMapCache.channelMapCache.entrySet()) {
			Map<String, String> map = new HashMap<String, String>();
			map.put("imei", entry.getKey());
			map.put("key_id", entry.getValue().getChannel().id().toString());
			list.add(map);
		}
		return list;
	}

	@GetMapping("/downData")
	public String downData(@RequestParam(name="imei", required = true) String imei,
							 @RequestParam(name="content", required = true) String content) {
		TcpChannelMap tcpChannelMap = TcpChannelMapCache.get(imei);
		if(tcpChannelMap != null && tcpChannelMap.getChannel().isOpen()){
			String upHexStr = CtWingUtils.strToHexStr(content);
			String upHexStrLenHex = CtWingUtils.hexLen4Calc(4, upHexStr.length()/2);
			String cmd = CtWingConstant.tcp_hex_03+upHexStrLenHex+upHexStr;
			System.out.println(cmd);
			byte[] bytes = CtWingUtils.hexStrToBytes(cmd);
			tcpChannelMap.getChannel().writeAndFlush(Unpooled.buffer().writeBytes(bytes));
			return "success";
		}
		return "fail";
	}

}

代码目录结构

├─boot-example-ctwing-tcp-server-2.0.5
│  │  pom.xml
│  │  
│  └─src
│      ├─main
│      │  ├─java
│      │  │  └─boot
│      │  │      └─ctwing
│      │  │          └─tcp
│      │  │              └─server
│      │  │                  │  BootCtWingTcpServer.java
│      │  │                  │  
│      │  │                  ├─config
│      │  │                  │      CtWingConstant.java
│      │  │                  │      SwaggerConfig.java
│      │  │                  │      
│      │  │                  ├─controller
│      │  │                  │      TcpServerController.java
│      │  │                  │      
│      │  │                  ├─netty
│      │  │                  │      TcpChannelInboundHandlerAdapter.java
│      │  │                  │      TcpChannelInitializer.java
│      │  │                  │      TcpChannelMap.java
│      │  │                  │      TcpChannelMapCache.java
│      │  │                  │      TcpMessageCodec.java
│      │  │                  │      TcpServer.java
│      │  │                  │      TcpServerThread.java
│      │  │                  │      
│      │  │                  └─utils
│      │  │                          CtWingUtils.java
│      │  │                          
│      │  └─resources
│      │          application.properties
│      │          logback-spring.xml
│      │          
│      └─test
│          └─java
│              └─boot
│                  └─ctwing
│                      └─tcp
│                          └─server
│                                  BootCtWingTcpServerTest.java
│                                  

我们先启动简易demo的Iot服务端

17:50:48.274 spring-boot-logging [main] INFO  s.d.s.w.s.ApiListingReferenceScanner - Scanning for api listing references
17:50:48.451 spring-boot-logging [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8179"]
17:50:48.466 spring-boot-logging [main] INFO  o.a.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
17:50:48.486 spring-boot-logging [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8179 (http) with context path ''
17:50:48.491 spring-boot-logging [main] INFO  b.c.tcp.server.BootCtWingTcpServer - Started BootCtWingTcpServer in 6.881 seconds (JVM running for 8.157)
server netty start
Hello World!
8996 netty start success!

在启动之前模拟设备端对本地的模拟连接,启动2个(imei需要更改)

如此可以在服务端的接口中查到2个终端设备连上,并保持着连接

可以看到有2个设备连上了Demo物联网平台

[
  {
    "key_id": "4f31e0c8",
    "imei": "869401041201815"
  },
  {
    "key_id": "99ac6929",
    "imei": "359951090161283"
  }
]

测试一下用这个demo平台下发

在这个设备的终端收到的消息(日志查看)

18:29:18.688 spring-boot-logging [nioEventLoopGroup-2-1] INFO  b.c.t.t.n.TcpChannelInboundHandlerAdapter - data--03001F6D7977E89A82E89A81E8889E2DE89A82E89A81E4B99FE4BC9AE8B7B3E8889E
18:29:18.688 spring-boot-logging [nioEventLoopGroup-2-1] INFO  b.c.t.t.n.TcpChannelInboundHandlerAdapter - hexStr--6D7977E89A82E89A81E8889E2DE89A82E89A81E4B99FE4BC9AE8B7B3E8889E
18:29:18.688 spring-boot-logging [nioEventLoopGroup-2-1] INFO  b.c.t.t.n.TcpChannelInboundHandlerAdapter - str--myw蚂蚁舞-蚂蚁也会跳舞
channelReadComplete

 在测试一下终端上报数据

 看云端demo的打印日志

18:30:05.745 spring-boot-logging [nioEventLoopGroup-3-2] INFO  b.c.t.s.n.TcpChannelInboundHandlerAdapter - data--02000FE68891E698AFE89A82E89A81E8889E
18:30:05.745 spring-boot-logging [nioEventLoopGroup-3-2] INFO  b.c.t.s.n.TcpChannelInboundHandlerAdapter - hexStr--E68891E698AFE89A82E89A81E8889E
18:30:05.745 spring-boot-logging [nioEventLoopGroup-3-2] INFO  b.c.t.s.n.TcpChannelInboundHandlerAdapter - str--我是蚂蚁舞
18:30:05.745 spring-boot-logging [nioEventLoopGroup-3-2] INFO  b.c.t.s.n.TcpChannelInboundHandlerAdapter - --channelReadComplete--4f31e0c8

这样就是一套最简单的IOT物联网平台,部署到云服务器上可以简单对接终端设备,这也是一套自建物联网平台的一套基础demo代码(用的是天翼物联网平台(AIOT)的TCP透传协议)

具体对接设备,那么在这套协议之上定义一套物联网开发的自定义协议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UDP协议在Windows上使用示例

UDP(User Datagram Protocol&#xff0c;用户数据报协议)是无连接的&#xff0c;因此在两个进程通信前没有握手过程。UDP协议提供一种不可靠数据传送服务&#xff0c;也就是说&#xff0c;当进程将一个报文发送进UDP套接字时&#xff0c;UDP协议并不保证该报文将到达接收进程。…

过孔基础常识

过孔&#xff0c;一个绝大多数硬件工程师都听说过&#xff0c;但又并非真正了解的名词。了解的都知道&#xff0c;其在PCB板中其着至关重要的的作用。没有过孔的存在&#xff0c;很难画出一块完美的PCB板。所以呢&#xff0c;小编今日就带大家了解了解什么是过孔。 什么是过孔…

FCN代码及效果展示

1. 代码获取 代码地址: https://github.com/Le0v1n/ml_code/tree/main/Segmentation/FCN 2. 从头开始训练 2.1 测试平台 GPU&#xff1a;NVIDIA RTX 3070CPU: Intel I5-10400FRAM: 16GBOS: Windows 11Dataset: VOC2012Class num: 21(201)Batch size: 4Learning Rate: 0.1Ep…

嘉兴经开区第四届创新创业大赛总决赛成功举办

12月21日至12月22日&#xff0c;嘉兴经济技术开发区第四届创新创业大赛总决赛成功举办&#xff0c;经过激烈角逐最后共有10家企业分别获得大赛初创组和成长组的一二三等奖。 总决赛现场 嘉兴经开区第四届中国创新创业大赛于6月正式启动&#xff0c;陆续在嘉兴、成都、北京、西…

【详细学习SpringBoot源码之内嵌Tomcat启动原理分析编译部署Tomcat源码过程解析-9】

一.知识回顾 【0.SpringBoot专栏的相关文章都在这里哟&#xff0c;后续更多的文章内容可以点击查看】 【1.SpringBoot初识之Spring注解发展流程以及常用的Spring和SpringBoot注解】 【2.SpringBoot自动装配之SPI机制&SPI案例实操学习&SPI机制核心源码学习】 【3.详细学…

12-RabbitMq概述与工作模式深度剖析

MQ概述 MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。 MQ 的优势 应用解耦&#xff1a;提高系统容错性和可维护性 异步提速&#xff1a;提升用户体验和系统吞吐量 削峰填谷&#xff1…

unity中使用代码接绘制三维模型

一 模型的构成 在三维世界中&#xff0c;绘制一个模型并不是什么很复杂的问题。只要知道了基本原理一切需求便迎刃而解。 如下图所示&#xff0c;任何模型都是由点线面构成的&#xff0c;而面的最小单位是三角形。 任何一个多边形的面&#xff0c;都是由多个三角形构成的。比…

Web前端105天-day64-HTML5_CORE

HTML5CORE04 目录 前言 一、复习 二、WebSocket 三、服务器搭建 四、聊天室 五、defineProperty 5.1.初识defineProperty 5.2.配置多个属性 5.3.可配置 5.4.赋值监听 5.5.练习 5.6.计算属性 总结 前言 HTML5CORE04学习开始 一、复习 SVG: 利用HTML的 DOM 来绘制图…

PCB贴片机如何送料?

1.常见的贴片机供料器四种形式 http://www.sz-bjzn.com/1547.html 2.模块化设计SMT贴片机送料器的操作方法 3.淘宝 https://item.taobao.com/item.htm?spma230r.1.14.98.33e41823OZ1zzn&id579043582781&ns1&abbucket20#detail 不错&#xff1a;https://item.tao…

distinct与group by 去重

distinct与group by 去重distinct 特点&#xff1a;group by 特点&#xff1a;总结&#xff1a;mysql中常用去重复数据的方法是使用 distinct 或者group by &#xff0c;以上2种均能实现&#xff0c;但也有不同的地方。distinct 特点&#xff1a; 1、distinct 只能放在查询字段…

重新更新anaconda

更新anaconda问题阐述问题分析打开Anaconda Nvaigator打开文件所在位置复制文件所在路径找到此电脑或者打开设置找到高级系统设置环境变量添加环境变量打开scripts文件修改成功再一次启动感谢观看今天手贱,不小心删掉的anaconda,我想一不做二不休,直接重新重装了,就找到了anaco…

经典SQL语句大全(基础、提升、技巧、数据开发、基本函数)

目录 前言 正文 第一章&#xff1a;基础 第二章&#xff1a;提升 第三章&#xff1a;技巧 第四章&#xff1a;数据开发-经典​​​​​​​ 第五章&#xff1a;SQL Server基本函数 第六章&#xff1a;常识 第七章&#xff1a;SQLServer2000 同步复制技术实现步骤 总结…

juc-4-synchronized原理

目录 1、synchronized 作用于静态方法 总结 ​编辑 案例 静态成员变量 (synchronized锁非静态方法) 案例 静态成员变量 (synchronized锁静态方法 或 直接锁类) 2、监视器锁(monitor) 2.1 synchronized怎么实现的线程安全呢&#xff1f; 3、JDK6 synchronized 的优化 3.1 C…

互联网技术不再是统领,当下正在发生着一场深刻的变革

拥抱实体经济&#xff0c;绝对是当下互联网玩家们的首要选择。无论是头部的互联网企业来讲&#xff0c;还是新生的互联网玩家而言&#xff0c;它们都不约而同地将关注的焦点聚焦在了这样一个方向上。   透过这一点&#xff0c;我们可以非常明显地感受到&#xff0c;一个全新的…

圣诞节,记录前行中跨过的2022

2022年&#xff0c;我人生的第二十四年&#xff0c;是我大学生活的最后一年&#xff0c;是我职场生涯的第一年&#xff0c;这一年从学生到打工人&#xff0c;从实习生到职场员工&#xff0c;变化了许多&#xff0c;做了许多&#xff0c;收获了许多&#xff0c;同时也成长了许多…

m自适应FSK解调系统误码率matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 FSK信号的解调也有非相干和相干两种&#xff0c;FSK信号可以看作是用两个频率源交替传输得到的&#xff0c;所以FSK的接收机由两个并联的ASK接收机组成。 (1)相干解调 相干解调是利用乘法器&…

Minecraft(我的世界) Fabric 1.19.3 服务器搭建教程

Debian系统使用MCSManager9面板搭建MC Java版MOD服务器的教程&#xff0c;本教程用的Fabric1.19.3服务端&#xff0c;用其他服务端的也可以参考一下。 视频教程&#xff1a;https://www.bilibili.com/video/BV1Zd4y1h7zG/ 我的世界(MC) Fabric 1.19.3 开服教程&#xff0c;新手…

IDEA Git 选项栏各项功能详解

IDEA Git 选项栏各项功能详解 如图所示 Copy Revision Number 顾名思义 拷贝当前版本号 到剪切板 a8e4b86ce9ca01968629504a6e19b4b99d76a853 Create Patch 在 git 日志中选择要创建补丁的commit&#xff0c;右键选择Create Patch...同一个文件在多次commit中都存在&#xff…

DSP-离散时间系统

目录 概念&#xff1a; 累加器(Accumulator)&#xff1a; N点滑动滤波器&#xff1a; 离散时间系统的分类 &#xff1a; 线性系统 Linear System 移不变系统 Shift-Invariant Systems 因果系统 Causal System 稳定系统 Stable System 无源&#xff08;passive&#x…

蚂蚁感冒(第五届蓝桥杯省赛C++A/B组)

目录 题目详细&#xff1a;​编辑 题目思路&#xff1a; 两种情况&#xff1a; 代码详解&#xff1a; 题目详细&#xff1a; 题目思路&#xff1a; 这个题目的关键在于对蚂蚁相遇 的时候情况的看待 两个蚂蚁相遇时候的情况 我们可以看作 他们相互穿过了彼此 假设相遇…