基于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()); } }