springboot框架使用Netty依赖中解码器的作用及实现详解

在项目开发 有需求 需要跟硬件通信
也没有mqtt 作为桥接 也不能http 请求 api 所以也不能 json字符串这么爽传输

所以要用tcp 请求 进行数据交互 数据还是16进制的 写法 有帧头 什么的
对于这种物联网的这种对接
我的理解就是 我们做的工作就像翻译
把这些看不懂的 字节流 变成 java 认识的数据

就了解了一下 netty 这个依赖
在springboot 项目种使用这个依赖进行 连接

但是这个依赖 如果不熟悉 使用不当 有可能有内存溢出的风险
所以大家如果自己用这个netty 依赖 做物联网的调用
要小心 最好能找一下 物联网中间件 基于netty封装的

这是我后续 找的一个中间件 也不算很成熟 如果大家有更好用的 欢迎留言 一起分享

http://www.iteaj.com/#/course

但是我发现还是要根据 对接的物联网硬件文档 写自定义解码器 或者编码器
这篇文章主要分享一下 我了解到的 netty 下的 四个解码器 使用方案

概念:
拆包和沾包
是典型的拆包和沾包问题,俗话说就是两端通信,一端发送一端接收,接收的那一端怎么知道是否已经完整的接收了数据?

假设服务端连续发送了两条消息:hello world! / hello client!

由于客户端不知道怎么才算一条消息,怎么才算两条消息,所以读取会有以下几种情况:

1.分两次读取消息,第一次是hello world!,第二次是hello client! 这是正常情况

2.一次就读取完成,hello world!hello client! 这种情况就叫沾包

3.分两次读取消息,第一次是hello ,第二次是world!hello client! 这第一次读取就是拆包,第二次就是沾包

总之就是读取到的信息不完整就是拆包,读取到的信息有额外多的信息就是沾包

解码器就可以来解决这个问题

依赖引入就不说了
先分享代码

netty配置类

package com.netty.server.tpcServer;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


/**
 * User:Json
 * Date: 2024/4/12
 **/
@Component
@Slf4j
public class NettyServer {

     private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

    // 服务端NIO线程组
    private final EventLoopGroup bossGroup = new NioEventLoopGroup();
    private final EventLoopGroup workGroup = new NioEventLoopGroup();

    public ChannelFuture start(String host, int port) {
        ChannelFuture channelFuture = null;
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 1024 就是规定传入的 字节长度的大小
                            // 对于字节 一个字符串占多少字节 int 占多多少字节 long 占多多少字节 如果明白这个 应该很好理解
                            //第一种:LineBasedFrameDecoder:传入的参数是消息最大长度,发送消息的大小必须小于设置值
                            // 行分隔符解码器(结尾根据 “\n” 作为结束标识)
                            //socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                            //第二种:DelimiterBasedFrameDecoder 自定义分割器解码器,结尾根据什么作为结束标识可以自定义 比如用 |
                           // socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("|".getBytes())));
                           // 第三种: FixedLengthFrameDecoder // 固定长度解码器,发送的消息需要定长
                          //  socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(7));

                            // 上面三个解码器明显的看到不够灵活  所以还有第四种
                            //第四种:LengthFieldBasedFrameDecoder:基于长度的自定义解码器,比较灵活 这个
                            // 这个 解码器 一共有 5个参数
//                            maxFrameLength:最大帧长度。也就是可以接收的数据的最大长度。如果超过,此次数据会被丢弃
//                            lengthFieldOffset:长度域偏移量。存储数据长度的一个偏移量
//                            lengthFieldLength:长度域字节数。存储数据长度的一个大小
//                            lengthAdjustment:数据长度修正。因为长度既可以代表data的长度,也可以是整个消息的长度
//                            initialBytesToStrip:跳过的字节数。可以选择舍弃一部分数据
                            // 可以理解为 我们给数据定义传输规则 就像http请求 定义规则 比如 请求头 数据结构 json 等等
                            // 所以我们作为接收数据的服务端 可以在开发接收数据的时候 定义规则 然后让客户端按照规则来传输数据
                            // 比如 发送一个消息,我们定义的结构为:消息头+数据长度+数据
                            // 我们怎么定义规则呢 比如:
                            // 整体字节为 15个字节
                            // 要发送的数据 "Message" 长度为 7个字节
                            //我们规定 maxFrameLength :1024 接收的最大数据,
                            // lengthFieldOffset:长度域偏移量 一般用来定义消息头 4个字节、
                            // lengthFieldLength:长度域字节数  用于定义数据长度(length)的【值】的大小 4个字节
                            // lengthAdjustment : 0 数据长度修正
                            // 假设我设置的数据长度是20,代表了整个消息体的长度,但是我数据却只有12个字节,这往后读20个字节无疑是错的,所以我们需要修正,怎么修正? 减8 就行
                            // 所以如果你需要修正你的 数据长度,那么lengthAdjustment就是用来修正的。
                            // initialBytesToStrip :8 ,如果在我们业务层只需要 消息体 像消息头 和 数据长度都不需要 那么 initialBytesToStrip就是用来跳过的字节数。
                            // 就比如 消息头规定 4个字节 数据长度 4个字节 所以我们如果只要 消息体里的内容 就需要跳过 8个字节 所以设置8 即可

                            // 获取全部数据
                          //  socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,4,4,0,0));

                            // 只获取消息体里的数据
                            socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,4,4,0,8));


                            // 自定义服务处理 所以再进行服务处理之前 需要先经历 解码器   既然有解码器 也有编码器
                            // 如果我们作为服务端 那就需要编码器 如果我们作为客户端 那就需要解码器
                            // 解码器主要用于 将接收到的字节数据转换成有意义的业务对象。比如java 认识的 实体类对象
                            // 在网络编程中,数据在传输过程中通常是以字节数组的形式进行传递的,
                            // 而业务处理通常需要对这些字节数组进行解析,转换为具体的业务对象。解码器正是完成这一工作的组件
                            // 而且解码器 还需要 处理拆包和沾包 等  所以我们在使用 tcp 进行通信的时候 到业务层处理 的时候 需要先经过解码器
                            socketChannel.pipeline().addLast(new ServerHandler());
                        }

                    });
            // 绑定端口并同步等待
            channelFuture = bootstrap.bind(host, port).sync();
            log.info("======Start Up Success!=========");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return channelFuture;
    }

    public void close() {
        workGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
        log.info("======Shutdown Netty Server Success!=========");
    }



    //测试 main
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new StringEncoder());
                        }
                    });

            ChannelFuture channelFuture = bootstrap.connect("192.168.0.209", 17070).sync();
            Channel channel = channelFuture.channel();

            // 连续发送多条消息
            for (int i = 0; i < 100; i++) {
                //System.getProperty("line.separator") 获取系统换行符  测试一种情况
               // String message = "Message " + i + System.getProperty("line.separator");
               // String message = "Message " + i+"|";  //测试第二种情况
               // String message = "Message"; // 第三种 固定长度情况
              //  channel.writeAndFlush(message);
                //第四种 自定义解码器
                //[4个字节 + 4个字节 + 数据]
                // 我们传输数据 就要这样拼接数据 消息头 +数据长度+数据
                String message = "Message"+i;
                //消息头 4个字节
                ByteBuf byteBuf = Unpooled.buffer();
                byteBuf.writeInt(102); //【lengthFieldOffset】  4个字节
                // 102 测试随便写的 只要是占4个字节的数字就行 为啥写个102就4个字节 因为102 是int类型 int类型的字节数是4个字节
                // 这个应该是 数据类型基础  int string long  等等 占几个字节的问题

                //数据长度 4个字节  长度也是 int型 所以跟消息头 一样 所以在设置解码器长度参数的时候 【lengthFieldLength】 4个字节就够用
                byteBuf.writeInt(message.getBytes().length);

                //发送的数据
                byteBuf.writeBytes(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8));

                channel.writeAndFlush(byteBuf);
               // Thread.sleep(100); // 可能需要调整间隔时间
            }

            channel.closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

回调类

package com.netty.server.tpcServer;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * User:Json
 * Date: 2024/4/12
 **/
public class ServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 客户端数据到来时触发
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        //第一种到第三种解码器测试
//        System.out.println("client request: " + buf.toString(CharsetUtil.UTF_8)+"======");
//        SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//        String callback = sf.format(new Date());
//        ctx.write(Unpooled.copiedBuffer(callback.getBytes()));

        //第四种 自定义解码器测试

        System.out.println("========================");
       // 获取全部数据
//        System.out.println("消息头:"+buf.readInt());
//        int i = buf.readInt();
//        System.out.println("数据长度:"+i);
//        System.out.println("消息体:"+buf.readBytes(i).toString(CharsetUtil.UTF_8));

        //只获取消息体里的数据
        System.out.println("只获取消息体: "+buf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

        // 将发送缓冲区的消息全部写到SocketChannel中
        ctx.flush();
    }

    /**
     * 发生异常时触发
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println(cause.getMessage());
        cause.printStackTrace();
        // 释放与ChannelHandlerContext相关联的资源
        ctx.close();
    }
}


springboot 启动时运行

package com.netty.server.init;

import com.netty.server.tpcServer.NettyServer;
import io.netty.channel.ChannelFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * User:Json
 * Date: 2024/4/12
 **/
@Component
@Slf4j
@Order(2)
public class NettyServerInit implements CommandLineRunner {

    @Resource
    private NettyServer nettyServer;

    // 这个 线程  后续优化    比如关闭 等等
    @Override
    public void run(String... args) throws Exception {
        // 开启服务
        ChannelFuture future = nettyServer.start("192.168.0.209", 17070);
        // 在JVM销毁前关闭服务
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                nettyServer.close();
            }
        });
        future.channel().closeFuture().sync();
    }
}

核心测试代码 在 NettyServer 类里

下面逐个分享一下解码器的 意思 :
第一种:

new LineBasedFrameDecoder(1024)

1024 就是规定传入的 字节长度的大小
对于字节 一个字符串占多少字节 int 占多多少字节 long 占多多少字节 如果明白这个 应该很好理解
LineBasedFrameDecoder:传入的参数是消息最大长度,发送消息的大小必须小于设置值
行分隔符解码器(结尾根据 “\n” 作为结束标识)

第二种:

new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("|".getBytes()))

DelimiterBasedFrameDecoder 自定义分割器解码器,结尾根据什么作为结束标识可以自定义 比如用 |

第三种:

new FixedLengthFrameDecoder(7)

// 固定长度解码器,发送的消息需要定长

第四种:重点

new LengthFieldBasedFrameDecoder(1024,4,4,0,0)

第四种:LengthFieldBasedFrameDecoder:基于长度的自定义解码器,比较灵活 这个
这个 解码器 一共有 5个参数
maxFrameLength:最大帧长度。也就是可以接收的数据的最大长度。如果超过,此次数据会被丢弃
lengthFieldOffset:长度域偏移量。存储数据长度的一个偏移量
lengthFieldLength:长度域字节数。存储数据长度的一个大小
lengthAdjustment:数据长度修正。因为长度既可以代表data的长度,也可以是整个消息的长度
initialBytesToStrip:跳过的字节数。可以选择舍弃一部分数据
可以理解为 我们给数据定义传输规则 就像http请求 定义规则 比如 请求头 数据结构 json 等等
所以我们作为接收数据的服务端 可以在开发接收数据的时候 定义规则 然后让客户端按照规则来传输数据
比如 发送一个消息,我们定义的结构为:消息头+数据长度+数据

对于字节 一个字符串占多少字节 int 占多多少字节 long 占多多少字节 如果明白这个 应该很好理解

我们怎么定义规则呢 比如:
整体字节为 15个字节
要发送的数据 “Message” 长度为 7个字节
我们规定 maxFrameLength :1024 接收的最大数据,
lengthFieldOffset:长度域偏移量 一般用来定义消息头 4个字节、
lengthFieldLength:长度域字节数 用于定义数据长度(length)的【值】的大小 4个字节
lengthAdjustment : 0 数据长度修正
假设我设置的数据长度是20,代表了整个消息体的长度,但是我数据却只有12个字节,这往后读20个字节无疑是错的,所以我们需要修正,怎么修正? 减8 就行
所以如果你需要修正你的 数据长度,那么lengthAdjustment就是用来修正的。
initialBytesToStrip :8 ,如果在我们业务层只需要 消息体 像消息头 和 数据长度都不需要 那么 initialBytesToStrip就是用来跳过的字节数。
就比如 消息头规定 4个字节 数据长度 4个字节 所以我们如果只要 消息体里的内容 就需要跳过 8个字节 所以设置8 即可
结合发送端和 接收数据端 一个例子 整体

接收数据端的 定义

 socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,4,4,0,8));

发送端

     //第四种 自定义解码器
      //[4个字节 + 4个字节 + 数据]
      // 我们传输数据 就要这样拼接数据 消息头 +数据长度+数据
      String message = "Message"+i;
      //消息头 4个字节
     ByteBuf byteBuf = Unpooled.buffer();
 byteBuf.writeInt(102); //【lengthFieldOffset】  4个字节
 // 102 测试随便写的 只要是占4个字节的数字就行 为啥写个102就4个字节 因为102 是int类型 int类型的字节数是4个字节
      // 这个应该是 数据类型基础  int string long  等等 占几个字节的问题

//数据长度 4个字节  长度也是 int型 所以跟消息头 一样 所以在设置解码器长度参数的时候 【lengthFieldLength】 4个字节就够用
  byteBuf.writeInt(message.getBytes().length);

   //发送的数据
  byteBuf.writeBytes(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8));

 channel.writeAndFlush(byteBuf);

测试结果
没有解码器的情况
在这里插入图片描述

有了之后:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深圳技术大学oj C : 生成r子集

Description 输出给定序列按字典序的 &#xfffd; 组合&#xff0c;按照所有 &#xfffd; 个元素出现与否的 01 标记串 &#xfffd;&#xfffd;&#xfffd;&#xfffd;−1,...,&#xfffd;1 的字典序输出. 此处01串的字典序指&#xff1a;先输入的数字对应低位&#x…

SOBEL图像边缘检测器的设计

本项目使用FPGA设计出SOBEL图像边缘检测器&#xff0c;通过分析项目在使用过程中的工作原理和相关软硬件设计进行分析详细介绍SOBEL图像边缘检测器的设计。 资料获取可联系wechat 号&#xff1a;comprehensivable 边缘可定义为图像中灰度发生急剧变化的区域边界,它是图像最基本…

Rust 跨平台-Android 和鸿蒙 OS

1. 安装 rustup rustup 是 Rust 的安装和版本管理工具 $ curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh 该命令会安装 rusup 和最新的稳定版本的 Rust&#xff1b;包括&#xff1a; rustc Rust 编译器&#xff0c;用于将 Rust 代码编译成可执行文件或库。 ca…

记录在Linux(龙蜥8.6)配置jdk环境变量不生效问题

在Linux中&#xff0c;将jdk解压至 /opt/soft/jdk 目录中&#xff0c;使用root用户配置在/etc/profile中配置环境变量并source后&#xff0c;使用root用户是正常的&#xff0c;但是切换到普通用户后提示&#xff1a; 这个问题是普通用户对 /opt/soft/jdk 目录无执行权限 解决&…

【精选】数据治理项目实施(合集)05——解码“数据架构”,数据架构包含哪些内容?

上一篇讲到了数据治理项目的前期调研工作&#xff0c;继数据调研工作完成之后&#xff0c;就要开始关于治理工作的各项方案设计&#xff0c;整体方案设计包括数据架构、元数据、主数据、数据质量、数据安全、指标标签体系、数据生命周期管理和管理评价等内容。这一篇重点讲一下…

4面体空间内直链4点结构分布与占比

在30个点的4面体空间内取4个点&#xff0c;有30*29*28*27/2427405种取法&#xff0c;要求得到的4个点必须在直链上。只有144个结构符合要求&#xff0c;在平移操作下不重合的结构有36个。 这36个结构可以按照旋转对称性进一步分成3组0&#xff0c;1&#xff0c;4&#xff0c;每…

SaaS客户裂变:如何构建合作伙伴的双向沟通桥梁

在SaaS行业中&#xff0c;客户裂变不仅是增长的关键&#xff0c;更要求与合作伙伴之间建立稳固的沟通桥梁。如何构建合作伙伴双向沟通的桥梁&#xff0c;真正做到理解对方的价值需求&#xff0c;实现长期合作共赢呢&#xff1f; 一、明确价值共享 首先&#xff0c;确保双方明…

MK米客方德SD NAND磨损均衡技术

上次MK给大家讲解了MK SD NAND异常掉电保护机制&#xff0c;不少的工程师朋友们对此挺感兴趣&#xff0c;今天再和大家聊一聊SD NAND内部的另外一个核心技术SD NAND&#xff1a;磨损均衡&#xff08;Wear Leveling&#xff09;。 SD NAND内部主要由NAND Flash和Flash Controll…

秋招季的策略与行动指南:提前布局,高效备战,精准出击

6月即将进入尾声&#xff0c;一年一度的秋季招聘季正在热火进行中。对于即将毕业的学生和寻求职业发展的职场人士来说&#xff0c;秋招是一个不容错过的黄金时期。 秋招的序幕通常在6月至9月间拉开&#xff0c;名企们纷纷开启网申的大门。在此期间&#xff0c;求职备战是一个系…

stm32学习笔记---TIM输入捕获(理论部分)

目录 输入捕获简介 频率测量 测频法 测周法 测频法和测周法的区别 中界频率 如何实现测周法 输入捕获的各部分电路 电路执行的细节 主从触发模式 输入捕获基本结构 PWMI基本结构 声明&#xff1a;本专栏是本人跟着B站江科大的视频的学习过程中记录下来的笔记&#…

检索增强生成RAG系列1--RAG的实现

大模型出现涌现能力之后&#xff0c;针对大模型的应用也如雨后春笋般。但是&#xff0c;在大模型真正落地之前&#xff0c;其实还需要做好最后一公里&#xff0c;而这个最后一公里&#xff0c;其中不同应用有着不同的方法。其中prompt、微调和RAG都是其中方法之一。本系列就是针…

C++11 右值引用和移动语义,完美转发和万能引用,移动构造和移动赋值,可变参数模板,lambda表达式,包装器

文章目录 C11简介统一的列表初始化&#xff5b;&#xff5d;初始化std::initializer_list声明autodecltypenullptr 范围for循环 智能指针STL中一些变化右值引用和移动语义左值引用和右值引用左值引用与右值引用比较 右值引用使用场景和意义右值引用引用左值及其一些更深入的使用…

Spring框架FactoryBean接口的作用和应用

一、FactoryBean源码解读 FactoryBean<T> 是 Spring 框架 beans.factory包中的一个接口&#xff0c;从字面意思可以理解为工厂bean&#xff0c;它是干什么的&#xff0c;类名上的泛型又是指什么&#xff0c;有什么作用&#xff1f; 注释看不懂没关系&#xff0c;先看一…

一键智控,舒适无限:网关在风机盘管智能温控中的应用

风机盘管智能控制系统采用钡铼技术系列无线网关&#xff0c;搭配各类风机设备及传感器组成无线物联中央空调室内机管理系统&#xff0c;实现整个办公楼的空调环境智能化管理。在建筑舒适度的前提下&#xff0c;降低能耗&#xff0c;避免能源浪费。 网关通信接口采用无线传输的…

上班族要怎么挑选智能猫砂盆?今年最受欢迎的牌子都在这里了!

对于上班族来说&#xff0c;猫砂盆里的猫屎到底该如何是好&#xff0c;放到下班回来再铲&#xff0c;猫砂的臭味早就飘满屋子&#xff0c;想立刻铲掉吧&#xff0c;班不要上啦&#xff1f;可是不铲就会生细菌&#xff0c;谁也不想花个几千块去给猫咪看病吧&#xff0c;谁不希望…

PointMamba: A Simple State Space Model for Point Cloud Analysis

1. 论文基本信息 2. 创新点 介绍了第一个状态空间模型 PointMamba&#xff0c;将其应用与点云分析。PointMamba 表现出令人印象深刻的能力&#xff0c;包括结构简单性&#xff08;例如&#xff0c;vanilla Mamba&#xff09;、低计算成本和知识可迁移性&#xff08;例如&#…

大数据处理引擎选型之 Hadoop vs Spark vs Flink

随着大数据时代的到来&#xff0c;处理海量数据成为了各个领域的关键挑战之一。为了应对这一挑战&#xff0c;多个大数据处理框架被开发出来&#xff0c;其中最知名的包括Hadoop、Spark和Flink。本文将对这三个大数据处理框架进行比较&#xff0c;以及在不同场景下的选择考虑。…

测绘局内外网文件导入导出,怎样才能效率安全两手抓?

测绘局负责进行各种基础测绘工作&#xff0c;如地形测量、地籍测绘、海洋测绘等&#xff0c;获取并更新国家基础地理信息数据。这些数据是国民经济建设、城市规划、资源调查、环境保护等各个领域的重要基础资料。对于维护国家地理信息安全、促进国民经济和社会发展具有重要意义…

工业边缘计算网关

1 介绍 HINETG系列边缘计算网关&#xff08;Linux操作系统&#xff09;&#xff0c;是华辰智通的—款面向工业现场设备接入、数据采集、设备监控的工业级边缘计算网关。采用ARM Cortex-A7 800MHz高性能CPU,拥有以太网、串口、CAN口、IO口等丰富的接口&#xff0c;支持以太网、…

专业软件测试公司分享:安全测评对于软件产品的重要性

在互联网普及的今天&#xff0c;随着各类软件的大规模使用&#xff0c;安全问题也变得愈发突出。因此&#xff0c;对软件进行全面的安全测评&#xff0c;不仅可以有效保障用户的信息安全&#xff0c;还能提升软件产品的信任度和市场竞争力。 安全测评对于软件产品的重要性就如…