栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【实现netty服务同时监听多个端口,处理多套协议】

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【实现netty服务同时监听多个端口,处理多套协议】

实现netty服务同时监听多个端口,处理多套协议
  • Netty
    • 1. 实现Netty服务端
    • 2. 通道初始化
    • 3. 创建对应的解析器和编码器
    • 4. 编写服务端处理器
    • 5. Netty服务启动类
    • 结尾

Netty

netty一个提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
在上一篇文章中介绍了Netty怎么去实现和硬件双工通信:https://blog.csdn.net/GBK_8/article/details/123256716?spm=1001.2014.3001.5502

1. 实现Netty服务端
@Slf4j
@Component
@RefreshScope
public class NettyServerOfLims {
    
    @Autowired
    private PortDefinition portDefinition;

    ChannelFuture future = null;

    NioEventLoopGroup boss = null;

    NioEventLoopGroup worker = null;

    ServerBootstrap bootstrap = new ServerBootstrap();

    public void start() {
        boss = new NioEventLoopGroup();
        worker = new NioEventLoopGroup();
        //1.绑定group
        bootstrap.group(boss, worker)
                //2.设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel
                .channel(NioServerSocketChannel.class)
                //3.保存连接数
                .option(ChannelOption.SO_BACKLOG, 1024)
                //4.有数据立即发送
                //.option(ChannelOption.SO_SNDBUF, 4 * 1024)
                //.option(ChannelOption.SO_RCVBUF, 4 * 1024)
                .handler(new LoggingHandler(LogLevel.INFO))
                //5.保持连接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //6.handler作用于boss -- childHandler作用于worker
                .childHandler(new SocketChannelInitHandler(portDefinition.getPort()));

        Map ports = portDefinition.getPort();
        log.info("netty服务器在{}端口启动监听", JSONObject.toJSONString(ports));
        try {
             
            for (Map.Entry p : ports.entrySet()) {
                final int port = p.getKey();
                // 绑定端口
                ChannelFuture future1 = bootstrap.bind(new InetSocketAddress(port)).sync();
                future1.addListener(future -> {
                    if (future.isSuccess()) {
                        log.info("netty 启动成功,端口:{}", port);
                    } else {
                        log.info("netty 启动失败,端口:{}", port);
                    }
                });
                future1.channel().closeFuture().addListener((ChannelFutureListener) channelFuture -> future1.channel().close());
            }
        } catch (Exception e) {
            log.error("netty 启动时发生异常-------{}", e);
        }
    }

    @PreDestroy
    public void stop() {
        if (future != null) {
            future.channel().close().addListener(ChannelFutureListener.CLOSE);
            future.awaitUninterruptibly();
            boss.shutdownGracefully();
            worker.shutdownGracefully();
            future = null;
            log.info(" 服务关闭 ");
        }
    }
}

端口配置类

@Data
@Configuration
@ConfigurationProperties(prefix = "netty")
public class PortDefinition {

    Map port;
}
2. 通道初始化
@Slf4j
public class SocketChannelInitHandler extends ChannelInitializer {


    
    public static final Map CHANNEL_MAP = new ConcurrentHashMap<>();


    Map ports;

    public SocketChannelInitHandler(Map ports) {
        this.ports = ports;
    }

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        //每次连接上来 对通道进行保存
        CHANNEL_MAP.put(socketChannel.id(), socketChannel.pipeline());
        ChannelPipeline pipeline = socketChannel.pipeline();
        int port = socketChannel.localAddress().getPort();
        String type = ports.get(port);
        //不同类型连接,处理链中加入不同处理协议
        switch (type) {
            case GatewayType.LIMS:
                pipeline.addLast(
                        new AMsgDecoder(),
                        new AMsgEncoder(),
                        new AServerHandler());
                break;
            case GatewayType.EXPLOSION_PROOF:
                //防爆
                pipeline.addLast(
                        new BMsgDecoder(),
                        new BMsgEncoder(),
                        new BServerHandler());
                break;
            case GatewayType.NON_EXPLOSION_PROOF:
                pipeline.addLast(
                        new CExpMsgDecoder(),
                        new CExpMsgEncoder(),
                        new CExpServerHandler());
                break;
            default:
                log.error("当前网关类型并不存在于配置文件中,无法初始化通道");
                break;
        }
        pipeline.addLast(
                new StringEncoder(StandardCharsets.UTF_8),
                new StringDecoder(StandardCharsets.UTF_8));
    }
}

3. 创建对应的解析器和编码器

3.1 信息解析器

@Slf4j
public class AMsgDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List list) throws Exception {
        //读取数据信息
        byte[] data = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(data);
        StringBuilder msg = new StringBuilder();
        for (byte aByte : data) {
            msg.append(String.format("%02x", aByte).toUpperCase());
        }
        if (StringUtils.isEmpty(msg.toString())) {
            log.error("接收的报文为空!");
        }
        log.info("<----初始数据: {} ", msg);
        assemblyResult(ctx, msg.toString(), list);
    }

    private void assemblyResult(ChannelHandlerContext ctx, String data, List list) {
        List limsMessageList = analysisMessageOfLims(data);
        list.addAll(pclMessage);
    }

    
    private List analysisMessageOfLims(String data) {
       // 解析报文,具体解析方式跟硬件约定协议有关。
       
    }
}
 

3.2 信息编码器

@Slf4j
public class AMsgEncoder extends MessageToByteEncoder {

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object o, ByteBuf byteBuf) throws Exception {
        SendData message = (SendData) o;
        //拼接需要发送的数据信息
        log.info("----> 初始数据【{}】 : {}", message.getCode(), msg);
        //将字符类型转换成字节类型
        byte[] bytes = DataTypeConvert.hexStringToBytes(msg.toString());
        //写入字节缓冲区
        byteBuf.writeBytes(bytes);
    }
}
4. 编写服务端处理器
//继承SimpleChannelInboundHandler的好处在于可以直接拿到解析后的实体类对象。
@Slf4j
@ChannelHandler.Sharable
public class AServerHandler extends SimpleChannelInboundHandler {

    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    
    private static final Map CHANNEL_MAP = new ConcurrentHashMap<>();

    public static Map getChannelMap() {
        return CHANNEL_MAP;
    }

    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Result message) throws Exception {
        String gwCode = message.getCode();
        ChannelId channelId = ctx.channel().id();
        CHANNEL_MAP.put(gwMac, channelId);
        
    }

    
    public static void closeChannel(String gwCode) {
        CHANNEL_MAP.remove(gwCode);
    }

    
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("新增网关设备连接成功,远端地址为:{}", ctx.channel().remoteAddress());
    }

    
    public void send(String code, String msg) {
        if (CHANNEL_MAP.containsKey(code)) {
            ChannelPipeline pipeline = SocketChannelInitHandler.CHANNEL_MAP.get(CHANNEL_MAP.get(code));
            if (pipeline == null) {
                //
                SocketChannelInitHandler.CHANNEL_MAP.remove(CHANNEL_MAP.get(code));
            }
            assert pipeline != null;
            pipeline.writeAndFlush(msg);
        } else {
            System.out.println("-------设备已经断开连接-------");
        }
    }


    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
        ChannelId id = ctx.channel().id();
        //关闭
        SocketChannelInitHandler.CHANNEL_MAP.remove(id);
        log.info("网关: {} 服务端连接关闭...", ctx.channel().remoteAddress());
    }

    
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("remoteAddress: {},连接异常:{}", ctx.channel().remoteAddress(), cause);
    }
5. Netty服务启动类
@Configuration
public class InitNettyServerConfig implements CommandLineRunner {


    @Autowired
    private NettyServer nettyServer;

    @Autowired
    public void setNettyServer(NettyServer nettyServer) {
        this.nettyServer = nettyServer;
    }

    @Override
    public void run(String... args) throws Exception {
        nettyServer.start();
    }
}
结尾

到此netty服务同时监听多个端口,处理多套协议实现完毕,在具体的项目中碰到过许许多多的问题,后面有时间也会编写出来,记录分享。

暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号