基于Netty的简单群聊系统

客户端启动类

package me.jar.netty.groupchat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.Scanner;

/**
 * @Description
 * @Date 2021/3/1-22:43
 */
public class GroupChatServer {
    private final int port; // 监听端口

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

    // run方法,处理客户端的请求
    public void run() {
        // 创建两个线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 向管道中加入解码器
                            pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                            // 向管道中加入编码器
                            pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                            // 加入自己的业务处理handler
                            pipeline.addLast("chatHandler", new GroupChatServerHandler());
                        }
                    });

            ChannelFuture cf = serverBootstrap.bind(port).sync();
            if (cf.isSuccess()) {
                System.out.println("服务器启动成功,端口->" +  port);
            }
            new Thread(() -> {
                Scanner scanner = new Scanner(System.in);
                System.out.println("输入【shutdown】命令可以关闭服务器:");
                while (scanner.hasNext()) {
                    String line = scanner.nextLine();
                    if ("shutdown".equalsIgnoreCase(line)) {
                        System.out.println("服务器马上关闭!");
                        cf.channel().close();
                        break;
                    }
                }
            }).start();
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new GroupChatServer(8899).run();
    }
}

客户端自定义handler

package me.jar.netty.groupchat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.util.function.Consumer;

/**
 * @Description
 * @Date 2021/3/1-23:07
 */
public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {
    // 定义一个channel组,管理所有的channel
    // GlobalEventExecutor.INSTANCE是全局的事件执行器,是一个单例
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    // handlerAdded表示连接建立,一旦连接,第一个被执行
    // 将当前channel加入到channelGroup
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        // 将该客户端加入聊天的信息推送给其他在线的客户端
        // 该方法会将channelGroup中的所有channel遍历,并发送数据,我们自己不需要遍历
        channelGroup.writeAndFlush("用户【" + channel.remoteAddress() + "】加入聊天\r\n");
        channelGroup.add(channel);
    }

    // 表示channel处于活动状态,提示xx上线
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("客户端【" + ctx.channel().remoteAddress() + "】上线了!");
    }

    // 表示channel处于不活动状态,提示xx离线了
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("客户端【" + ctx.channel().remoteAddress() + "】离线了!");
    }

    // 断开连接,将xx用户离开的消息推送给当前在线的用户
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        channelGroup.writeAndFlush("用户【" + ctx.channel().remoteAddress() + "】退出聊天\r\n");
        // 客户端断开,channelGroup会自动移除该channel
        System.out.println("当前channelGroup大小->" + channelGroup.size());
    }

    // 读取数据
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        Channel ch = ctx.channel();
        // 遍历channelGroup,根据不同的情况,回送不同的消息
        channelGroup.forEach(channel -> {
            if (channel != ch) {
                // 不是当前channel,转发消息
                channel.writeAndFlush("用户【" + ch.remoteAddress() + "】发送消息->" + msg + "\r\n");
            } else {
                // 回显自己的消息给自己
                channel.writeAndFlush("你发送了消息->" + msg + "\r\n");
            }
        });
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.out.println("客户端【" + ctx.channel().remoteAddress() + "】连接异常:" + cause.getMessage());
        ctx.close();
    }
}

 

客户端启动类

package me.jar.netty.groupchat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.Scanner;

/**
 * @Description
 * @Date 2021/3/2-21:47
 */
public class GroupChatClient {
    // 属性
    private final String host;
    private final int port;

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

    public void run() {
        NioEventLoopGroup loopGroup = new NioEventLoopGroup(4);

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(loopGroup).channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 加入相关handler
                            pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                            pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                            // 加入自己的handler
                            pipeline.addLast("clientHandler", new GroupChatClientHandler());
                        }
                    });
            ChannelFuture cf = bootstrap.connect(host, port).sync();
            Channel channel = cf.channel();
            System.out.println("----------------------" + channel.localAddress() + "-----------------------");
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()) {
                String line = scanner.nextLine();
                if ("exit".equalsIgnoreCase(line)) {
                    channel.close();
                    break;
                }
                // 通过channel发送到服务器端
                channel.writeAndFlush(line + "\r\n");
            }
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            loopGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new GroupChatClient("127.0.0.1", 8899).run();
    }
}

客户端自定义handler

package me.jar.netty.groupchat;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @Description
 * @Date 2021/3/2-21:58
 */
public class GroupChatClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        System.out.println(msg.trim());
    }
}

 

posted @ 2021-03-02 22:19  hello4world  阅读(147)  评论(0)    收藏  举报