netty实现一个简单的聊天室

使用netty实现一个简单的聊天室

需求

当有用户连接时广播消息=>XXX加入聊天室,用户断开连接时广播消息=>XXX离开聊天室,并且用户发送消息时,服务端进行广播

Server端实现

Server代码:

public class ChatServer {
    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap cb = new ServerBootstrap();
        try {
            //这里直接用匿名内部类构造channel初始化类
            cb.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()))//分割器,分割每行数据
                            .addLast(new StringDecoder(CharsetUtil.UTF_8))
                            .addLast(new StringEncoder(CharsetUtil.UTF_8))
                            .addLast(new ChatServerHandler());
                }
            });
            ChannelFuture channelFuture = cb.bind( 9999).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

handler代码:

public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
    //这里是一个channel容器,用于管理所有没有断开的连接,广播消息
    static ChannelGroup cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        Channel me = ctx.channel();
        System.out.println("来自=>"+me.remoteAddress()+"的消息:"+msg+"\n");
        cg.forEach(ch->{
            if(me != ch){
                ch.writeAndFlush("来自=>"+me.remoteAddress()+"的消息:"+msg+"\n");
            }
            else {
                ch.writeAndFlush("自己:"+msg+"\n");
            }
        });
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        cg.writeAndFlush("[系统提示]-用户:"+channel.remoteAddress()+"-加入聊天室\n");
        cg.add(channel);
        System.out.println(channel.remoteAddress()+" 上线,当前容量:"+cg.size());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        cg.writeAndFlush("[系统提示]-用户:"+channel.remoteAddress()+"-离开聊天室\n");
        //cg.remove(channel); ChannelGroup 容器会自动清除断开的连接,所以不需要手动断开
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        cg.writeAndFlush("[系统提示]-用户:"+channel.remoteAddress()+"-上线\n");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        cg.writeAndFlush("[系统提示]-用户:"+channel.remoteAddress()+"-下线\n");
    }
}

上述所有发送的消息最后都加了一个换行符,是因为我们下面配置客户端channel的时候也要要求数据带换行分来进行分割,若不换行它会认为这条消息没有处理完,不会放行到后面执行

客户端实现

Server代码:

public class ClientServer {
    public static void main(String[] args) {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap cb = new Bootstrap();
        try {
            //这里直接用匿名内部类构造channel初始化类
            cb.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()))
                            .addLast(new StringDecoder(CharsetUtil.UTF_8))
                            .addLast(new StringEncoder(CharsetUtil.UTF_8))
                            .addLast(new ChatClientHandler());
                }
            });

            Channel channel = cb.connect("localhost", 9999).sync().channel();
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                channel.writeAndFlush(br.readLine()+"\r\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

handler代码:

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

由于客户端接收消息只需要显示就行了,所以handler很简单

运行

我们首先启动服务端,然后依次开三个客户端
第一个客户端连接时:
服务端

客户端1

第二个客户端连接时
服务端

客户端1

客户端2

第三个客户端连接时
服务端

客户端1

客户端2

客户端3

客户端3发送消息时
客户端1

客户端2

客户端3

客户3断开时
客户端1

客户端2

总结

这是个很经典的程序,这里也主要用了一个DelimiterBasedFrameDecoder 的handler初始化channel的管道,使其分割换行发送消息

posted @ 2020-12-18 23:15  DirtyShady  阅读(265)  评论(0)    收藏  举报