zoukankan      html  css  js  c++  java
  • Netty之多用户的聊天室(三)

    Netty之多用户的聊天室(三)

    一.简单说明

      笔者有意将Netty做成一个系列的文章,因为笔者并不是一个善于写文章的人,而且笔者学习很多技术一贯的习惯就是敲代码,很多东西敲着敲着就就熟了,然后再进行深入的研究,当然这种学习的方式对于有些朋友来讲,可能觉得不可思议,但是我想说的是,这只是笔者自己的学习方式而已,我并没有想把这种学习方式强加给任何人。细心的读者可能已经发现,其实Netty程序的编写风格很雷同,不同的可能就是Handler的处理。本文笔者将展示一个基于Netty的多客户端聊天室的简单demo,那么我们直接上代码吧。

    二.聊天室的实现

    2.1服务端启动程序

    public class ChartServer {
        public static void main(String[] args) throws InterruptedException {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            
            try{
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                
                serverBootstrap.group(bossGroup, workerGroup)
                               .channel(NioServerSocketChannel.class)
                               .childHandler(new ChartServerInitializer());
                
                ChannelFuture channelFuture = serverBootstrap.bind(8877).sync();
                channelFuture.channel().closeFuture().sync();
            }finally{
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }

    2.2服务端通道初始化代码

    public class ChartServerInitializer extends ChannelInitializer<SocketChannel>{
    
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            
            /**
             * DelimiterBasedFrameDecoder: 基于分隔符的帧解码器。
             * 两个参数的含义分别为:
             *     1.帧数据的最大长度。
             *  2.以XX作为分隔符, Delimiters.lineDelimiter()表示一
    或者
    作为分割符号。
             *  例如一下字符串:
             *  +--------------+
             *  | ABC
    DEF
     |
             *  +--------------+
             *  解码后的字符串为:
             *  +-----+-----+
             *  | ABC | DEF |
             *  +-----+-----+
             *  而不是:
             *  +----------+
             *  | ABC
    DEF |
             *  +----------+
             */
            pipeline.addLast("delimiterBasedFrameDecoder", new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
            pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));  //编码不指定,默认为utf-8
            pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
            pipeline.addLast(new ChartServerHandler());
        }
    }

    2.3服务端Handler处理代码

    public class ChartServerHandler extends SimpleChannelInboundHandler<String>{
    
        private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);    
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            Channel channel = ctx.channel();
            System.out.println(channelGroup.size());
            channelGroup.forEach(c -> {
                if(channel == c){  //如果自己
                    channel.writeAndFlush("【自己】: " + msg + "
    ");
                }else{ //发送给其他人的信息
                    c.writeAndFlush(channel.remoteAddress() + ": " + msg + "
    ");
                }
            });
        }
    
        //连接建立
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel(); //获取到上线的连接
            
            channelGroup.writeAndFlush("【服务器】: " + channel.remoteAddress() + "上线
    "); //服务器发送广播通知
            
            channelGroup.add(channel);  //将通道添加到组
        }
        
        //连接断开
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            channelGroup.writeAndFlush("【服务器】: " + channel.remoteAddress() + "下线了
    ");
        };
        
        //连接激活
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(ctx.channel().remoteAddress() + "上线了");
        }
        
        //连接断开
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(ctx.channel().remoteAddress() + "下线了");
        };
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }

    2.4客户端启动代码

    public class Client {
        public static void main(String[] args) throws InterruptedException, IOException {
            EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
            
            try{
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(eventLoopGroup)
                         .channel(NioSocketChannel.class)
                         .handler(new ClientInitializer());
                
                Channel channel = bootstrap.connect("localhost", 8877).sync().channel();
                
                BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                for(;;){
                    channel.writeAndFlush(reader.readLine() + "
    ");
                }
            }finally{
                eventLoopGroup.shutdownGracefully(); //优雅关闭
            }
        }
    }

    2.5客户端通道初始化代码

    public class ClientInitializer extends ChannelInitializer<SocketChannel>{
    
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            
            pipeline.addLast("delimiterBasedFrameDecoder", new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
            pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
            pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
            
            pipeline.addLast(new ClientHandler());
        }
    }

    2.6.客户端Handler代码

    public class ClientHandler extends SimpleChannelInboundHandler<String>{
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(msg);
        }
    }

    三.程序运行

    运行服务端启动代码,然后运行多个客户端启动代码,接着你就可以聊天了。

  • 相关阅读:
    Go接口特性
    go递归函数
    K8S部署Redis Cluster集群(三主三从模式) 部署笔记
    Jenkins密码忘记重置方法
    inode满了处理
    Linux 系统日常巡检脚本
    垃圾代码评析——关于《C程序设计伴侣》6.2(三)
    CRITICAL THINGKING笔记——ad hominem
    垃圾代码评析——关于《C程序设计伴侣》6.2(二)
    垃圾代码评析——关于《C程序设计伴侣》6.2(一)
  • 原文地址:https://www.cnblogs.com/miller-zou/p/6995628.html
Copyright © 2011-2022 走看看