Netty网络框架学习笔记-6(Netty简单实现一个群聊_2022.03.14)
- 实现多人群聊 / 一对一私聊
- 服务器端:可以监测用户上线,离线,并实现消息转发功能
1.0 编写netty服务端
@Slf4j
public class GroupChatServer {
public static void main(String[] args) {
NioEventLoopGroup bossEventLoopGroup = new NioEventLoopGroup(2);
NioEventLoopGroup workerEventLoopGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossEventLoopGroup, workerEventLoopGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 添加框架提供的字符串编解码处理器
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new GroupChatServerChannelHandler());
}
});
try {
ChannelFuture channelFuture = bootstrap.bind(new InetSocketAddress("localhost", 8888)).sync();
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
log.info("GroupChatServer==, 服务器已经启动成功, 等待连接中!");
}
}
});
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("GroupChatServer===, 发生异常:{}", e);
} finally {
bossEventLoopGroup.shutdownGracefully();
workerEventLoopGroup.shutdownGracefully();
}
}
}
1.1 服务端处理器
@Slf4j
public class GroupChatServerChannelHandler extends SimpleChannelInboundHandler<String> {
private static ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap();
//定义一个 channle 组,管理所有的 channel
//GlobalEventExecutor.INSTANCE) 是全局的事件执行器,是一个单例
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("GroupChatServerChannelHandler-channelActive 通道上线了!");
log.info("GroupChatServerChannelHandler-channelActive 远程地址{}!", ctx.channel().remoteAddress());
}
/**
* handlerAdded 表示连接建立,一旦连接,第一个被执行, 添加通道组
*
* @param ctx
* @author: ZhiHao
* @date: 2022/3/14
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
channelGroup.add(ctx.channel());
}
/**
* 断开连接, 将 xx 客户离开信息推送给当前在线的客户
*
* @param ctx
* @author: ZhiHao
* @date: 2022/3/14
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
//channelGroup.remove(channel); // 自动会删除, 无需调用!
String id = channel.id().asLongText();
String number = concurrentHashMap.get(id);
concurrentHashMap.remove(id);
//将该客户离线的信息推送给其它在线的客户端, 该方法会将 channelGroup 中所有的 channel 遍历,并发送 消息
channelGroup.writeAndFlush("[ 客 户 端 ]" + number + "号 离开 聊 天 " + " \n");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("GroupChatServerChannelHandler-channelInactive 通道离线了!", ctx.channel().remoteAddress());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
log.info("GroupChatServerChannelHandler-channelRead0, 读取到数据{}!", msg);
Channel channel = ctx.channel();
String id = channel.id().asLongText();
// 是否包含此客户端, 不包含则添加
if (!concurrentHashMap.containsKey(id) && NumberUtil.isNumber(msg)) {
concurrentHashMap.put(id, msg);
//将该客户加入聊天的信息推送给其它在线的客户端, 该方法会将 channelGroup 中所有的(包含自己) channel 遍历,并发送 消息
channelGroup.writeAndFlush("[ 客 户 端 ]" + msg + "号 加 入 聊 天 " + " \n");
return;
}
// 否则是将消息转发给其他客户端
channelGroup.forEach((ch)->{
if (channel != ch){
ch.writeAndFlush(msg);
}else {
// 自己回显发送的信息
channel.writeAndFlush(msg);
}
});
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
log.info("GroupChatServerChannelHandler-channelReadComplete 通道读取数据完毕!");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
2.0 编写客户端
@Slf4j
public class GroupChatClient {
private static int number = 1;
public static void main(String[] args) {
NioEventLoopGroup worker = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE,true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 添加框架提供的字符串编解码处理器
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new GroupChatClientChannelHandler());
}
});
try {
ChannelFuture channelFuture = bootstrap.
connect(new InetSocketAddress("localhost", 8888)).sync();
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
Channel channel = future.channel();
log.info("GroupChatClient==, 服务端连接成功{}!", channel.remoteAddress());
// 这里的线程不能阻塞, 因为这个通道与这个线程绑定了, 后面的读写都是使用这个线程
channel.writeAndFlush(String.valueOf(RandomUtil.getRandom().nextInt(8)));
}
}
});
worker.execute(()->{
Scanner scan = new Scanner(System.in);
while (scan.hasNextLine()) {
log.info("GroupChatClient==, 请输入发送信息!");
String next = scan.nextLine();
channelFuture.channel().writeAndFlush(next);
}
});
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("GroupChatClient==, 发生异常:{}",e);
}finally {
worker.shutdownGracefully();
}
}
}
2.1 客户端处理器
@Slf4j
public class GroupChatClientChannelHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("GroupChatClientChannelHandler- 通道激活了!");
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
log.info("GroupChatClientChannelHandler-channelRead0 读取到的数据:{}!",msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
3.0 结果
先启动服务端, 然后启动多个客户端进行测试
12:13:17.541 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatServer - GroupChatServer==, 服务器已经启动成功, 等待连接中!
12:13:20.947 [nioEventLoopGroup-2-1] WARN io.netty.bootstrap.ServerBootstrap - Unknown channel option 'SO_BACKLOG' for channel '[id: 0xeb527441, L:/127.0.0.1:8888 - R:/127.0.0.1:59538]'
12:13:20.971 [nioEventLoopGroup-3-1] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelActive 通道上线了!
12:13:20.971 [nioEventLoopGroup-3-1] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelActive 远程地址/127.0.0.1:59538!
12:13:20.994 [nioEventLoopGroup-3-1] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelRead0, 读取到数据7!
12:13:21.006 [nioEventLoopGroup-3-1] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelReadComplete 通道读取数据完毕!
12:13:25.084 [nioEventLoopGroup-2-1] WARN io.netty.bootstrap.ServerBootstrap - Unknown channel option 'SO_BACKLOG' for channel '[id: 0x1e4d20bf, L:/127.0.0.1:8888 - R:/127.0.0.1:59558]'
12:13:25.086 [nioEventLoopGroup-3-2] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelActive 通道上线了!
12:13:25.086 [nioEventLoopGroup-3-2] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelActive 远程地址/127.0.0.1:59558!
12:13:25.114 [nioEventLoopGroup-3-2] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelRead0, 读取到数据3!
12:13:25.116 [nioEventLoopGroup-3-2] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelReadComplete 通道读取数据完毕!
12:13:30.519 [nioEventLoopGroup-2-1] WARN io.netty.bootstrap.ServerBootstrap - Unknown channel option 'SO_BACKLOG' for channel '[id: 0x2fa2dae6, L:/127.0.0.1:8888 - R:/127.0.0.1:59578]'
12:13:30.550 [nioEventLoopGroup-3-3] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelActive 通道上线了!
12:13:30.550 [nioEventLoopGroup-3-3] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelActive 远程地址/127.0.0.1:59578!
12:13:30.550 [nioEventLoopGroup-3-3] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelRead0, 读取到数据3!
12:13:30.558 [nioEventLoopGroup-3-3] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelReadComplete 通道读取数据完毕!
12:13:56.023 [nioEventLoopGroup-3-1] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelRead0, 读取到数据你们好, 我是7号!
12:13:56.026 [nioEventLoopGroup-3-1] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelReadComplete 通道读取数据完毕!
12:14:16.829 [nioEventLoopGroup-3-3] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelRead0, 读取到数据我是三号!
12:14:16.830 [nioEventLoopGroup-3-3] INFO com.zhihao.netty.groupchat.GroupChatServerChannelHandler - GroupChatServerChannelHandler-channelReadComplete 通道读取数据完毕!
// -----------------------------------------------------------------------
12:13:20.933 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler- 通道激活了!
12:13:20.935 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClient - GroupChatClient==, 服务端连接成功localhost/127.0.0.1:8888!
12:13:21.007 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:[ 客 户 端 ]7号 加 入 聊 天
!
12:13:25.117 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:[ 客 户 端 ]3号 加 入 聊 天
!
12:13:30.554 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:[ 客 户 端 ]3号 加 入 聊 天
!
12:13:46.437 [nioEventLoopGroup-2-2] INFO com.zhihao.netty.groupchat.GroupChatClient - GroupChatClient==, 请输入发送信息!
你们好, 我是7号
12:13:56.022 [nioEventLoopGroup-2-2] INFO com.zhihao.netty.groupchat.GroupChatClient - GroupChatClient==, 请输入发送信息!
12:13:56.026 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:你们好, 我是7号!
12:14:16.830 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:我是三号!
// -----------------------------------------------------------------------
12:13:30.522 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler- 通道激活了!
12:13:30.524 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClient - GroupChatClient==, 服务端连接成功localhost/127.0.0.1:8888!
12:13:30.554 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:[ 客 户 端 ]3号 加 入 聊 天
!
12:13:56.026 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:你们好, 我是7号!
12:14:11.116 [nioEventLoopGroup-2-2] INFO com.zhihao.netty.groupchat.GroupChatClient - GroupChatClient==, 请输入发送信息!
我是三号
12:14:16.828 [nioEventLoopGroup-2-2] INFO com.zhihao.netty.groupchat.GroupChatClient - GroupChatClient==, 请输入发送信息!
12:14:16.830 [nioEventLoopGroup-2-1] INFO com.zhihao.netty.groupchat.GroupChatClientChannelHandler - GroupChatClientChannelHandler-channelRead0 读取到的数据:我是三号!
1