【Netty】「项目实战」(一)如何构建多客户端聊天室
前言
本篇博文是《从 0 到 1 学习 Netty》中实战系列的第一篇博文,主要内容是使用 Netty 构建包含登录、私聊、群聊、退出等功能的多客户端聊天室,往期系列文章请访问博主的 Netty 专栏,博文中的所有代码全部收集在博主的 GitHub 仓库中;
整体结构
本文将介绍如何使用 Netty 构建一个多客户端聊天室,包括用户登录、消息发送、多人聊天、退出聊天等核心功能,让读者了解 Netty 的基本使用方法,并具备构建简单的聊天室的能力。
项目整体结构如下所示:
client
包:存放客户端相关类;message
包:存放各种类型的消息;protocol
包:存放自定义协议;server
包:存放服务器相关类;handler
包:存放消息相关处理类;service
包:存放用户相关服务类;session
包:存放聊天相关会话类;
用户登录功能实现
为了让用户能够进行登录操作,我们需要实现一个登录请求消息的传递机制,流程示意图如下所示:
我们将使用 LoginRequestMessage
来封装用户输入的账号密码信息,并将其发送给服务端进行验证,客户端实现代码如下所示:
同时,服务端将会使用 SimpleChannelInboundHandler
来关注并处理特定类型的消息,如 LoginRequestMessage
,因为这个消息包含了用户的登录信息,所以服务端会对这些信息进行验证,通过后会将用户名与 channel 进行绑定,并返回相应的结果给客户端。请注意,以上代码仅是为了凸显使用 Netty 完成登录的过程,因此示例简化了业务,实际上应该将访问令牌返回给客户端。
服务端实现代码如下所示:
运行结果:
客户端向服务端发送登录请求后,需要等待服务端返回登录结果才能进行接下来的操作。
这时可以创建一个 CountDownLatch
对象,并将其初始值设置为 1。在发送登录请求的线程中,调用 await()
方法使该线程进入等待状态,而在服务端返回登录结果后,调用 countDown()
方法对计数器进行减一操作,此时该线程就会被唤醒并继续执行接下来的代码。
同时,可以使用 AtomicBoolean
类型的变量来记录用户登录状态,因为 AtomicBoolean
类型的变量可以确保读写操作的原子性,避免出现线程安全问题。
添加客户端代码如下:
运行结果:
总而言之,使用 CountDownLatch
进行计数,并结合 AtomicBoolean
记录登录状态,可以有效地保证程序的并发安全性和正确性。
消息发送功能实现
在实现用户登录功能之后,下一步需要着手完成聊天功能的开发,其中一个核心功能就是消息发送功能。消息发送功能旨在让聊天参与者双方都在线时可以实现实时通信,流程示意图如下所示:
为了实现这一过程,我们可以使用 ChatRequestMessage
对象来封装消息,ChatRequestMessage
是一个自定义的 Java 类型,它包含了发送方 from
、接收方 to
和消息正文 content
等信息。
而且我们还需要通过执行指令 send name content
来实际发送消息,客户端实现代码如下所示:
同时,服务器需要对此进行相应的处理,使用 SimpleChannelInboundHandler
来关注并处理特定类型的消息 ChatRequestMessage
,当服务器接收到一条 ChatRequestMessage
消息时,它将首先解析出其中的接收方 to
。
接着,服务器会遍历所有已经连接到服务器上的客户端 channel,查找是否存在一个 channel 的属性值与接收方 to
相匹配。如果匹配成功,则说明接收方在线,并且服务器会将处理过的消息通过该 channel 发送至接收方;否则,服务器将认为接收方当前不在线。
ChatRequestMessageHandler
实现代码如下所示:
运行结果:
多人聊天功能实现
多人聊天是指在一个聊天室中,多个用户可以进行实时聊天的功能。在实现多人聊天之前,我们已经实现了用户登录功能和消息发送功能,这两个功能是多人聊天的基础。
为了实现多人聊天,我们需要添加一些新的功能:创建群聊、发送消息到群聊、查看成员列表、加入群聊和退出群聊。
其中,创建群聊是指用户可以自己创建一个聊天室,并邀请其他用户加入。发送消息到群聊是指用户可以将消息发送到所在的群聊中,让其他成员看到。查看成员列表是指用户可以查看当前群聊中的所有成员。加入群聊是指用户可以选择加入已有的群聊,开始和其他成员聊天。退出群聊是指用户可以主动退出一个群聊,不再接收该群聊的消息。
创建群聊
我们将仿造 QQ 群聊或者微信群聊的创建流程,创建指令为 gcreate [group name] [m1,m2,m3...]
。
首先是从用户那里收集一些信息,包括群聊的名称和其成员的列表。为了确保群组中没有重复的成员,我们可以使用一个 set
数据结构来存储成员名称。
收集完这些信息后,我们可以使用自定义类 GroupCreateRequestMessage
创建一个新消息,此消息将包含服务器创建群聊所需的所有信息,包括群组的名称和成员列表。然后通过网络连接将此消息发送到服务器。
客户端代码如下所示:
在服务器端,服务器将接收 GroupCreateRequestMessage
并解析其内容。然后,它将使用这些信息创建一个指定名称的新群聊,并邀请相关成员加入。如果群聊已经存在,则会创建失败。
服务端代码如下所示:
运行结果:
需要完整代码的读者请访问博主的 Github:GroupCreateRequestMessageHandler;
发送消息
在实现创建群聊功能之后,我们需要实现发送消息的功能,以便在群聊中进行交流。为了确保每个在线成员都能够及时收到消息,我们需要采用一种广播机制来实现消息的分发。
具体而言,我们可以通过遍历所有的聊天室成员所对应的 channel,将消息发送给每一个在线用户。当然,这种方式并不是最高效的方法,因为如果有大量的在线用户,这会导致服务器性能下降。
因此,在实际应用中,可能会使用消息队列或者事件通知等更加高效的消息传递机制来实现。
代码如下所示:
运行结果:
需要完整代码的读者请访问博主的 Github:GroupChatRequestMessageHandler;
查看成员
gmembers [group name]
需要完整代码的读者请访问博主的 Github:GroupMembersRequestMessageHandler;
加入群聊
gjoin [group name]
需要完整代码的读者请访问博主的 Github:GroupJoinRequestMessageHandler;
退出群聊
gquit [group name]
需要完整代码的读者请访问博主的 Github:GroupChatRequestMessageHandler;
后记
通过本文的介绍,我们详细了解了如何使用 Netty 构建一个多客户端聊天室。在这个过程中,我们复习了 Netty 的基础知识,包括 Netty 编程模型、Channel、EventLoop 和 Pipeline 等概念,并通过实现用户登录、消息发送、多人聊天、退出聊天等核心功能,加深了对 Netty 的理解。
通过本示例,我们不仅可以掌握 Netty 的基本使用方法,而且可以使用这些技术构建更高级别的网络应用程序。
以上就是 Netty 如何构建多客户端聊天室 的所有内容了,希望本篇博文对大家有所帮助!
参考:
版权声明: 本文为 InfoQ 作者【sidiot】的原创文章。
原文链接:【http://xie.infoq.cn/article/08182963e8ecf55a90761375c】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论