HarmonyOS 设计一个简单的群聊App,后端应该如何实现? 原创
先思考一个问题,如果让我们自己设计一个可以群聊的App,不使用各种即时通讯的IM框架,你会如何实现通信的过程呢?
这里先说下思路,我们可以选择高性能的网络传输框架Netty,它作为基础通信组件被各种RPC框架广泛使用,例如在Hadoop的Avro
组件和Dubbo
中在底层都使用了Netty。下面我们就看看如何基于Netty,实现一个群聊App,代码分别从服务端和客户端两方面介绍。
服务端代码
Server
在Netty的服务端,我们需要定义BossEventLoopGroup
和WorkerEventLoopGroup
两个EventLoopGroup
进行工作,它们是一组EventLoop
的抽象,可以从一组EventLoop
里面按照一定规则获取其中一个EventLoop
来处理任务。
在下面的代码中,启动服务端时创建两个EventLoopGroup
事件循环线程组,bossGroup
专门负责接收客户端的连接,workerGroup
维护的Selector
并对其后续的I/O事件进行处理,专门负责网络的读写。之后使用ServerBootstrap
服务端启动引导类配置整个Netty程序,串联各个组件进行启动。
在创建Server端时,我们在ChannelPipeline
中添加了Netty自带的String类型的编码器与解码器,最后添加我们的业务逻辑处理的handler
。
总结一下,在服务端启动时核心逻辑为:
- 获取管道
pipeline
- 向
pipeline
中加入解码器和编码器 - 加入业务处理器
handler
到这服务端我们就已经写完了,再开始写客户端。
Handler
接下来,我们要先创建一个服务端的handler
,用于处理业务逻辑。具体的业务处理就在这里面进行定义,新建一个类继承SimpleChannelInboundHandler
类,处理接收的消息:
首先定义一个ChannelGroup
组,管理所有channel
。GlobalEventExecutor.INSTANCE
表示是全局的事件执行器,是一个单例模式。
下面我们看一下需要重写的几个核心方法:
handlerAdded
handlerAdded
表示连接建立,一旦连接将被第一个被执行。在该方法中,将当前channel
加入到channelGroup
。并将该客户加入聊天的信息推送给其他在线的客户端,这里遍历channelGroup
中所有channel
,并发送消息。
handlerRemoved
断开连接会触发handlerRemoved
方法。在该方法中,将下线消息推送给当前在线的客户。
需要注意,执行了当前方法时就相当于已经执行了:
这里会自动执行remove
方法,所以就不需要我们再额外手动调用remove
方法了。
channelActive & channelInactive
channelActive
方法和channelInactive
方法表示channel
处于活动状态或不活动状态,这里仅打印上下线信息即可。
channelRead0
channelRead0()
是读取数据方法,用于接收客户端发送的信息。
在该方法中,会遍历channelGroup
,根据不同情况,我们需要发送不同的消息:
- 如果不是当前的
channel
,那么显示其他客户端发送了消息 - 如果是当前的
channel
,显示自己发送了信息
exceptionCaught
最后,在发生异常时执行exceptionCaught
方法,只要关闭ChannelHandlerContext
就好了。
客户端代码
Client
和服务端比起来,客户端的设计就简单多了,客户端不再需要两个EventLoopGroup
,只需要一个就可以了。详细代码如下,其中获取管道、向管道加入编码解码器和设置业务处理器handler
的过程与服务端基本相同。
注意,客户端与服务端明显不同的是,客户端需要输入发送给别人的信息,因此创建一个扫描器,接收来自键盘的输入。
Handler
接下来定义一个客户端业务处理器,同样继承SimpleChannelInboundHandler
:
相对于服务端相比非常简单,只需要重写channelRead0
方法,在其中只要打印接收到的信息即可。
通信测试
下面对这个过程进行一下测试,首先启动一个服务端和三个客户端,在服务端会打印客户端的上线信息:
服务端启动时打印的信息:
较早登录的客户端会收到服务端转发的其他客户端的上线信息:
发送接收消息测试,会根据发送者的不同进行显示的区分:
其余客户端下线时,当前客户端会显示下线信息:
服务端显示的下线信息:
到这里,一个群聊系统的基本功能就已经实现了。需要注意的是,这里是不支持点对点的聊天的,如果再需要点对点的聊天,那么就不能使用ChannelGroup
了,可以使用HashMap
缓存各个Channel
的信息,实现定向的消息发送。
学会了!!!!
优秀~