Netty学习(五)-DelimiterBasedFrameDecoder
上一节我们说了LineBasedframeDecoder来解决粘包拆包的问题,TCP以流的方式进行数据传输,上层应用协议为了对消息进行区分,一般采用如下4种方式:
- 消息长度固定,累计读取到消息长度总和为定长Len的报文之后即认为是读取到了一个完整的消息。计数器归位,重新读取。
- 将回车换行符作为消息结束符。
- 将特殊的分隔符作为消息分隔符,回车换行符是他的一种。
- 通过在消息头定义长度字段来标识消息总长度。
LineBasedframeDecoder属于第二种,今天我们要说的DelimiterBasedFrameDecoder和FixedLengthFrameDecoder属于第三种和第一种。DelimiterBasedFrameDecoder用来解决以特殊符号作为消息结束符的粘包问题,FixedLengthFrameDecoder用来解决定长消息的粘包问题。下面首先来用DelimiterBasedFrameDecoder来写一个例子,我们看一下效果然后接着分析用法。
1. DelimiterBasedFrameDecoder使用
服务端:
public class HelloWordServer {
private int port;
public HelloWordServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer());
try {
ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
HelloWordServer server = new HelloWordServer(7788);
server.start();
}
}
服务端ServerChannelInitializer:
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
ByteBuf delimiter = Unpooled.copiedBuffer("\t".getBytes());
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(2048,delimiter));
// 字符串解码 和 编码
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 自己的逻辑Handler
pipeline.addLast("handler", new ServerHandler());
}
}
服务端handler:
public class ServerHandler extends ChannelInboundHandlerAdapter {
private int counter;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String)msg;
System.out.println("server receive order : " + body + ";the counter is: " + ++counter);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}
客户端:
public class HelloWorldClient {
private int port;
private String address;
public HelloWorldClient(int port,String address) {
this.port = port;
this.address = address;
}
public void start(){
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
try {
ChannelFuture future = bootstrap.connect(address,port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
HelloWorldClient client = new HelloWorldClient(7788,"127.0.0.1");
client.start();
}
}
客户端ClientChannelInitializer:
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
/*
* 这个地方的 必须和服务端对应上。否则无法正常解码和编码
*
*
*/
ByteBuf delimiter = Unpooled.copiedBuffer("\t".getBytes());
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(2048,delimiter));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 客户端的逻辑
pipeline.addLast("handler", new ClientHandler());
}
}
客户端handler:
public class ClientHandler extends ChannelInboundHandlerAdapter {
private byte[] req;
private int counter;
public ClientHandler() {
req = ("Unless required by applicable law or agreed to in writing, software\t" +
" distributed under the License is distributed on an \"AS IS\" BASIS,\t" +
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\t" +
" See the License for the specific language governing permissions and\t" +
" limitations under the License.This connector uses the BIO implementation that requires the JSSE\t" +
" style configuration. When using the APR/native implementation, the\t" +
" penSSL style configuration is required as described in the APR/native\t" +
" documentation.An Engine represents the entry point (within Catalina) that processes\t" +
" every request. The Engine implementation for Tomcat stand alone\t" +
" analyzes the HTTP headers included with the request, and passes them\t" +
" on to the appropriate Host (virtual host)# Unless required by applicable law or agreed to in writing, software\t" +
"# distributed under the License is distributed on an \"AS IS\" BASIS,\t" +
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\t" +
"# See the License for the specific language governing permissions and\t" +
"# limitations under the License.# For example, set the org.apache.catalina.util.LifecycleBase logger to log\t" +
"# each component that extends LifecycleBase changing state:\t" +
"#org.apache.catalina.util.LifecycleBase.level = FINE\t"
).getBytes();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf message;
message = Unpooled.buffer(req.length);
message.writeBytes(req);
ctx.writeAndFlush(message);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String buf = (String)msg;
System.out.println("Now is : " + buf + " ; the counter is : "+ (++counter));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
输出如下:
server receive order : Unless required by applicable law or agreed to in writing, software;the counter is: 1
server receive order : distributed under the License is distributed on an "AS IS" BASIS,;the counter is: 2
server receive order : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.;the counter is: 3
server receive order : See the License for the specific language governing permissions and;the counter is: 4
server receive order : limitations under the License.This connector uses the BIO implementation that requires the JSSE;the counter is: 5
server receive order : style configuration. When using the APR/native implementation, the;the counter is: 6
server receive order : penSSL style configuration is required as described in the APR/native;the counter is: 7
server receive order : documentation.An Engine represents the entry point (within Catalina) that processes;the counter is: 8
server receive order : every request. The Engine implementation for Tomcat stand alone;the counter is: 9
server receive order : analyzes the HTTP headers included with the request, and passes them;the counter is: 10
server receive order : on to the appropriate Host (virtual host)# Unless required by applicable law or agreed to in writing, software;the counter is: 11
server receive order : # distributed under the License is distributed on an "AS IS" BASIS,;the counter is: 12
server receive order : # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.;the counter is: 13
server receive order : # See the License for the specific language governing permissions and;the counter is: 14
server receive order : # limitations under the License.# For example, set the org.apache.catalina.util.LifecycleBase logger to log;the counter is: 15
server receive order : # each component that extends LifecycleBase changing state:;the counter is: 16
server receive order : #org.apache.catalina.util.LifecycleBase.level = FINE;the counter is: 17
启动服务端和客户端,我们能看到服务端接收客户端发过来的消息一共分17次接收。那么为什么是17次呢?而且我们并没有使用在上一篇中解决拆包和粘包问题的LineBasedFrameDecoder,并且这次我们的消息每一行的末尾也换成了”\t”。下面就来讲解一下DelimiterBasedFrameDecoder的使用。
DelimiterBasedFrameDecoder是将特殊的字符作为消息的分隔符,本例中用到的是”\t”。而LineBasedFrameDecoder是默认将换行符”\n”作为消息分隔符。首先我们注意到在ServerChannelInitializer中我们在添加解码器时跟以前有点不一样:
ByteBuf delimiter = Unpooled.copiedBuffer("\t".getBytes());
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(2048, delimiter));
这里我们添加DelimiterBasedFrameDecoder解码器并且手动指定消息分隔符为:”\t”。我们可以看一下DelimiterBasedFrameDecoder的构造方法:
public DelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, ByteBuf delimiter) {
this(maxFrameLength, stripDelimiter, true, delimiter);
}
maxFrameLength:解码的帧的最大长度
stripDelimiter:解码时是否去掉分隔符
failFast:为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常
delimiter:分隔符
这个时候大家应该明白了为什么服务端分17次收到消息。我们在消息的每一行都加了一个”\t”,自然解码器在度消息时遇到”\t”就会认为这是一条消息的结束。用这种方式我们可以把”\t”换成任何我们自定义的字符对象。换成”\n”也是可以的。
2. FixedLengthFrameDecoder使用
FixedLengthFrameDecoder是固定长度解码器,它能够按照指定的长度对消息进行自动解码。使用它也没有什么特别费力的事情,在ServerChannelInitializer类中添加:
pipeline.addLast(new FixedLengthFrameDecoder(23));//参数为一次接受的数据长度
即可,同时也别忘了把刚才使用的DelimiterBasedFrameDecoder注释掉啊,不然达不到效果。
Netty学习(五)-DelimiterBasedFrameDecoder的更多相关文章
- Netty学习五:Buffers
1. Netty中的缓冲 在Netty中并没有使用Java自带的ByteBuffer,而是自己实现提供了一个缓存区来用于标识一个字节序列,并帮助用户操作原始字节或者自定义的POJO. Java NIO ...
- Netty 学习(五):服务端启动核心流程源码说明
Netty 学习(五):服务端启动核心流程源码说明 作者: Grey 原文地址: 博客园:Netty 学习(五):服务端启动核心流程源码说明 CSDN:Netty 学习(五):服务端启动核心流程源码说 ...
- Netty 学习笔记(1)通信原理
前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ...
- Netty学习——基于netty实现简单的客户端聊天小程序
Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...
- Netty学习笔记-入门版
目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...
- netty学习资料
netty学习资料推荐官方文档和<netty权威指南>和<netty in action>这两本书.下面收集下网上分享的资料 netty官方参考文档 Netty 4.x Use ...
- Netty学习之客户端创建
一.客户端开发时序图 图片来源:Netty权威指南(第2版) 二.Netty客户端开发步骤 使用Netty进行客户端开发主要有以下几个步骤: 1.用户线程创建Bootstrap Bootstrap b ...
- TweenMax动画库学习(五)
目录 TweenMax动画库学习(一) TweenMax动画库学习(二) TweenMax动画库学习(三) Tw ...
- netty学习资源收集
Netty学习笔记 Netty In Actions CSDN专栏 一起学Netty-CSDN专栏 Netty In Action中文版
随机推荐
- 小白开学Asp.Net Core 《六》
小白开学Asp.Net Core <六> —— 探究.Net Core 跨平台的奥秘 1.写这篇文章的初衷 有好多朋友反馈看不懂我写的开源的一个练手项目(GitHub:https://gi ...
- scrapy基础知识之 Scrapy-Redis分布式策略:
Scrapy-Redis分布式策略: 假设有四台电脑:Windows 10.Mac OS X.Ubuntu 16.04.CentOS 7.2,任意一台电脑都可以作为 Master端 或 Slaver端 ...
- Windows下通过VNC远程访问Linux服务器,并实现可视化
前言 最近因部门需要,老大想让我在公司Linux服务器上弄个Oracle,以用作部门测试环境的数据库服务器,经过一番折腾后,成功完成了任务.因公司Linux服务器是无图形界面的,本人接触Linux不多 ...
- linux系统的基础优化
目录 前言 网络优化 在虚拟软件中配置虚拟局域网 接着可以配置自己windows主机的网络连接配置 在虚拟软件中虚拟机添加网卡 虚拟机中的系统基础优化 前言 在自己做linux的相关服务实验时,是没有 ...
- 五分钟了解Zabbix
Zabbix-简介 Zabbix概念 Zabbix组成 Server Zabbix server 是 Zabbix agent 向其报告可用性.系统完整性信息和统计信息的核心组件.是存储所有配置信息. ...
- 个人永久性免费-Excel催化剂功能第56波-获取Excel对象属性相关自定义函数
之前零散开发过一些自定义函数获取Excel对象属性,此次再细细地把有价值的属性都一一给开发完成,某些场景下,有这些小函数还是可以比较方便地实现一些通过Excel界面没法轻松获取到的信息. 修复与更新 ...
- Linux 安装MySql——apt-get版
0)apt-get update 1)通过apt-get安装 更新设置到最新系统: sudo apt-get update sudo apt-get upgrade sudo apt-ge ...
- wp伪静态网页打开慢之提速方案1s内打开 wp的静态化插件测试
自上篇文章,我做了伪静态话.可是伪静态访问还是php动态页面,还需要服务端分析如何处理,访问页面时会发现有一个漫长的等待响应的时间.这是打开速度在4s左右.而静态页面则是直接打开,不需要服务器操作,不 ...
- WebGL中深度碰撞方法总结
z-fighting问题是三维渲染中常见的问题,本文根据实际工作中遇到的一些场景,进行了系统的总结 一个实际工作中的问题 当两个面离得太近就会发生深度碰撞问题,比如: 遇到深度检测问题,最重要的是先搞 ...
- .gitignore文件我自己常用的配置
我项目中一般不需要上传到git服务器上的有 .idea ------.idea目录 .mvn ------.mvn目录 .iml mvnw mvnw.cmd logs/ --- 我生成的日志文件目录 ...