学习netty遇到的关于 LineBasedFrameDecoder 的问题
最近在看《Netty权威指南》这本书,关于TCP粘包/拆包,书中使用的是 LineBasedFrameDecoder 来解决的,但是我在实践的过程中出现了问题,上代码吧。
这个是 server 的代码
package com.cd.netty4.zhc.demo.ex01; import java.text.SimpleDateFormat;
import java.util.Date; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.CharsetUtil; /**
* 本例子参考《Netty权威指南(第2版)》第4章
* 先运行 TimeServerExc02,然后运行 TimeClientExc02,可解决 TCP 粘包/拆包问题
* 使用 LineBasedFrameDecoder + StringDecoder 解决 TCP 粘包/拆包问题
* */
public class TimeServerExc02 {
public static void main(String args[]) {
System.out.println("---------------------- server 测试开始 ---------------------");
new TimeServerExc02().bind("127.0.0.1", 1234);
System.out.println("---------------------- server 测试end ---------------------");
} public void bind(String host, int port) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serboot = new ServerBootstrap().group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
arg0.pipeline().addLast("decoder", new StringDecoder());
arg0.pipeline().addLast("handler", new TimeServerHandlerExc02());
}
}); try {
// 绑定端口,同步等待成功
ChannelFuture future = serboot.bind(host, port).sync();
// 等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
} class TimeServerHandlerExc02 extends ChannelInboundHandlerAdapter { @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channelActive(有client连接上了)..");
ctx.writeAndFlush(Unpooled.copiedBuffer("您已经成功连接上了 server!", CharsetUtil.UTF_8)); // 必须有flush
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server channelRead..");
String msgStr = msg.toString();
System.out.println("读入client消息:" + msgStr);
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
ByteBuf resp = Unpooled.copiedBuffer(currentTime, CharsetUtil.UTF_8);
ctx.writeAndFlush(resp);
System.out.println("向client发送消息:" + currentTime);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} }
这个是client的代码:
package com.cd.netty4.zhc.demo.ex01; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.CharsetUtil; /**
* 本例子参考《Netty权威指南(第2版)》第4.2章
* */
public class TimeClientExc02 { public static void main(String[] args) {
try {
System.out.println("---------------------- client 测试开始 ---------------------");
new TimeClientExc02().connect("127.0.0.1", 1234);
System.out.println("---------------------- client 测试end ---------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
} private int count; public void connect(String host, int port) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap boot = new Bootstrap().group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast(new TimeClientHandlerExc02());
}
});
ChannelFuture future = boot.connect(host, port);
// 等待客户端链路关闭
future.channel().closeFuture().sync();
} finally {
// 优雅的退出,释放NIO线程组
group.shutdownGracefully();
}
} class TimeClientHandlerExc02 extends ChannelInboundHandlerAdapter { @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client channelActive(client 连接成功)..");
for (int i = 0; i < 50; i++) {
System.out.print(i + ",");
ctx.writeAndFlush(
Unpooled.copiedBuffer("It's a good day , I want to know time--" + i , CharsetUtil.UTF_8)); // 必须有flush
}
ctx.flush();
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("client channelRead.." + ++count);
String msgStr = msg.toString();
System.out.println("读入 server 消息:" + msgStr);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} } }
我先运行的是server,然后是client,发现 server 的 channelActive(..) 以及 client 的 channelActive(..) 都有运行到,但是后续的 channelRead(..) 方法却迟迟没有运行到,我把 LineBasedFrameDecoder 和 StringDecoder 这两个 解码器去掉,则代码正常,但是会有 TCP 粘包/拆包问题。
在网上查了问题原因,无果,认真看了两遍书,发现 LineBasedFrameDecoder 的工作原理是“ 它依次遍历ByteBuf中的可读字节,判断看是否有‘\n’或者‘\r\n’,如果有,就在此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行 ”。
所以,我的问题就出在消息结尾处没有加上换行符,修改代码后,可运行。修改后代码如下:
package com.cd.netty4.zhc.demo.ex01; import java.text.SimpleDateFormat;
import java.util.Date; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.CharsetUtil; /**
* 本例子参考《Netty权威指南(第2版)》第4章
* 先运行 TimeServerExc02,然后运行 TimeClientExc02,可解决 TCP 粘包/拆包问题
* 使用 LineBasedFrameDecoder + StringDecoder 解决 TCP 粘包/拆包问题
* 注意:使用 LineBasedFrameDecoder时,发送的消息结尾一定要是\n(官方是 System.getProperty("line.separator")),server端 和 client 都必须如此
* 因为LineBasedFrameDecoder 的工作原理是,依次遍历Bytebuf中的可读字节,判断是否有“\n”或者“\r\n”,如果有则在此位置结束
* */
public class TimeServerExc02 {
public static void main(String args[]) {
System.out.println("---------------------- server 测试开始 ---------------------");
new TimeServerExc02().bind("127.0.0.1", 1234);
System.out.println("---------------------- server 测试end ---------------------");
} public void bind(String host, int port) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serboot = new ServerBootstrap().group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
arg0.pipeline().addLast("decoder", new StringDecoder());
arg0.pipeline().addLast("handler", new TimeServerHandlerExc02());
}
}); try {
// 绑定端口,同步等待成功
ChannelFuture future = serboot.bind(host, port).sync();
// 等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
} class TimeServerHandlerExc02 extends ChannelInboundHandlerAdapter { @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channelActive(有client连接上了)..");
ctx.writeAndFlush(Unpooled.copiedBuffer("您已经成功连接上了 server!", CharsetUtil.UTF_8)); // 必须有flush
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server channelRead..");
String msgStr = msg.toString();
System.out.println("读入client消息:" + msgStr);
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
ByteBuf resp = Unpooled.copiedBuffer(currentTime + System.getProperty("line.separator"), CharsetUtil.UTF_8);
ctx.writeAndFlush(resp);
System.out.println("向client发送消息:" + currentTime);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} }
package com.cd.netty4.zhc.demo.ex01; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.CharsetUtil; /**
* 本例子参考《Netty权威指南(第2版)》第4.2章
* 注意:使用 LineBasedFrameDecoder时,发送的消息结尾一定要是\n(官方是 System.getProperty("line.separator")),server端 和 client 都必须如此
* 因为LineBasedFrameDecoder 的工作原理是,依次遍历Bytebuf中的可读字节,判断是否有“\n”或者“\r\n”,如果有则在此位置结束
* */
public class TimeClientExc02 { public static void main(String[] args) {
try {
System.out.println("---------------------- client 测试开始 ---------------------");
new TimeClientExc02().connect("127.0.0.1", 1234);
System.out.println("---------------------- client 测试end ---------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
} private int count; public void connect(String host, int port) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap boot = new Bootstrap().group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast(new TimeClientHandlerExc02());
}
});
ChannelFuture future = boot.connect(host, port);
// 等待客户端链路关闭
future.channel().closeFuture().sync();
} finally {
// 优雅的退出,释放NIO线程组
group.shutdownGracefully();
}
} class TimeClientHandlerExc02 extends ChannelInboundHandlerAdapter { @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client channelActive(client 连接成功)..");
for (int i = 0; i < 50; i++) {
System.out.print(i + ",");
ctx.writeAndFlush(
Unpooled.copiedBuffer("It's a good day , I want to know time--" + i + "\n", CharsetUtil.UTF_8)); // 必须有flush
}
ctx.flush();
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("client channelRead.." + ++count);
String msgStr = msg.toString();
System.out.println("读入 server 消息:" + msgStr);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} } }
查看了 LineBasedFrameDecoder 的部分源码,确实是以换行作为分割符的。
学习netty遇到的关于 LineBasedFrameDecoder 的问题的更多相关文章
- 深入学习Netty(5)——Netty是如何解决TCP粘包/拆包问题的?
前言 学习Netty避免不了要去了解TCP粘包/拆包问题,熟悉各个编解码器是如何解决TCP粘包/拆包问题的,同时需要知道TCP粘包/拆包问题是怎么产生的. 在此博文前,可以先学习了解前几篇博文: 深入 ...
- Netty学习——Netty和Protobuf的整合(二)
Netty学习——Netty和Protobuf的整合(二) 这程序是有瑕疵的,解码器那里不通用,耦合性太强,有两个很明显的问题,但是要怎么解决呢?如:再加一个内部类型 Person2,之前的代码就不能 ...
- Netty学习——Netty和Protobuf的整合(一)
Netty学习——Netty和Protobuf的整合 Protobuf作为序列化的工具,将序列化后的数据,通过Netty来进行在网络上的传输 1.将proto文件里的java包的位置修改一下,然后再执 ...
- 深入学习Netty(1)——传统BIO编程
前言 之前看过Dubbo源码,Nacos等源码都涉及到了Netty,虽然遇到的时候查查资料,后面自己也有私下学习Netty并实践,但始终没有形成良好的知识体系,Netty对想要在Java开发上不断深入 ...
- 深入学习Netty(2)——传统NIO编程
前言 学习Netty编程,避免不了从了解Java 的NIO编程开始,这样才能通过比较让我们对Netty有更深的了解,才能知道Netty大大的好处.传统的NIO编程code起来比较麻烦,甚至有遗留Bug ...
- 深入学习Netty(3)——传统AIO编程
前言 之前已经整理过了BIO.NIO两种I/O的相关博文,每一种I/O都有其特点,但相对开发而言,肯定是要又高效又简单的I/O编程才是真正需要的,在之前的NIO博文(深入学习Netty(2)--传统N ...
- 深入学习Netty(4)——Netty编程入门
前言 从学习过BIO.NIO.AIO编程之后,就能很清楚Netty编程的优势,为什么选择Netty,而不是传统的NIO编程.本片博文是Netty的一个入门级别的教程,同时结合时序图与源码分析,以便对N ...
- netty04(重点来了、指定某个客户端发信息或者群发)小声嘀咕~~我也是从零开始学得、、、想学习netty的又不知道怎么下手的童鞋们~~
还是和上几篇一样,先给出前面笔记的连接,有没看的可以去看看再来! netty01 . netty02 .netty03 看到这里.你基本上可以使用netty接受信息和根据对应的信息返回信息了 接 ...
- 【Netty4】深入学习Netty
Netty is an asynchronous event-driven network application framework for rapid development of mainta ...
随机推荐
- maven 配置文件 settings.xml pom.xml
maven涉及的配置文件一般有三个:(作用范围依次减小,优先级依次升高) 全局配置(maven安装包中):${M2_HOME}/conf/settings.xml 用户配置(该文件是拷贝全局配置而来的 ...
- 计算机CPU是怎么认识代码的?
先说一下半导体,啥叫半导体?就是介于导体和绝缘体中间的一种东西,比如二极管. 电流可以从A端流向C端,但反过来则不行.你可以把它理解成一种防止电流逆流的东西. 当C端10V,A端0V,二极管可以视 ...
- 大二逃课总结的1.2w字的计算机网络知识!扫盲!
本文是我在大二学习计算机网络期间整理, 大部分内容都来自于谢希仁老师的<计算机网络>这本书. 为了内容更容易理解,我对之前的整理进行了一波重构,并配上了一些相关的示意图便于理解. @ 目录 ...
- 【Flutter 混合开发】添加 Flutter 到 Android Activity
Flutter 混合开发系列 包含如下: 嵌入原生View-Android 嵌入原生View-iOS 与原生通信-MethodChannel 与原生通信-BasicMessageChannel 与原生 ...
- U137971 公司搬迁 - 并查集 奇偶性
题目描述 因为人员规模扩大,T公司准备搬到新的写字楼去,写字楼分为A座和B座,n名不同工号的员工x(p1,p2,p3...pn) 按照下面两个规则确定在A座或者B座进行办公:(1)如果工号为x的员工在 ...
- java的“同一”与“相等”
变量:引用(指向地址) + 值(该变量指向值所储存的那一片内存) 两个变量同一 : 判断 是否 这两个变量指向同一片内存. 两个变量相等 : 判断 是否 这两个变量的类型相同,且值相等. 注:常用的& ...
- CodeForces 578E Walking!
题意 略. 题解 好毒瘤啊,我最多就口胡第一问的样子吧. 第一问很显然(跟凤凰县探险队员一样显然),就是每次贪心选长度最大的满足条件的子序列,选不到就折返回来.所以折返的次数很明显就是选出子序列的个数 ...
- Java学习的第五十二天
1.例9.4对象数组的使用方法 public class Cjava { public static void main(String[]args) { Box b[] = {new Box(10,1 ...
- redis方法-
//链接错误注意 //1.防火墙 //2.配置文件IP绑定 $redis = new Redis(); //连接redis $redis->connect('127.0.0.1', 6379); ...
- python模块导入(包)
模块 关注公众号"轻松学编程"了解更多. 1.1. 模块的概述 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里的代码就会越来越长,越来越不容易维护. 为了编写可维 ...