netty系列之:文本聊天室
简介
经过之前的系列文章,我们已经知道了netty的运行原理,还介绍了基本的netty服务搭建流程和消息处理器的写法。今天本文会给大家介绍一个更加复杂的例子,文本聊天室。
聊天室的工作流程
今天要介绍的是文本聊天室,对于文本聊天室来说,首先需要建立一个服务器,用于处理各个客户端的连接,对于客户端来说,需要建立和服务器的连接,然后向服务器输入聊天信息。服务器收到聊天信息之后,会对消息进行响应,并将消息返回至客户端,这样一个聊天室的流程就完成了。
文本处理器
之前的文章中,我们有提到过,netty的传输只支持ByteBuf类型,对于聊天室直接输入的字符串是不支持的,需要对字符串进行encode和decode转换。
之前我们介绍的encode和decode的类叫做ObjectDecoder和ObjectEncoder。今天我们再介绍两个专门处理字符串的StringDecoder和StringEncoder。
StringEncoder要比ObjectEncoder简单很多,因为对于对象来说,我们还需要在Byte数组的头部设置Byte数组的大小,从而保证对象所有数据读取正确。对于String来说,就比较简单了,只需要保证一次读入的数据都是字符串即可。
StringEncoder继承自MessageToMessageEncoder,其核心的encode代码如下:
protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception { if (msg.length() == 0) { return; } out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset)); }
从上面的代码可以看出,核心实际上是调用了ByteBufUtil.encodeString方法,将String转换成了ByteBuf。
对于字符串编码来说,还需要界定一个编码的范围,比如我们需要知道需要一次编码多少字符串,一般来说我们通过回车符来界定一次字符串输入的结束。
netty也提供了这样的非常便利的类叫做DelimiterBasedFrameDecoder,通过传入不同的Delimiter,我们可以将输入拆分成不同的Frame,从而对一行字符串进行处理。
new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()))
我再看一下StringDecoder的核心代码,StringDecoder继承自MessageToMessageDecoder:
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception { out.add(msg.toString(charset)); }
通过调用ByteBuf的toString方法,将BuyteBuf转换成为字符串,并且输出到channel中。
初始化ChannelHandler
在initChannel的时候,我们需要向ChannelPipeline中添加有效的Handler。对于本例来说,需要添加StringDecoder、StringEncoder、DelimiterBasedFrameDecoder和真正处理消息的自定义handler。
我们将初始化Pipeline的操作都放在一个新的ChatServerInitializer类中,这个类继承自ChannelInitializer,其核心的initChannel方法如下:
public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 添加行分割器 pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); // 添加String Decoder和String Encoder,用来进行字符串的转换 pipeline.addLast(DECODER); pipeline.addLast(ENCODER); // 最后添加真正的处理器 pipeline.addLast(SERVER_HANDLER); }
ChatServerInitializer在Bootstrap中的childHandler中进行添加:
childHandler(new ChatServerInitializer())
真正的消息处理逻辑
有了上面的逻辑之后,我们最后只需要专注于真正的消息处理逻辑即可。
这里我们的逻辑是当客户端输入“再见”的时候,就关闭channel,否则就将消息回写给客户端。
其核心逻辑如下:
public void channelRead0(ChannelHandlerContext ctx, String request) throws Exception { // 如果读取到"再见"就关闭channel String response; // 判断是否关闭 boolean close = false; if (request.isEmpty()) { response = "你说啥?\r\n"; } else if ("再见".equalsIgnoreCase(request)) { response = "再见,我的朋友!\r\n"; close = true; } else { response = "你是不是说: '" + request + "'?\r\n"; } // 写入消息 ChannelFuture future = ctx.write(response); // 添加CLOSE listener,用来关闭channel if (close) { future.addListener(ChannelFutureListener.CLOSE); } }
通过判断客户端的出入,来设置是否关闭按钮,这里的关闭channel是通过向ChannelFuture中添加ChannelFutureListener.CLOSE来实现的。
ChannelFutureListener.CLOSE是一个ChannelFutureListener,它会在channel执行完毕之后关闭channel,事实上这是一个非常优雅的关闭方式。
ChannelFutureListener CLOSE = new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { future.channel().close(); } };
对于客户端来说,其核心就是从命令行读取输入,这里使用InputStreamReader接收命令行输入,并使用BufferedReader对其缓存。
然后将命令行输入通过调用 ch.writeAndFlush写入到channel中,最后监听命令行输入,如果监听到“再见“,则等待server端关闭channel,其核心代码如下。
// 从命令行输入 ChannelFuture lastWriteFuture = null; BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); for (;;) { String line = in.readLine(); if (line == null) { break; } // 将从命令行输入的一行字符写到channel中 lastWriteFuture = ch.writeAndFlush(line + "\r\n"); // 如果输入'再见',则等待server端关闭channel if ("再见".equalsIgnoreCase(line)) { ch.closeFuture().sync(); break; } } // 等待所有的消息都写入channel中 if (lastWriteFuture != null) { lastWriteFuture.sync(); }
总结
经过上面的介绍,一个简单的聊天室就建成了。后续我们会继续探索更加复杂的应用,希望大家能够喜欢。
本文的例子可以参考:learn-netty4
本文已收录于 http://www.flydean.com/10-netty-chat/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Firefox 92开始默认启用WebRender:Firefox 93开始无法禁用
在即将发布(计划 9 月 7 日)的 Firefox 92 版本更新中,Mozilla 面向所有用户公开了 WebRender,使其成为 Windows、macOS、Linux 和 Android 的默认渲染引擎。在即将发布的 Firefox 93 版本中,用户将无法关闭该引擎,Mozilla 会从 Mozilla Central 中移除这个偏好,并限制允许这么做的环境变量。 那什么是 WebRender? WebRender 能够更快、更流畅地渲染页面。Mozilla 在 WebRender 渲染引擎上投入了大量的时间。该公司从 Servo 开始将其添加到 Firefox 中。 如何查看 WebRender 是否已经启用 访问 about:support 向下滚动到图像 检查合成字段的值,如果值显示为 WebRender,那么它在 Firefox 浏览器中是启用的。 目前,适用于 macOS(Firefox 83)和 Linux(Firefox 91)平台的 Firefox 已经默认启用该渲染引擎。而 Windows 和 Android 平台上 Firefox 92 版本也默认启用该...
- 下一篇
千亿级模型在离线一致性保障方案详解
导读:在模型的全链路测试过程中,模型的问题统一定义成广义一致性性问题,一致性问题也是模型稳定性的基础保障,落地到具体点上从维度上划分可以分为数据不一致、延时不一致、策略机制不一致、性能不一致等几个方向,在后果衍生上都会导致模型稳定性指标抖动,预估效果不符合预期,所以一致性测试在当前大规模机器学习的测试过程中有着非常重要的作用,但是在通常对于模型的测试方案也很难做到全局解决,其中也牵扯到效果和效率的折中。文章在一致性几个方面要做到核心节点的策略和效果重点保障,最大限度保障停更和回滚的发生,当前一致性方案在百度商业内部落地收益较好,在效果和效率上也做了很多的优化策略,整体方案满足业务线的需求。 全文6948字,预计阅读时间 12分钟。 一、背景与概述 点击率模型在广告检索阶段预估广告可能被点击的概率,在广告的排序、截断中起着重要的作用。 点击率模型分为在线预估和离线训练两部分,离线训练主要进行模型的训练和评估,在线预估主要是模型的应用和反馈。具体的在离线模型闭环链路如图1所示,线上点击和展现的广告数据到视图日志,经过反作弊系统和流式特征抽取后得到样本作用于模型训练,训练好的模型经过评估后应...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19