Netty:创建多人聊天室

439 阅读2分钟

用netty来简单实现一个聊天室功能。

  • 项目所需依赖
<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.50.Final</version>
</dependency>

客户端

   public class server {
   public static void main(String[] args) {
       EventLoopGroup boss = new NioEventLoopGroup(1);
       EventLoopGroup work = new NioEventLoopGroup();
       try {
           ServerBootstrap bootstrap = new ServerBootstrap();
           bootstrap.group(boss,work)
                   .channel(NioServerSocketChannel.class)
                   .option(ChannelOption.SO_BACKLOG,1024)
                   .childHandler(new ChannelInitializer<SocketChannel>() {
                       @Override
                       protected void initChannel(SocketChannel socketChannel) throws Exception {
                           ChannelPipeline pipeline = socketChannel.pipeline();
                           pipeline.addLast("decoder",new StringDecoder());// 解码器
                           pipeline.addLast("encoder",new StringEncoder());//编码器
                           pipeline.addLast(new NettyServerHandler());
                       }
                   });
           ChannelFuture sync = bootstrap.bind(8888).sync();// netty是一个非阻塞异步  在此处最好保证先执行完 在用服务器做业务 所以加上同步方法sync()
           System.out.println("聊天室启动成功");
           sync.channel().closeFuture().sync();
       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           boss.shutdownGracefully();
           work.shutdownGracefully();
       }
   }
}

客户端handler

public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
// channelGroup保存了所有和服务器连接的channel 
// 可以调用的channelgroup的size方法来查看当前多少连接者
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    // 在读取到消息时 触发此方法
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.forEach(ch->{
        // 区分当前用户和其他用户
            if(ch!=channel){
                ch.writeAndFlush("客户端:"+channel.remoteAddress()+"发送了消息"+s+"\n");
            }else {
                ch.writeAndFlush("自己:"+channel.remoteAddress()+"发送了消息"+s+"\n");
            }
        });
    }

    // 有新建链接netty会自动调用这个接口
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel=ctx.channel();
        channelGroup.writeAndFlush("客户端:"+channel.remoteAddress()+"上线了"+"  "+simpleDateFormat.format(new Date()));
        channelGroup.add(channel);
        System.out.println(channel.remoteAddress()+"上线了");
    }
    // 当有断开链接  会自动调用这个
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush("客户端:"+channel.remoteAddress()+"下线了");
        System.out.println(channel.remoteAddress()+"下线了");
    }
}

服务端

/**
 * @Author Weng
 * @Description
 * @Date 2021/1/18 20:37
 */

public class client {
    public static void main(String[] args) {
        EventLoopGroup boss = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(boss)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast("decoder",new StringDecoder());
                            pipeline.addLast("encoder",new StringEncoder());
                            pipeline.addLast(new NettyClientHandler());
                        }
                    });
                    //连接对应的服务器开放端口
            ChannelFuture sync = bootstrap.connect("127.0.0.1", 8888).sync();
            Channel channel = sync.channel();
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()){
                String s = scanner.nextLine();
                channel.writeAndFlush(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
        }
    }
}

客户端 handler

public class NettyClientHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
    // 客户端直接打印出收到的服务端消息
        System.out.println(s);
    }
}
  • 根据netty的核心线程模型,处理用户连接的是一个NioEventLoopGroup(boos),可以通过构造函数生成多个线程(一个线程对应着一个NioEventLoopGroup)来处理用户连接请求。boos会将连接后生成的channel注册到到work内的selector中去,在work中响应用户的消息处理,将所有事件丢到Popeline中去执行。Popeline中有我们自定义的handler(也是我们主要编写的业务逻辑代码处),编码器、解码器也在Popeline中注册的。
    • EventLoopGroup boss = new NioEventLoopGroup(1);
    • EventLoopGroup work = new NioEventLoopGroup();