007-核心技术-netty-基于netty的群聊系统
一、基于netty群聊
服务器端
package com.github.bjlhx15.netty.demo.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.text.SimpleDateFormat; public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> { //定义一个channel组。管理所有的channel //GlobalEventExecutor.INSTANCE是全局的事件执行器,是一个单例 private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //handlerAdded 表示连接建立,一旦连接,第一个被执行 // 当前的channel加入到 channelgroup @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); // 将该客户加入聊天的信息推送到其他所有客户端 // 该方法会将 channelGroup中所有的channel遍历,并发送消息,不需要自己遍历 channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 加入聊天\n"); channelGroup.add(channel); } //断开连接 @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 离开了\n"); // channelGroup.remove()//不需要执行这里,这个方法会触发channelGroup 删除这个channel } // 表示channel处于活动状态 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + " 上线了"); } // 表示channel处于不活动状态 @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + " 离线了"); } @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { // 获取到当前的channel Channel channel = channelHandlerContext.channel(); channelGroup.forEach(ch -> { if (channel != ch) { ch.writeAndFlush("[客户]" + channel.remoteAddress() + " 发送消息," + s + "\n"); } else { ch.writeAndFlush("[自己]发送了消息" + s + "\n"); } }); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
服务器端Handler
package com.github.bjlhx15.netty.demo.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.text.SimpleDateFormat; public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> { //定义一个channel组。管理所有的channel //GlobalEventExecutor.INSTANCE是全局的事件执行器,是一个单例 private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //handlerAdded 表示连接建立,一旦连接,第一个被执行 // 当前的channel加入到 channelgroup @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); // 将该客户加入聊天的信息推送到其他所有客户端 // 该方法会将 channelGroup中所有的channel遍历,并发送消息,不需要自己遍历 channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 加入聊天\n"); channelGroup.add(channel); } //断开连接 @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 离开了\n"); // channelGroup.remove()//不需要执行这里,这个方法会触发channelGroup 删除这个channel } // 表示channel处于活动状态 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + " 上线了"); } // 表示channel处于不活动状态 @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + " 离线了"); } @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { // 获取到当前的channel Channel channel = channelHandlerContext.channel(); channelGroup.forEach(ch -> { if (channel != ch) { ch.writeAndFlush("[客户]" + channel.remoteAddress() + " 发送消息," + s + "\n"); } else { ch.writeAndFlush("[自己]发送了消息" + s + "\n"); } }); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
客户端
package com.github.bjlhx15.netty.demo.netty.groupchat; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; 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 java.util.Scanner; 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() throws InterruptedException { EventLoopGroup eventExecutors = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap() .group(eventExecutors) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast(new GroupChatClientHandler());//加入自己业务的handler } }); ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); Channel channel = channelFuture.channel(); System.out.println("------------" + channel.localAddress() + "-------"); Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { String s = scanner.nextLine(); channel.writeAndFlush(s + "\r\n"); } } finally { eventExecutors.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new GroupChatClient("127.0.0.1", 7000).run(); } }
客户端Handler
package com.github.bjlhx15.netty.demo.netty.groupchat; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class GroupChatClientHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { System.out.println(s.trim()); } }
启动测试即可
如果有私聊,需要将ChannelGroup ,替换为Map<String,Channel>,key是用户ID,然后就能找到对应的Channel,在ChannelRead0时查找发送即可。