物联网netty对接socket设备-springboot操作

一、创建springboot项目

在线生成springboot项目https://start.spring.io/
加入依赖:

  <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.36.Final</version>
        </dependency>

二、服务端

1.服务端加上@Component注解,交由Spring管理实例。

@Component
public final class PortStart {

    public static final Logger log = LoggerFactory.getLogger(PortStart.class);

    /**
     * 连接处理group
     */
    private EventLoopGroup boss = new NioEventLoopGroup();
    /**
     * 事件处理group
     */
    private EventLoopGroup worker = new NioEventLoopGroup();

    private ServerBootstrap bootstrap = new ServerBootstrap();

    /**
     * key: port
     * value: ChannelFuture
     */
    private ConcurrentHashMap<Integer, ChannelFuture> portAndChannelFutures = new ConcurrentHashMap<>();

    private List<Integer> portList = new ArrayList<>();

    public PortStart(List<Integer> list) {
        this.portList = list;
    }

    public void start() {
        // 绑定处理group
        bootstrap.group(boss, worker).channel(NioServerSocketChannel.class)
                //保持连接数
                .option(ChannelOption.SO_BACKLOG, 300)
                //有数据立即发送
                //.option(ChannelOption.TCP_NODELAY, true)
                //保持连接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //处理新连接
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        // 增加任务处理
                        ChannelPipeline p = sc.pipeline();
                        p.addLast(
                                //使用了netty自带的编码器和解码器
                                new ByteArrayEncoder(),
                                //  new ByteArrayDecoder(),
                                //心跳检测,读超时,写超时,读写超时
                                //new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS),
                                //自定义的处理器
                                new NettyServerHandler()
                        );
                    }
                });

        if (portAndChannelFutures.isEmpty()) {
            portAndChannelFutures = new ConcurrentHashMap<>(portList.size());
        }

        // 多端口绑定
        for (int i = 0; i < portList.size(); i++) {
            final int port = portList.get(i);
            ChannelFuture channelFuture = bootstrap.bind(port);
            portAndChannelFutures.put(port, channelFuture);
            addLogIfSuccess(channelFuture, port);
        }
    }

    public boolean startOnePort(int port) {
        ChannelFuture channelFuture = bootstrap.bind(port);

        portAndChannelFutures.put(port, channelFuture);
        return addLogIfSuccess(channelFuture, port).isSuccess();
    }

    //关闭单个端口的NioServerSocketChannel
    public void stopServerChannel(int port) {
        portAndChannelFutures.get(port).channel().close();
    }

    public void stopAll() {
        boss.shutdownGracefully();
        worker.shutdownGracefully();
        log.info("stop all port");
    }


    private ChannelFuture addLogIfSuccess(ChannelFuture channelFuture, int port) {
        return channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) {
                if (future.isSuccess()) {
                    log.info("Started success,port: {}", port);
                } else {
                    log.error("Started Failed,port: {}", port);
                }
            }
        });
    }

}

2.自定义socket命令下发方法,后面控制设备命令下发及端口控制

@Component
@Slf4j
public class NettyServer {

    private PortStart portStart;

    @Resource
    private ResourceConfig config;

    public void bind() {
        portStart = new PortStart(readPropertiesFile());
        portStart.start();
    }
    
     --自定义读取resources目录下properties端口文件
    public  List<Integer> readPropertiesFile()  {
        try {
            Properties props = new Properties();
            InputStream in = this.getClass().getResourceAsStream("/config/"+config.getProperty_file_name());
            props.load(new InputStreamReader(in, "UTF-8"));
            Iterator<Map.Entry<Object, Object>> iterator = props.entrySet().iterator();
            List<Integer> list = new ArrayList<>();
            while(iterator.hasNext()) {
                Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) iterator.next();
                String strValue = (String)entry.getValue();
                list.add(Integer.valueOf(strValue));
            }
            return list;
        } catch (Exception e) {
           log.error("NettyServer的错误信息:"+e.getMessage());
            return null;
        }
    }
   
    public void stopAll() {
        portStart.stopAll();
    }
    --开启指定端口
    public boolean startOnePort(int port) {
        return portStart.startOnePort(port);
    }
    
    --关闭运行中指定端口
    public void stopOnePort(int port) {
        portStart.stopServerChannel(port);
    }
    
}

3.启动类,添加 @PostConstruct注解服务启动

@Component
public class NettyStart {

    @Resource
    private NettyServer nettyServer;

    @PostConstruct
    public void run() {
        nettyServer.bind();
    }

}

三、服务端业务处理handler

服务端的生命周期以及接收客户端的信息收发和处理。这里不建议使用阻塞的操作,容易影响netty的性能。

@ChannelHandler.Sharable
@Component
@Slf4j
public class NettyServerHandler extends ChannelInboundHandlerAdapter  {

    /**
     * 客户端连接会触发
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("端口号:"+getPort(ctx)+"连接上设备");
    }

    /**
     * 客户端发消息会触发
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] data = new byte[buf.readableBytes()];
        buf.readBytes(data);
        log.info("接收到报文{}", CommonUtils.bytesToHexString(data));
        -- 使用线程池,使设备报文解析传递
        ExecutorService pool= SpringBeanUtils.getBean(TaskExecutePool.class).getFixedThreadPool();
        -- 通过端口号去区分不同设备,不同解析
        MessageConsumer consumer= SpringBeanUtils.getBean(ConsumerRoute.class).routeByPort(getPort(ctx));
        pool.execute(consumer);
    }

    /**
     * 发生异常触发
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("设备断开连接的端口号:"+getPort(ctx));
        log.error(cause.getMessage()+"ip地址:"+ctx.channel().remoteAddress().toString());
        ctx.close();
    }
    }

总结

**从整体来看,只不过是将Netty服务端交给Spring来管理启动和销毁的工作,在服务端,我们对端口加以控制,对运行时端口的管控,借以更好的控制socket设备,对设备进行解析    结果的处理及对设备的命令下发控制**
发布了4 篇原创文章 · 获赞 7 · 访问量 1392

猜你喜欢

转载自blog.csdn.net/weixin_44761211/article/details/105420626