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/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
netty系列之:文本聊天室的更多相关文章
- Netty之多用户的聊天室(三)
Netty之多用户的聊天室(三) 一.简单说明 笔者有意将Netty做成一个系列的文章,因为笔者并不是一个善于写文章的人,而且笔者学习很多技术一贯的习惯就是敲代码,很多东西敲着敲着就就熟了,然后再进行 ...
- 用SignalR 2.0开发客服系统[系列2:实现聊天室]
前言 交流群:195866844 上周发表了 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 这篇文章,得到了很多帮助和鼓励,小弟在此真心的感谢大家的支持.. 这周继续系列2,实现聊天室 ...
- Netty高级应用及聊天室实战
Netty 高级应用 1. 编解码器 概念:在网络应用中,需要实现某种编解码器.将原始字节数据与自定义消息数据进行相互转换.网络中都是以字节码的形式传输的. 对Netty而言,编解码器由两部分组成:编 ...
- netty系列之:对聊天进行加密
目录 简介 PKI标准 各类证书的后缀和转换 netty中启动SSL server netty中启动SSL client 总结 简介 在之前的文章中,我们讲到了怎么使用netty建立聊天室,但是这样的 ...
- Netty学习笔记(四) 简单的聊天室功能之服务端开发
前面三个章节,我们使用了Netty实现了DISCARD丢弃服务和回复以及自定义编码解码,这篇博客,我们要用Netty实现简单的聊天室功能. Ps: 突然想起来大学里面有个课程实训,给予UDP还是TCP ...
- netty实现消息中心(二)基于netty搭建一个聊天室
前言 上篇博文(netty实现消息中心(一)思路整理 )大概说了下netty websocket消息中心的设计思路,这篇文章主要说说简化版的netty聊天室代码实现,支持群聊和点对点聊天. 此demo ...
- Netty 系列八(基于 WebSocket 的简单聊天室).
一.前言 之前写过一篇 Spring 集成 WebSocket 协议的文章 —— Spring消息之WebSocket ,所以对于 WebSocket 协议的介绍就不多说了,可以参考这篇文章.这里只做 ...
- 在线聊天室的实现(2)--基于Netty 4.x的Echo服务器实现
前言: 就如前文所讲述的, 聊天室往往是最基本的网络编程的学习案例. 本文以WebSocket为底层协议, 实现一个简单的基于web客户端的Echo服务. 服务器采用Netty 4.x来实现, 源于其 ...
- Netty入门(二)之PC聊天室
参看Netty入门(一):Netty入门(一)之webSocket聊天室 Netty4.X下载地址:http://netty.io/downloads.html 一:服务端 1.SimpleChatS ...
随机推荐
- 关于使用Draw.io画数据库E-R图的说明
背景简介 E-R图也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型.属性和联系的方法,用来描述现实世界的概念模型. diagrams.net是用于构建图表 ...
- 『无为则无心』Python序列 — 18、Python列表概念及常用操作API
目录 1.列表的概念 (1)列表的定义 (2)列表的应用场景 (3)列表的定义格式 2.列表的常用操作 (1)列表的查找 1)通过下标查找 2)通过方法查找 3)判断是否存在 (2)列表的增加 @1. ...
- 27、Tomcat服务的安装与配置
服务器名称 ip地址 slave-node1 172.16.1.91 27.1. Tomcat简介: Tomcat是Apache软件基金会(Apache Software Foundation)的Ja ...
- 32、JavaScript介绍
32.1.JavaScript概述: 1.JavaScript的历史: 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中),后将其改名Scr ...
- 23、ORA-00439:DEFERRED_SEGMENT_CREATION 问题解决
23.1.说明: 1. 众所周知,在清空表内所有数据时,truncate比delete要快很多,原因是,delete语句每次删除一行,都在事务日志中为所删除的每行记录一项. truncate通过释放存 ...
- CentOS-Docker搭建VeryNginx
下载镜像 $ docker pull camil/verynginx $ cd /home GIT克隆(yum install git -y) $ git clone https://github.c ...
- SpringCloud:feign默认jackson解析'yyyy-MM-ddTHH:mm:ssZ'时间格式报错
Feign默认的使用jackson解析,所以时间传值时会报错,时间格式错误 解决办法: 修改feign解析方式为fastjson方式: @Configuration public class CxfC ...
- linux学习之路第七天(压缩和解压类指令详解)
压缩和解压类 1.gzip/gunzip 指令 gzip 指令用于压缩文件, gunzip用于解压的 基本语法 gzip 文件 (功能描述:压缩文件,指令将文件压缩成*.gz文件) gunzip 文件 ...
- Parrot os 安装vmtools
1.更新源(这步个人觉得官方源还可以,没网上说的那么慢) vim /etc/apt/sources.list.d/parrot.list linux命令 ,按i进入修改模式,修改结束,之后先按esc, ...
- leetcode 字符串转换整数 (模拟)
思路分析 1.跟着题意模拟,分成几种情况来看待 2.一种全是空格 3.有可能有空格,然后有符号的 4.有可能有空格,无符号数字 5.有可能有空格,非数字开头 6.最后还需要考虑一个越界的问题,所以要除 ...