Chat_Room(Netty 入门级项目)

github项目推荐:Chat_Room

Netty 入门级项目

码云:https://gitee.com/baldness_and_coldness/Chat_Room
Github:https://github.com/fff-bald/Chat_Room
Netty入门相关知识:https://blog.csdn.net/qq_35751014/category_9722459.html

一、简单介绍

Chat_Room是一个基于Netty开发的命令行聊天室程序,使用了C/S架构,用户使用客户端向服务器发送短信,由服务器转发至所有连接中的客户端。
在这里插入图片描述

二、工作流程

客户端:

1、客户端连接服务器:客户端通过服务器的ip地址和端口号连接上服务器,加入到聊天室中。

(实质上是channel和EventLoopGroup建立和连接)
2、客户端发送短信:通过SocketChannel向服务器发送数据。

服务器:

1、服务器初始化:定义EventLoopGroup,通过ServerBootstrap对服务器初始化,选择对外暴露的端口,初始化channel并绑定handler。

3、用户记录
上线,当有客户端第一次连接服务器,该channel 处于活动状态,将channel存于数组中,channel与用户一一绑定。

下线,当channel 断开连接时,从channel数组中删除对应的channel,之后有短信传入也不会在发送到该客户端。

2、短信转发:当有客户端向服务器发送短信时,服务器遍历channel数组,通过channel向每个客户端发送这条短信。

三、核心实现

项目的核心功能是通过Netty实现的,Netty的相关基础知识需要大家系统性地学习。

Netty是由JBOSS提供的一个java开源框架,现为
Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

使用Netty实现功能,最主要的是handler的编写,需要实现客户端发送消息到服务器,服务器将消息转发到所有客户端中。

Clint 中 handler的实现很简单,只要展示由服务器发来的短信即可。

自定义一个handler继承SimpleChannelInboundHandler< String>,重写channelRead0方法就行了,当服务器给客户端发送短信时,这个方法就会触发。

public class MyClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String meg) throws Exception {
        WriteAndPrint.println(meg.trim());
    }
}

Server中的handle要实现的功能就相对多一些,需要记录所有注册过的客户端,并且从客户端中收到信息后,需要完成对信息的转发。

同样自定义一个handler继承SimpleChannelInboundHandler< String>

handlerAdded(ChannelHandlerContext ctx) 此方法表示连接建立,一旦建立连接,就第一个被执行
channelActive(ChannelHandlerContext ctx) 见名知义,表示 channel 处于活动状态
channelInactive(ChannelHandlerContext ctx) 与上相似,表示 channel 处于不活动状态
handlerRemoved(ChannelHandlerContext ctx) throws Exception 表示 channel 断开连接
channelRead0(ChannelHandlerContext ctx, String msg) 与client同

四、Netty进阶

如果你看到了这里,或者在github上看过了这个项目的源代码,你可能会觉得这个项目太简单了,想要拓展更多的功能,却发现无从入手。

事件驱动

这是很正常的,因为这个项目没有对可拓展性进行考虑,服务器粗暴地将所有来自客户端的数据都当成了需要转发的短信,这不利于我们之后对功能的拓展。

问题:
举个栗子,我希望实现这么一个功能:
用户可以自定义自己在聊天室中的昵称昵称。

想要实现这个功能,用户自定义的昵称就必须要从客户端传递到服务器。
但对于服务器来说,所有从客户端发来的数据地位都是相同的,它无法识别出哪一条是对用户昵称的定义。

自然而然的我们就会想到要在数据中加标志位
例如在这个chatroom程序中,我们可以在客户端每条发送的String信息前拼上一位的标志位,”1”代表是要发送的信息,”2“代表要用户设置的昵称。

这种方法虽然简单,但是不推荐
万一后面实现的功能越来越多,拼接字符串不好管理。

解决办法
我们可以将发送的消息从一条String变成一个Message类,类中包括消息头和消息体,消息头为标志位,消息体为实际短信。
不过要对类进行序列化编码,Netty自带的编码器就不太给力了,目前很多公司都是使用http+jsontcp+protobuf

适配器模式

还是回到原本的问题,如果实现的功能越来越多,简单的if-else式判断就会变得不易于拓展,然后我们就能想到使用适配器设计模式。

如果你们对 适配器模式 + 事件驱动的Netty开发 感兴趣,
我推荐你们下一个Netty的进阶项目:nico / ratel

nico / ratel
简单介绍:https://blog.csdn.net/awsl_6699/article/details/115604307
码云地址:https://gitee.com/ainilili/ratel

猜你喜欢

转载自blog.csdn.net/awsl_6699/article/details/115603301