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,即调用ChannelInitializerinitChannel方法,当连接比较多时,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)群聊功能实现相关推荐

  1. 互联网早报 | 3月16日 星期二 | 微信AI直播助理开放内测;汽车之家港交所挂牌上市;美团App内测“群聊”功能...

    今日看点 ✦ 汽车之家港交所成功挂牌,成年内首家回港二次上市中概股 ✦ 恒大汽车与腾讯旗下梧桐车联成立合资公司 ,共同开发车载智能操作系统 ✦ 微信AI直播助理开放内测,助力电商带货 ✦ 美团App内 ...

  2. 360搜索、UC浏览器等被3·15点名应用已下架;马斯克宣布通过NFT卖歌;美团App再发力社交,内测 “群聊”功能 |极客头条...

    「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧. 整理 | 丁恩华 出品 | CSDN(ID:CSDNnews ...

  3. C++搭建集群聊天室(十四):群聊功能

    文章目录 群聊功能思路 放码过来 groupuser.hpp group.hpp groupmodel.hpp groupmodel.cpp 群聊功能思路 1.创建群聊,提交群信息,返回群号 2.拉取 ...

  4. Line推出新语音群聊功能 最多支持200人

    3月11日,据科技博客VentureBeat报道,日本移动通讯巨头Line公司近日宣布,为支持企业电话会议,该公司将在最新版Line应用中增加语音群聊功能,最多支持200人同时群聊. Line的这项新 ...

  5. 基于WebSocket实现一个简易的群聊功能

    本文主要来讲解如何使用WebSocket来实现一个简易的群聊功能 引入maven依赖 <dependency><groupId>org.springframework.boot ...

  6. 360搜索、UC浏览器等被3·15点名应用已下架;马斯克宣布通过NFT卖歌;美团App再发力社交,内测 “群聊”功能...

    凌云时刻 一分钟速览新闻点! 猎聘.前程无忧为大量简历流向黑市而致歉 医疗广告竞价排名,360 搜索.UC 纷纷上榜 315 晚会 315 晚会曝光人脸识别乱象:海量人脸信息已被搜集 安兔兔曝光 re ...

  7. android微信群聊功能,微信安卓内测更新,这个群聊功能等了8年

    近日,安卓版微信悄然迎来了 8.0.3 内测版更新,本次内测又带来了哪些新功能呢,一起来看看吧. 01 群聊支持键入 @所有人 微信作为国民级应用,而微信群又是微信中的一个重要组成部分." ...

  8. 小程序集成网易云通信群聊功能Demo发布

    前端代码是可以直接使用的,获取后端代码加微信13439975582 功能实现说明: 1.小程序生命周期完美整合 2.消息小红点,群聊小红点代码实现都实现了 3.历史信息回放 4.小程序帐号集成 代码都 ...

  9. 基于Vue+springboot+websocket实现的简短仿微信web聊天室(私聊和群聊功能)(可在线预览)

    写目录 一.界面展示 二.介绍 一.界面展示 之前闲着有空就给自己的个人博客搭了一些附加功能,聊天室也是其中之一,简单的实现了私聊.群聊功能,可以发送emoji表情和图片等,项目已经部署在www.tc ...

最新文章

  1. 在ML中缺乏数据可是个大问题,亲测有效的5种方法帮您解决
  2. 【NOIP2013模拟9.29】密码
  3. sql server 更改端口之后的登入方式
  4. Ubuntu上 anaconda的卸载
  5. 类似抖音的短视频app开发难度大吗?短视频源码让你事半功倍
  6. SQL语句备查(引用)
  7. subclipse同步冲突问题A conflict in the working copy obstructs the current operation
  8. 第五章 卷积神经网络(CNN)
  9. leetcode 算法 Excel表列序号 python实现
  10. Axure| 旋转控件或者图片
  11. 常见的80道面试算法题
  12. 《#华为云#听从你心,无问西东》及网友跟帖
  13. 解决sublime中文输入问题
  14. 微信公众号迁移公证书需要哪些材料?账号迁移流程来了
  15. Foxmail设置标签
  16. TS在前端发展的当前形式(愚见)
  17. 知识图谱学习(笔记整理)
  18. numpy简单实现梯度投影法
  19. python库吐血整理
  20. java web inf_JavaWeb - 访问 WEB-INF 资源几种方式

热门文章

  1. 视频抽帧及将图片合成视频
  2. Stapler-1靶场详细教学(7种漏洞利用+5种提权)
  3. 大咖面对面 | Reva:NFT 发展路上的“无限回廊”
  4. html实现点击元素的出现与隐藏
  5. 新环境中奋起的岁月(六)
  6. Matplotlib设置次坐标轴
  7. word中超链接显示成{HYPERLINK url}形式的解决方案
  8. Linux之文件权限管理篇
  9. C++ String去除头尾空格 实现trim()方法
  10. thinkphp6使用自定义异常类