群聊的发起与通知

这小节,我们来学习一下如何创建一个群聊,并通知到群聊中的各位成员

我们依然是先来看一下最终的效果是什么样的。

1. 最终效果

服务端

创建群聊的客户端

其他客户端

  1. 首先,依然是三位用户依次登录到服务器,分别是闪电侠、极速、萨维塔。
  2. 然后,我们在闪电侠的控制台输入 createGroup 指令,提示创建群聊需要输入 userId 列表,然后我们输入以英文逗号分隔的 userId。
  3. 群聊创建成功之后,分别在服务端和三个客户端弹出提示消息,包括群的 ID 以及群里各位用户的昵称。

2. 群聊原理

群聊的原理我们在 仿微信 IM 系统简介 已经学习过,我们再来重温一下

群聊指的是一个组内多个用户之间的聊天,一个用户发到群组的消息会被组内任何一个成员接收,下面我们来看一下群聊的基本流程。

如上图,要实现群聊,其实和单聊类似

  1. A,B,C 依然会经历登录流程,服务端保存用户标识对应的 TCP 连接
  2. A 发起群聊的时候,将 A,B,C 的标识发送至服务端,服务端拿到之后建立一个群聊 ID,然后把这个 ID 与 A,B,C 的标识绑定
  3. 群聊里面任意一方在群里聊天的时候,将群聊 ID 发送至服务端,服务端拿到群聊 ID 之后,取出对应的用户标识,遍历用户标识对应的 TCP 连接,就可以将消息发送至每一个群聊成员

这一小节,我们把重点放在创建一个群聊上,由于控制台输入的指令越来越多,因此在正式开始之前,我们先对我们的控制台程序稍作重构。

2. 控制台程序重构

2.1 创建控制台命令执行器

首先,我们把在控制台要执行的操作抽象出来,抽象出一个接口

ConsoleCommand.java

public interface ConsoleCommand {void exec(Scanner scanner, Channel channel);
}

2.2 管理控制台命令执行器

接着,我们创建一个管理类来对这些操作进行管理。

ConsoleCommandManager.java

public class ConsoleCommandManager implements ConsoleCommand {private Map<String, ConsoleCommand> consoleCommandMap;public ConsoleCommandManager() {consoleCommandMap = new HashMap<>();consoleCommandMap.put("sendToUser", new SendToUserConsoleCommand());consoleCommandMap.put("logout", new LogoutConsoleCommand());consoleCommandMap.put("createGroup", new CreateGroupConsoleCommand());}@Overridepublic void exec(Scanner scanner, Channel channel) {//  获取第一个指令String command = scanner.next();ConsoleCommand consoleCommand = consoleCommandMap.get(command);if (consoleCommand != null) {consoleCommand.exec(scanner, channel);} else {System.err.println("无法识别[" + command + "]指令,请重新输入!");}}
}
  1. 我们在这个管理类中,把所有要管理的控制台指令都塞到一个 map 中。
  2. 执行具体操作的时候,我们先获取控制台第一个输入的指令,这里以字符串代替,比较清晰(这里我们已经实现了上小节课后思考题中的登出操作),然后通过这个指令拿到对应的控制台命令执行器执行。

这里我们就拿创建群聊举个栗子:首先,我们在控制台输入 createGroup,然后我们按下回车,就会进入 CreateGroupConsoleCommand 这个类进行处理

CreateGroupConsoleCommand.java

public class CreateGroupConsoleCommand implements ConsoleCommand {private static final String USER_ID_SPLITER = ",";@Overridepublic void exec(Scanner scanner, Channel channel) {CreateGroupRequestPacket createGroupRequestPacket = new CreateGroupRequestPacket();System.out.print("【拉人群聊】输入 userId 列表,userId 之间英文逗号隔开:");String userIds = scanner.next();createGroupRequestPacket.setUserIdList(Arrays.asList(userIds.split(USER_ID_SPLITER)));channel.writeAndFlush(createGroupRequestPacket);}}

进入到 CreateGroupConsoleCommand 的逻辑之后,我们创建了一个群聊创建请求的数据包,然后提示输入以英文逗号分隔的 userId 的列表,填充完这个数据包之后,调用 writeAndFlush() 我们就可以发送一个创建群聊的指令到服务端。

最后,我们再来看一下经过我们的改造,客户端的控制台线程相关的代码。

NettyClient.java

private static void startConsoleThread(Channel channel) {ConsoleCommandManager consoleCommandManager = new ConsoleCommandManager();LoginConsoleCommand loginConsoleCommand = new LoginConsoleCommand();Scanner scanner = new Scanner(System.in);new Thread(() -> {while (!Thread.interrupted()) {if (!SessionUtil.hasLogin(channel)) {loginConsoleCommand.exec(scanner, channel);} else {consoleCommandManager.exec(scanner, channel);}}}).start();
}

抽取出控制台指令执行器之后,客户端控制台逻辑已经相对之前清晰很多了,可以非常方便地在控制台模拟各种在 IM 聊天窗口的操作,接下来,我们就来看一下如何创建群聊。

3. 创建群聊的实现

3.1 客户端发送创建群聊请求

通过我们前面讲述控制台逻辑的重构,我们已经了解到我们是发送一个 CreateGroupRequestPacket 数据包到服务端,这个数据包的格式为:

CreateGroupRequestPacket.java

public class CreateGroupRequestPacket extends Packet {private List<String> userIdList;
}

它只包含了一个列表,这个列表就是需要拉取群聊的用户列表,接下来我们看下服务端如何处理的。

3.2 服务端处理创建群聊请求

我们依然是创建一个 handler 来处理新的指令。

NettyServer.java

.childHandler(new ChannelInitializer<NioSocketChannel>() {protected void initChannel(NioSocketChannel ch) {// ...// 添加一个 handler ch.pipeline().addLast(new CreateGroupRequestHandler());// ...}
});

接下来,我们来看一下这个 handler 具体做哪些事情

CreateGroupRequestHandler.java

public class CreateGroupRequestHandler extends SimpleChannelInboundHandler<CreateGroupRequestPacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, CreateGroupRequestPacket createGroupRequestPacket) {List<String> userIdList = createGroupRequestPacket.getUserIdList();List<String> userNameList = new ArrayList<>();// 1. 创建一个 channel 分组ChannelGroup channelGroup = new DefaultChannelGroup(ctx.executor());// 2. 筛选出待加入群聊的用户的 channel 和 userNamefor (String userId : userIdList) {Channel channel = SessionUtil.getChannel(userId);if (channel != null) {channelGroup.add(channel);userNameList.add(SessionUtil.getSession(channel).getUserName());}}// 3. 创建群聊创建结果的响应CreateGroupResponsePacket createGroupResponsePacket = new CreateGroupResponsePacket();createGroupResponsePacket.setSuccess(true);createGroupResponsePacket.setGroupId(IDUtil.randomId());createGroupResponsePacket.setUserNameList(userNameList);// 4. 给每个客户端发送拉群通知channelGroup.writeAndFlush(createGroupResponsePacket);System.out.print("群创建成功,id 为[" + createGroupResponsePacket.getGroupId() + "], ");System.out.println("群里面有:" + createGroupResponsePacket.getUserNameList());}
}

整个过程可以分为以下几个过程

  1. 首先,我们这里创建一个 ChannelGroup。这里简单介绍一下 ChannelGroup:它可以把多个 chanel 的操作聚合在一起,可以往它里面添加删除 channel,可以进行 channel 的批量读写,关闭等操作,详细的功能读者可以自行翻看这个接口的方法。这里我们一个群组其实就是一个 channel 的分组集合,使用 ChannelGroup 非常方便。
  2. 接下来,我们遍历待加入群聊的 userId,如果存在该用户,就把对应的 channel 添加到ChannelGroup 中,用户昵称也添加到昵称列表中。
  3. 然后,我们创建一个创建群聊响应的对象,其中 groupId 是随机生成的,群聊创建结果一共三个字段,这里就不展开对这个类进行说明了。
  4. 最后,我们调用 ChannelGroup 的聚合发送功能,将拉群的通知批量地发送到客户端,接着在服务端控制台打印创建群聊成功的信息,至此,服务端处理创建群聊请求的逻辑结束。

我们接下来再来看一下客户端处理创建群聊响应。

3.3 客户端处理创建群聊响应

客户端依然也是创建一个 handler 来处理新的指令。

NettyClient.java

.handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {// ...// 添加一个新的 handler 来处理创建群聊成功响应的指令ch.pipeline().addLast(new CreateGroupResponseHandler());// ...}
});

然后,在我们的应用程序里面,我们仅仅是把创建群聊成功之后的具体信息打印出来。

CreateGroupResponseHandler.java

public class CreateGroupResponseHandler extends SimpleChannelInboundHandler<CreateGroupResponsePacket> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, CreateGroupResponsePacket createGroupResponsePacket) {System.out.print("群创建成功,id 为[" + createGroupResponsePacket.getGroupId() + "], ");System.out.println("群里面有:" + createGroupResponsePacket.getUserNameList());}
}

在实际生产环境中,CreateGroupResponsePacket 对象里面可能有更多的信息,然后以上逻辑的处理也会更加复杂,不过我们这里已经能说明问题了。

到了这里,这小节的内容到这里就告一段落了,下小节,我们来学习群聊成员管理,包括添加删除成员,获取成员列表等等,最后,我们再对本小节内容做一下总结。

4. 总结

  1. 群聊的原理和单聊类似,无非都是通过标识拿到 channel。
  2. 本小节,我们重构了一下控制台的程序结构,在实际带有 UI 的 IM 应用中,我们输入的第一个指令其实就是对应我们点击 UI 的某些按钮或菜单的操作。
  3. 通过 ChannelGroup,我们可以很方便地对一组 channel 进行批量操作。

netty之微信-群聊的发起与通知(十八)相关推荐

  1. 群文件通知.html,16实战:群聊的发起与通知.html

    16实战:群聊的发起与通知.md /*--------------------------------------------------------------------------------- ...

  2. netty之微信-群聊消息的收发及 Netty 性能优化(二十)

    群聊消息的收发及 Netty 性能优化 通过前面小节的学习,相信大家看到本小节标题就已经知道该如何实现本小节的功能了吧,为了给大家学到更多的知识,在实现了群聊消息收发之后,本小节将带给大家更多的惊喜. ...

  3. 小游戏正在毁灭微信群聊(文中有福利)

    小游戏已经成为毁灭微信群聊体验的第一杀手. 在几年前群聊杀手还主要是养生文和鸡汤文,但后来随着商家开发各种玩法以及抄袭各种玩法,主流的杀手变成了抢票.拼团.网课推荐.邀请注册等.小游戏虽然起步晚,但是 ...

  4. 微信群聊消失找回方法介绍

    ​ 微信聊天窗口过多难免顶掉了许多群聊窗口,对于这类消失的群聊窗口又应该怎么找回呢,下面就来看看相关的内容吧. ​ 微信群聊不见了怎么办? 1.打开手机微信,进入"通讯录"页面,找 ...

  5. cdr 表格自动填充文字_微信群聊上线接龙表格功能,老师一族的福音来了?

    最近微信上线了一个新功能,群聊接龙表格,也就是仅针对群聊开放的功能.当对方发来接龙表格时,会出现一个"查看接龙"的提示,用户点击进入后,可添加相关信息. 经测试,该功能首先上线于i ...

  6. 怎么用计算机收钱,怎么群收款-今天,微信群聊可以收钱了!(天呐)

    微信又放大招了!!! 机智的小编发现,小程序这招棋还没落下,微信已经悄悄变了样!!! 今天,微信群聊可以收钱了! (我的天呐) ▼ 今天,Apple Store的微信6.3.28,新版正式更新. 本次 ...

  7. 手把手教你分析微信群聊记录,识别害群之马

    导读:很多朋友加入了很多微信群,作为群中一员,你想知道这个群是否值得留下?而作为群主,你想了解目前这个群是否健康?如果有问题,那么坏了这锅粥的老鼠屎是谁?应该怎样设立群规?本文通过简单的分析给出思路, ...

  8. 微信 6.3.32 for Android发布 群收款微信群聊可以收钱

    Android版微信迎来v6.3.32版更新,本次升级主要是解决了一些已知问题.近期版本微信群聊中可以按群成员和日期查找聊天内容,群主可启用需群主确认才可邀请朋友进群功能,群聊里也可以收钱了,聊天中视 ...

  9. 小游戏正在毁灭微信群聊[联络易]

    小游戏已经成为毁灭微信群聊体验的第一杀手. 在几年前群聊杀手还主要是养生文和鸡汤文,但后来随着商家开发各种玩法以及抄袭各种玩法,主流的杀手变成了抢票.拼团.网课推荐.邀请注册等.小游戏虽然起步晚,但是 ...

最新文章

  1. 我希望支持JavaScript GraphQL实现的API
  2. 限时早鸟票 | 2019 中国大数据技术大会(BDTC)超豪华盛宴抢先看!
  3. 关于学习Python的一点学习总结(19->if及相关的符号运算)
  4. 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案
  5. 推荐系统--揭开推荐的神奇面纱
  6. 小米开发出100W手机快充技术:实测逆天
  7. hibernate实体的几种状态:
  8. 围棋经典棋谱_秀秀老师:茶艺师也要学好围棋
  9. 前端学习(2182):keep-alive及其他问题
  10. ubuntu 16.04系统中nvidai、cuda、cudnn安装及注意事项
  11. gnss单频软件接收机应用与编程_GNSS/GPS RTK定位 (手机,无人车定位,无人驾驶,因子图优化)...
  12. boost operator 实例代码
  13. OpenShift 4 - 节点是如何通过CRI-O运行容器的
  14. Sonar问题及解决方案汇总
  15. 【兼容写法】HttpServerUtility.Execute 在等待异步操作完成时被阻止。关键词:MVC,分部视图,异步...
  16. 小字体小行高兼容性分析及差异解决办法
  17. 通用即插即用监视器驱动下载_请你给广色域显示器装下驱动好么? 尤其是k7b小金刚以及nano ips面板显示器的用户...
  18. 视觉素材-10 个值得珍藏的高清桌面壁纸网站
  19. JEESZ分布式框架简介---技术介绍文档
  20. 《Spring+Spring MVC+MyBatis从零开始学》傻瓜式学习笔记

热门文章

  1. association的使用
  2. 程序员的10大老大难问题
  3. TCP协议拥塞控制算法(Reno、HSTCP、BIC、Vegas、Westwood)
  4. 样本的方差的均值等于总体的方差
  5. docker修改已部署容器的配置文件
  6. arm 2022.10.24
  7. Sonic simple服务中设备图片、测试用例运行异常图片、失败录像路径映射配置
  8. windows系统通过虚拟机安装linux
  9. c语言程序设计 大学考试题库,广东工业大学《C语言程序设计》考试复习重点题库.pdf...
  10. Python趣味编程 | 看看如何用Python生成素描风格的自拍照,并且加上Logo