• SpringBoot中使用Netty实现TCP通讯,服务器主动向客户端发送数据


    简述:

    Springboot项目的web服务后台,web服务运行在9100端口。

    后台使用netty实现了TCP服务,运行在8000端口。

    启动截图如下:

     

      

    pom依赖

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


    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;
    import io.netty.handler.codec.string.StringDecoder;
    import io.netty.handler.codec.string.StringEncoder;
    
    /**
     * netty服务器,主要用于与客户端通讯
     */
    public class NettyServer {
        private final int port; //监听端口
    
    
        public NettyServer(int port) {
            this.port = port;
        }
    
        //编写run方法,处理客户端的请求
        public void run() throws Exception {
    
            //创建两个线程组
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup(); //8个NioEventLoop
    
            try {
                ServerBootstrap b = new ServerBootstrap();
    
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 128)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                //获取到pipeline
                                ChannelPipeline pipeline = ch.pipeline();
                                //向pipeline加入解码器
                                pipeline.addLast("decoder", new StringDecoder());
                                //向pipeline加入编码器
                                pipeline.addLast("encoder", new StringEncoder());
                                //加入自己的业务处理handler
                                pipeline.addLast(new NettyServerHandler());
    
                            }
                        });
                System.out.println("netty服务器启动成功(port:" + port + ")......");
                ChannelFuture channelFuture = b.bind(port).sync();
                //监听关闭
                channelFuture.channel().closeFuture().sync();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
    
        }
    
    }
    

    netty业务处理handler

    import io.netty.channel.Channel;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.channel.group.ChannelGroup;
    import io.netty.channel.group.DefaultChannelGroup;
    import io.netty.util.concurrent.GlobalEventExecutor;
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
    
        //定义一个channle 组,管理所有的channel
        //GlobalEventExecutor.INSTANCE) 是全局的事件执行器,是一个单例
        public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    
        /**
         * 有客户端与服务器发生连接时执行此方法
         * 1.打印提示信息
         * 2.将客户端保存到 channelGroup 中
         */
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            System.err.println("有新的客户端与服务器发生连接。客户端地址:" + channel.remoteAddress());
            channelGroup.add(channel);
        }
    
        /**
         * 当有客户端与服务器断开连接时执行此方法,此时会自动将此客户端从 channelGroup 中移除
         * 1.打印提示信息
         */
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            System.err.println("有客户端与服务器断开连接。客户端地址:" + channel.remoteAddress());
        }
    
        /**
         * 表示channel 处于活动状态
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(ctx.channel().remoteAddress() + " 处于活动状态");
        }
    
        /**
         * 表示channel 处于不活动状态
         */
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(ctx.channel().remoteAddress() + " 处于不活动状态");
        }
    
        /**
         * 读取到客户端发来的数据数据
         */
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) {
            //获取到当前channel
            Channel channel = ctx.channel();
            System.err.println("有客户端发来的数据。地址:" + channel.remoteAddress() + " 内容:" + msg);
        }
    
        /**
         * 处理异常
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            log.error("发生异常。异常信息:{}", cause.getMessage());
            //关闭通道
            ctx.close();
        }
    }
    

    Springboot项目中启动netty服务

    启动类修改:

    测试结果截图

    服务启动,及客户端连接

    客户端给服务器发送数据

    服务器给客户端发送数据

    服务器查看当前所有连接的客户端

    服务器获取到所有客户单的ip地址及端口号后,即可通过其给指定客户端发送数据

     
     
  • 相关阅读:
    Postgresql10离线安装
    Clickhouse集群安装部署
    Clickhouse建表语法、视图语法、数据表DDL(数据定义语言)、数据DML(数据操作语言)
    Clickhouse基础语法、数据类型、数据表引擎学习
    Spring4.0+Mybatis整合时占位符无法读取jdbc.properties的问题
    Code: 210. DB::NetException: Connection refused (localhost:9000)
    使用Jdbc的方式连接Clickhouse列式数据库
    Dbeaver连接不上远程服务器部署的Clickhouse问题
    Clickhouse入门学习、单机、集群安装部署
    Another Redis DeskTop Manage一款免费的Redis可视化工具
  • 原文地址:https://www.cnblogs.com/yelanggu/p/16737845.html
Copyright © 2020-2023  润新知