Netty聊天系统(4)群聊功能实现
7 群聊功能的实现
7.1 群聊的创建
- 创建一个创建群聊请求的实体类,依然是继承Packet,因为要通过我们的协议进行编解码
- 服务器端创建一个处理创建群聊请求的Handler,并实现其中的逻辑
- 创建一个群聊创建响应的实体类,继承Packet
- 客户端创建一个Handler来处理响应
创建一个CreateGroupRequestPacket类,
@Data
public class CreateGroupRequestPacket extends Packet {private String groupName; //群名private Long groupLeader; //群主IDprivate List<Long> userIdList; //群成员ID@Overridepublic Byte getCommand() {return Command.CREATE_GROUP_REQUEST;}
}
服务器端添加一个处理的Handler
创建一个Netty提供的
DefaultChannelGroup
类来存储所有的channel。发送群聊消息就向
DefaultChannelGroup
中写入消息即可
public class CreateGroupRequestHandler extends SimpleChannelInboundHandler<CreateGroupRequestPacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, CreateGroupRequestPacket requestPacket) throws Exception {List<Long> userIdList = requestPacket.getUserIdList();String groupName = requestPacket.getGroupName();Long groupLeaderId = requestPacket.getGroupLeader();//创建一个Channel分组DefaultChannelGroup channelGroup = new DefaultChannelGroup(ctx.executor());//遍历userIdList,将用户对应的channel添加到channel分组中for (Long userId: userIdList){Channel channel = ChannelUtil.getChannel(userId);if(channel != null){channelGroup.add(channel);}else {//TODO:如果不在线,将加群的请求存入数据库,当该用户登录时再去查询数据库是否有邀请群的通知System.out.println(userId + "不在线");}}// 创建一个群聊IDlong groupID = RandomUtils.nextLong();// 将该channelGroup保存起来ChannelGroupUtil.addGroup(groupID, groupLeaderId, channelGroup);//创建response返回给客户端CreateGroupResponsePacket responsePacket = new CreateGroupResponsePacket();responsePacket.setGroupId(groupID); //这里暂时返回一个随机的long数字作为群聊IDresponsePacket.setSuccess(true);responsePacket.setGroupName(groupName);responsePacket.setUserIdList(userIdList);//给所有客户端发送拉群通知channelGroup.writeAndFlush(responsePacket);}
}
ChannelGroupUtil
public class ChannelGroupUtil {//使用ConcurrentHashMap保存映射关系//群ID与ChannelGroup的映射private static final Map<Long, ChannelGroup> groupMap = new ConcurrentHashMap<>();//群ID与群主id的映射private static final Map<Long, Long> groupLeaderMap = new ConcurrentHashMap<>(); public static void addGroup(Long groupId, Long groupLeaderId, ChannelGroup channels){groupMap.put(groupId, channels);groupLeaderMap.put(groupId, groupLeaderId);}public static ChannelGroup getChannelGroup(Long groupId){return groupMap.get(groupId);}public static Long getGroupLeaderId(Long groupId){return groupLeaderMap.get(groupId);}}
客户端处理器,收到消息后打印即可
public class CreateGroupResponseHandler extends SimpleChannelInboundHandler<CreateGroupResponsePacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, CreateGroupResponsePacket responsePacket) throws Exception {String groupName = responsePacket.getGroupName();List<Long> userIdList = responsePacket.getUserIdList();System.out.println("您已经加入群聊:"+groupName+",该群有以下群成员\n"+userIdList);}
}
测试时,输入群聊名和userIdList,此时默认添加自己的userId。
7.2 群成员管理
7.2.1 加入群聊功能实现
创建一个请求实体类,用作请求和响应传输
@Data
public class JoinGroupRequestPacket extends Packet {private Long groupId;private Long userId;@Overridepublic Byte getCommand() {return Command.JOIN_GROUP_REQUEST;}
}
@Data
public class JoinGroupResponsePacket extends Packet{private Boolean agree;private Long groupId;private Long userId;@Overridepublic Byte getCommand() {return Command.JOIN_GROUP_RESPONSE;}
}
服务器端处理加入群聊请求:找到群主ID,将请求转发给群主
public class JoinGroupRequestServerHandler extends SimpleChannelInboundHandler<JoinGroupRequestPacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, JoinGroupRequestPacket joinGroupRequest) throws Exception {Long groupId = joinGroupRequest.getGroupId();// 1)获取群主ID,将消息转发到群主Long groupLeaderId = ChannelGroupUtil.getGroupLeaderId(groupId);if(groupLeaderId != null){Channel channel = ChannelUtil.getChannel(groupLeaderId);channel.writeAndFlush(joinGroupRequest);}else {System.out.println("群主不在线,持久化请求信息......");}}
}
客户端响应是否同意加入群聊
public class JoinGroupRequestClientHandler extends SimpleChannelInboundHandler<JoinGroupRequestPacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, JoinGroupRequestPacket joinGroupRequest) throws Exception {Long groupId = joinGroupRequest.getGroupId();Long userId = joinGroupRequest.getUserId();System.out.println(userId + "申请加入ID为"+groupId+"的群聊");//这里先默认同意加群ChannelGroup channelGroup = ChannelGroupUtil.getChannelGroup(groupId);Channel channel = ChannelUtil.getChannel(userId);if(channel != null){channelGroup.add(channel);}else {System.out.println("用户"+userId+"已经离线,加入ID为"+groupId+"的群聊失败。持久化......");}//构造responseJoinGroupResponsePacket joinGroupResponse = new JoinGroupResponsePacket();joinGroupResponse.setAgree(true);joinGroupResponse.setGroupId(groupId);joinGroupResponse.setUserId(userId);ctx.channel().writeAndFlush(joinGroupResponse);}
}
服务器端将加入群聊响应转发给客户端
public class JoinGroupResponseServerHandler extends SimpleChannelInboundHandler<JoinGroupResponsePacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, JoinGroupResponsePacket joinGroupResponse) throws Exception {Long userId = joinGroupResponse.getUserId();Channel channel = ChannelUtil.getChannel(userId);if(channel != null){channel.writeAndFlush(joinGroupResponse);}else {System.out.println("用户"+userId+"不在线,持久化信息及通知");}}
}
客户端显示加入群聊成功与否
public class JoinGroupResponseClientHandler extends SimpleChannelInboundHandler<JoinGroupResponsePacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, JoinGroupResponsePacket joinGroupResponse) throws Exception {Long groupId = joinGroupResponse.getGroupId();System.out.println("您已经加入ID为" + groupId +"的群聊");}
}
最后加入对应的pipeline即可。
群成员管理还包括:退出群聊、获取群成员、管理员剔除群人员等,都是重复的工作,这里不再占用篇幅
7.3 群聊消息的收发
群聊消息的转发实现思路也跟之前一样:
1)定义GroupMsgPacket
用于数据传输
2)在服务器端写Handler实现消息转发
3)客户端写Handler接受打印消息
8 性能优化
8.1 共享Handler
在我们的服务器端和客户端,pipeline中添加Handler时都是采用创建一个新的Handler对象的方式。
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new Spliter());pipeline.addLast(new PacketDecoder());pipeline.addLast(new LoginRequestHandler());pipeline.addLast(new MsgRequestHandler());pipeline.addLast(new CreateGroupRequestHandler());......pipeline.addLast(new PacketEncoder());}});
每当有新连接时,都会去初始化当前连接的ChannelHandler,即调用ChannelInitializer
的initChannel
方法,当连接比较多时,Handler对象会非常多,可能造成OOM。
观察我们写的所有Handler类,是没有成员变量的,也就意味着,多线程情况下该类是线程安全的,此时我们可以将类设置成单例模式,提升效率。
将所有Handler重构成单例模式
单例模式有很多种写法:饿汉式、懒汉式。懒汉式又有双重校验、静态内部类、枚举等实现。
这里采用静态内部类的方式实现,以JoinGroupRequestServerHandler
为例:
@ChannelHandler.Sharable
public class JoinGroupRequestServerHandler extends SimpleChannelInboundHandler<JoinGroupRequestPacket> {//私有构造函数private JoinGroupRequestServerHandler(){}//静态内部类private static class JoinGroupRequestServerHandlerBuilder{private static JoinGroupRequestServerHandler instance = new JoinGroupRequestServerHandler();}//提供对外获取单例对象的方法public static JoinGroupRequestServerHandler getInstance(){return JoinGroupRequestServerHandlerBuilder.instance;}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, JoinGroupRequestPacket joinGroupRequest) throws Exception {Long groupId = joinGroupRequest.getGroupId();// 1)获取群主ID,将消息转发到群主Long groupLeaderId = ChannelGroupUtil.getGroupLeaderId(groupId);if(groupLeaderId != null){Channel channel = ChannelUtil.getChannel(groupLeaderId);channel.writeAndFlush(joinGroupRequest);}else {System.out.println("群主不在线,持久化请求......");}}
}
注意:在Netty中,一个Handler对象被多个channel共享的话,需要加上@ChannelHandler.Sharable
注解,否则会报错。
Netty聊天系统(4)群聊功能实现相关推荐
- 互联网早报 | 3月16日 星期二 | 微信AI直播助理开放内测;汽车之家港交所挂牌上市;美团App内测“群聊”功能...
今日看点 ✦ 汽车之家港交所成功挂牌,成年内首家回港二次上市中概股 ✦ 恒大汽车与腾讯旗下梧桐车联成立合资公司 ,共同开发车载智能操作系统 ✦ 微信AI直播助理开放内测,助力电商带货 ✦ 美团App内 ...
- 360搜索、UC浏览器等被3·15点名应用已下架;马斯克宣布通过NFT卖歌;美团App再发力社交,内测 “群聊”功能 |极客头条...
「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧. 整理 | 丁恩华 出品 | CSDN(ID:CSDNnews ...
- C++搭建集群聊天室(十四):群聊功能
文章目录 群聊功能思路 放码过来 groupuser.hpp group.hpp groupmodel.hpp groupmodel.cpp 群聊功能思路 1.创建群聊,提交群信息,返回群号 2.拉取 ...
- Line推出新语音群聊功能 最多支持200人
3月11日,据科技博客VentureBeat报道,日本移动通讯巨头Line公司近日宣布,为支持企业电话会议,该公司将在最新版Line应用中增加语音群聊功能,最多支持200人同时群聊. Line的这项新 ...
- 基于WebSocket实现一个简易的群聊功能
本文主要来讲解如何使用WebSocket来实现一个简易的群聊功能 引入maven依赖 <dependency><groupId>org.springframework.boot ...
- 360搜索、UC浏览器等被3·15点名应用已下架;马斯克宣布通过NFT卖歌;美团App再发力社交,内测 “群聊”功能...
凌云时刻 一分钟速览新闻点! 猎聘.前程无忧为大量简历流向黑市而致歉 医疗广告竞价排名,360 搜索.UC 纷纷上榜 315 晚会 315 晚会曝光人脸识别乱象:海量人脸信息已被搜集 安兔兔曝光 re ...
- android微信群聊功能,微信安卓内测更新,这个群聊功能等了8年
近日,安卓版微信悄然迎来了 8.0.3 内测版更新,本次内测又带来了哪些新功能呢,一起来看看吧. 01 群聊支持键入 @所有人 微信作为国民级应用,而微信群又是微信中的一个重要组成部分." ...
- 小程序集成网易云通信群聊功能Demo发布
前端代码是可以直接使用的,获取后端代码加微信13439975582 功能实现说明: 1.小程序生命周期完美整合 2.消息小红点,群聊小红点代码实现都实现了 3.历史信息回放 4.小程序帐号集成 代码都 ...
- 基于Vue+springboot+websocket实现的简短仿微信web聊天室(私聊和群聊功能)(可在线预览)
写目录 一.界面展示 二.介绍 一.界面展示 之前闲着有空就给自己的个人博客搭了一些附加功能,聊天室也是其中之一,简单的实现了私聊.群聊功能,可以发送emoji表情和图片等,项目已经部署在www.tc ...
最新文章
- 在ML中缺乏数据可是个大问题,亲测有效的5种方法帮您解决
- 【NOIP2013模拟9.29】密码
- sql server 更改端口之后的登入方式
- Ubuntu上 anaconda的卸载
- 类似抖音的短视频app开发难度大吗?短视频源码让你事半功倍
- SQL语句备查(引用)
- subclipse同步冲突问题A conflict in the working copy obstructs the current operation
- 第五章 卷积神经网络(CNN)
- leetcode 算法 Excel表列序号 python实现
- Axure| 旋转控件或者图片
- 常见的80道面试算法题
- 《#华为云#听从你心,无问西东》及网友跟帖
- 解决sublime中文输入问题
- 微信公众号迁移公证书需要哪些材料?账号迁移流程来了
- Foxmail设置标签
- TS在前端发展的当前形式(愚见)
- 知识图谱学习(笔记整理)
- numpy简单实现梯度投影法
- python库吐血整理
- java web inf_JavaWeb - 访问 WEB-INF 资源几种方式