03_netty实现聊天室功能

【概述】

聊天室主要由两块组成:聊天服务器端(ChatRoomServer)和聊天客户端(ChatClient)。

[ 聊天服务器(ChatRoomServer)功能概述 ]  

1.监听所有客户端的接入、断线

2.有客户端A接入聊天室时,将接入消息发给除了客户端A的其他客户端

3.当客户端A退出聊天室时,将退出消息发给除了客户端A的其他客户端

4.当客户端A发送消息到聊天室时,将消息转发给除了客户端A的其他客户端

[ 聊天客户端(ChatClient)功能概述 ]

1.发送消息至聊天服务器

2.接收聊天服务器发送过来的所有消息

 

【聊天服务端 ChatRoomServer】

/**
 * 聊天室服务端
 */
public class ChatRoomServer {

    private final int port ;


    public ChatRoomServer(int port) {
        this.port = port;
    }

    public void start(){
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try{
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChatServerInitialize())
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .option(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new ChatRoomServer(9999).start();  //服务端监听本地的9999端口
    }
}

【ChatServerInitialize】

public class ChatServerInitialize extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        System.out.println("用户【"+channel.remoteAddress()+"】接入聊天室......");
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder",new StringDecoder());
        pipeline.addLast("encoder",new StringEncoder());
        pipeline.addLast("handler",new ChatServerHandler());
    }
}

【ChatServerHandler】

/**
 * 聊天服务器对各种情况的处理
 */
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {

    public static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 当从服务端收到新的客户端连接时
     * 客户端的 Channel 存入 channels 列表中,并通知列表中的其他客户端 Channel
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel clientChannel = ctx.channel();
        channels.add(clientChannel);
        for (Channel ch : channels) {
            if (ch != clientChannel) {  //通知除了自己以外的其他用户
                ch.writeAndFlush("【提示】:用户【" + clientChannel.remoteAddress() + "】进入聊天室...\n");
            }
        }
    }


    /**
     * 每当从服务端收到客户端断开时
     * 客户端的 Channel 自动从 channels 列表中移除了,并通知列表中的其他客户端 Channel
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel clientChannel = ctx.channel();
        channels.remove(clientChannel);
        for (Channel ch : channels) {
            if (ch != clientChannel) {  //通知除了自己以外的其他用户
                ch.writeAndFlush("【提示】:用户【" + clientChannel.remoteAddress() + "】退出聊天室...\n");
            }
        }
    }

    /**
     * 接受到客户端发出的消息
     * 判断channel是否是
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        Channel clientChannel = ctx.channel();
        for (Channel ch : channels) {
            if (ch != clientChannel) {
                ch.writeAndFlush("用户【" + clientChannel.remoteAddress() + "】说:" + msg + "\n");
            } else {
                ch.writeAndFlush("【我】说:" + msg + "\n");
            }
        }
    }

    /**
     * 服务端监听到客户端活动
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel clientChannel = ctx.channel();
        System.out.println("用户【"+clientChannel.remoteAddress()+"】在线中...");
    }

    /**
     * 服务端监听到客户端 不活动
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel clientChannel = ctx.channel();
        System.out.println("用户【 " +clientChannel.remoteAddress()+"】:离线了");

    }
}

【ChatClient 聊天客户端】

/**
 * 聊天客户端
 */
public class ChatClient {

    private final String host;

    private final int port;

    public ChatClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() {
        EventLoopGroup worker = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();

        try{
            bootstrap.group(worker)
                    .channel(NioSocketChannel.class)
                    .handler(new ClientInitializer());
            Channel channel  = bootstrap.connect(host,port).sync().channel();
            //客户端从键盘输入数据
            BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
            while(true){
                channel.writeAndFlush(input.readLine()+"\n");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            worker.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new ChatClient("127.0.0.1",9999).start(); //连接服务器端
    }

}

【ChatClientInitializer 】

public class ChatClientInitializer extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        //当有客户端连接服务器时,netty会调用这个初始化器的 initChannel方法
        System.out.println("客户端开始初始化......");

        ChannelPipeline pipeline = socketChannel.pipeline();

        pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder",new StringDecoder());
        pipeline.addLast("encoder",new StringEncoder());
        pipeline.addLast("handler",new ChatClientHandler());
    }
}

【ChatClientHandler】

public class ChatClientHandler extends SimpleChannelInboundHandler<String> {

    /**
     * 打印服务端发送过来的数据
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println(s);
    }
}

【运行结果】

 [1.启动聊天服务器]

 

[2.启动一个客户端A]

 

 [3.再启动一个客户端B]

 

[4.客户端A发送消息]

 

[5.客户端A关闭]

 

 

posted @ 2019-01-27 18:28  HigginCui  阅读(1249)  评论(1编辑  收藏  举报