zoukankan      html  css  js  c++  java
  • Netty 多客户端连接与通信

    实现场景: 聊天

    服务端,客户端A,客户端B,客户端C。当客户端发送消息给服务端后,服务端在将这条消息广播个所有客户端户端A,客户端B,客户端C。

    需求1: 客户端上线后,会通知所有客户端上线。

    如客户端A先建立连接,不需要通知。

    当客户端B与服务端建立连接,服务端告诉A,客户端B上线。

    A和B建立连接后,客户端C和服务端建立连接。服务端广播一条信息给A和B。

    需求2: A、B、C都已经建立连接,当A发送一条给服务端,服务端广播这条消息给所有客户端,客户端A会提示这条消息是自己发送的。

    一、服务端程序的编写

    1、MyChartServer 类

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

      

      

    2、MyChatServerInitializer 类

    public class MyChatServerInitializer extends ChannelInitializer<SocketChannel>{
    
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            ChannelPipeline pipeline = socketChannel.pipeline();
            pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
            pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
            pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
            pipeline.addLast(new MyChartServerHandle());
        }
    }
    

      

    3、MyChartServerHandle 类

    public class MyChartServerHandle extends SimpleChannelInboundHandler<String>{
    
    
        //用于保存所有Channel对象
        private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            //channel为当前客户端发给服务端的channel
            Channel channel = ctx.channel();
            channelGroup.forEach(ch -> {
    
                if(channel != ch){
                    ch.writeAndFlush(channel.remoteAddress() + " 发送的消息:" + msg + "
    ");
                } else{
                    ch.writeAndFlush("[自己]" + msg + " 
    ");
                }
            });
    
        }
    
        //表示连接建立
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
           //chanel可以理解成Connection
            Channel channel = ctx.channel();
            //广播消息给所有的客户端
            channelGroup.writeAndFlush("[服务器] - " + channel.remoteAddress() + " 加入
    ");
            channelGroup.add(channel);
        }
    
        //表示连接断掉了
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            //广播消息给所有的客户端
            channelGroup.writeAndFlush("[服务器] - " + channel.remoteAddress() + " 离开
    ");
            //下面这行代码Netty会自动调用
            //channelGroup.remove(channel);
        }
    
        //表示连接时活动状态
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            //广播消息给所有的客户端
            CommonUtil.println( channel.remoteAddress() + " 上线 
    ");
        }
    
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            //广播消息给所有的客户端
            CommonUtil.println( channel.remoteAddress() + " 下线 
    ");
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();;
            ctx.close();
        }
    }
    

      

      二、客户端程序编写

    1、MyChatClient类

    public class MyChatClient {
        public static void main(String[] args) throws  Exception{
            EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
            try {
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                        .handler(new MyChatClientInitializer());
    
                //channel表示与服务端的Connection
                Channel channel = bootstrap.connect("localhost",8899).sync().channel();
                //不断读取客户端输入的内容
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    
                for(;;){
                    channel.writeAndFlush(br.readLine() + "
    ");
                }
    
            }finally {
                eventLoopGroup.shutdownGracefully();
            }
        }
    }
    

      

      2、MyChatClientInitializer  类

    public class MyChatClientInitializer  extends ChannelInitializer<SocketChannel> {
    
    
    
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
            pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
            pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
            pipeline.addLast(new MyChartClientHandle());
        }
    }
    

      

    3、MyChartClientHandle 类

    public class MyChartClientHandle extends SimpleChannelInboundHandler<String> {
    
        // 对于客户端来说,输入来自控制台
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            //仅仅打印来自服务端的信息
            CommonUtil.println(msg);
    
        }
    
    
    }
    

      

    三、测试

    1、启动MyChartServer,然后启动MyChatClient

    MyChartServer打印 59786 上线

    2、再启动一个客户端

    可以发现60635 上线,

    并且第一个客户端提示 60635 加入

    3、再启动一个客户端

    提示60966上线

    第一个客户端 提示60966 加入

    第二个客户端 提示60966 加入

    四、测试2

    1、客户端A发送消息: 大家好,我是客户端A

    客户端A接收到写信息:  [自己]大家好,我是客户端A 

    客户端B接收到信息

    客户端C接收到的信息

    自此,实现了通过多个Socket实现的通信的过程

  • 相关阅读:
    什么是理想?
    leetcode 62. 不同路径-动态规划及优化,双100%
    使用双指针暴力解决力扣28题《实现 strStr()》
    使用双指针解决力扣27题《移除元素》
    SQL SERVER 数据库日志已满时清理日志的方法
    修改git提交的名字和邮箱
    React Native运行出现Could not find "iPhone X" simulator
    eosio 编译与部署
    恢复经常写博客的习惯
    MAC OS系统替换homebrew使用阿里云或中科大的镜像源
  • 原文地址:https://www.cnblogs.com/linlf03/p/11296379.html
Copyright © 2011-2022 走看看