目录

DelimiterBasedFrameDecoder

服务器

客户端

运行结果


DelimiterBasedFrameDecoder

DelimiterBasedFrameDecoder 分隔符解码器,使用自定义的分隔符作为码流结束标识的消息的解码。

本文以经典的 Echo 服务为例,EchoServer 接收到 EchoClient 的请求后,将其打印出来,然后将原始消息返回给客户端,消息以 “$_” 作为分隔符。

本文代码仍然在 《 LineBasedFrameDecoder 行解码器,回车换行符解决 TCP 粘包》基础上进行修改。

服务器

EchoServer 内容如下:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
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.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/*** Created by Administrator on 2018/11/11 0011.* Echo 服务器*/
public class EchoServer {public static void main(String[] args) {int port = 9898;new EchoServer().bind(port);}public void bind(int port) {/*** interface EventLoopGroup extends EventExecutorGroup extends ScheduledExecutorService extends ExecutorService* 配置服务端的 NIO 线程池,用于网络事件处理,实质上他们就是 Reactor 线程组* bossGroup 用于服务端接受客户端连接,workerGroup 用于进行 SocketChannel 网络读写*/EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {/** ServerBootstrap 是 Netty 用于启动 NIO 服务端的辅助启动类,用于降低开发难度* */final ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {System.out.println(Thread.currentThread().getName() + ",服务器初始化通道...");/*** 创建分隔符缓冲对象 ByteBuf,使用自定义的 "$_" 作为消息结束符,自己也可以定义为其它的字符作为结束符** DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter)* DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters)* 分隔符解码器重载了好几个构造器方法,其中常用的就是上面这两个*      maxFrameLength:单条消息的最大长度,当达到该长度后仍然没有查找到分隔符时,则抛出 TooLongFrameException 异常*      防止由于异常码流缺失分隔符导致内存溢出(亲测 Netty 4.1 版本,服务器并未抛出异常,而是客户端被强制断开连接了)*      delimiter:分隔符缓冲对象,第二个构造器可见可以指定多个结束符*/ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new EchoServerHandler());}});/**服务器启动辅助类配置完成后,调用 bind 方法绑定监听端口,调用 sync 方法同步等待绑定操作完成*/ChannelFuture f = b.bind(port).sync();System.out.println(Thread.currentThread().getName() + ",服务器开始监听端口,等待客户端连接.........");/**下面会进行阻塞,等待服务器连接关闭之后 main 方法退出,程序结束* */f.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {/**优雅退出,释放线程池资源*/bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

重点在第 57-59行,要点在注释中已经说明。

EchoServerHandler 内容如下:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.concurrent.atomic.AtomicInteger;
/*** Created by Administrator on 2017/5/16.* ChannelInboundHandlerAdapter extends ChannelHandlerAdapter 用于对网络事件进行读写操作*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {/*** 因为多线程,所以使用原子操作类来进行计数*/private static AtomicInteger atomicInteger = new AtomicInteger();/*** 收到客户端消息,自动触发** @param ctx* @param msg* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {/*** DelimiterBasedFrameDecoder 自动对发送来的消息进行了解码,后续的 ChannelHandler 接收到的 msg 对象是完整的消息包* 第二个 ChannelHandler 是 StringDecoder,它将 ByteBuf 解码成字符串对象* 第三个 EchoServerHandler 接收到的 msg 对象就是解码后的字符串对象*/String body = (String) msg;System.out.println((atomicInteger.addAndGet(1)) + "--->" + Thread.currentThread().getName() + ",The server receive  order : " + body);/**回复消息* 由于创建的 DelimiterBasedFrameDecoder 解码器默认会自动去掉分隔符,所以返回给客户端时需要自己拼接分隔符* DelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, ByteBuf delimiter)*  这个构造器可以设置是否去除分隔符* 最后创建 ByteBuf 将原始的消息重新返回给客户端* */String respMsg = body + "$_";ByteBuf respByteBuf = Unpooled.copiedBuffer(respMsg.getBytes());/*** 每次写的时候,同时刷新,防止 TCP 粘包*/ctx.writeAndFlush(respByteBuf);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {System.out.println("-----客户端关闭:" + ctx.channel().remoteAddress());/**当发生异常时,关闭 ChannelHandlerContext,释放和它相关联的句柄等资源 */ctx.close();}
}

客户端

EchoClient 内容如下:

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
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.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/*** Created by Administrator on 2017/5/16.* Echo 客户端*/
public class EchoClient {/*** 使用 2 个线程模拟 2 个客户端** @param args*/public static void main(String[] args) {for (int i = 0; i < 2; i++) {new Thread(new MyThread()).start();}}static class MyThread implements Runnable {@Overridepublic void run() {connect("192.168.1.20", 9898);}public void connect(String host, int port) {/**配置客户端 NIO 线程组/池*/EventLoopGroup group = new NioEventLoopGroup();try {/**Bootstrap 与 ServerBootstrap 都继承(extends)于 AbstractBootstrap* 创建客户端辅助启动类,并对其配置,与服务器稍微不同,这里的 Channel 设置为 NioSocketChannel* 然后为其添加 Handler,这里直接使用匿名内部类,实现 initChannel 方法* 作用是当创建 NioSocketChannel 成功后,在进行初始化时,将它的ChannelHandler设置到ChannelPipeline中,用于处理网络I/O事件*/Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {/*** 创建分隔符缓冲对象 ByteBuf,使用自定义的 "$_" 作为消息结束符,自己也可以定义为其它的字符作为结束符** DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter)* DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters)* 分隔符解码器重载了好几个构造器方法,其中常用的就是上面这两个*      maxFrameLength:单条消息的最大长度,当达到该长度后仍然没有查找到分隔符时,则抛出 TooLongFrameException 异常*      防止由于异常码流缺失分隔符导致内存溢出(亲测 Netty 4.1 版本,服务器并未抛出异常,而是客户端被强制断开连接了)*      delimiter:分隔符缓冲对象,第二个构造器可见可以指定多个结束符*/System.out.println(Thread.currentThread().getName() + ",客户端初始化管道...");ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new EchoClientHandler());}});/**connect:发起异步连接操作,调用同步方法 sync 等待连接成功*/ChannelFuture channelFuture = b.connect(host, port).sync();System.out.println(Thread.currentThread().getName() + ",客户端发起异步连接..........");/**等待客户端链路关闭*/channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {/**优雅退出,释放NIO线程组*/group.shutdownGracefully();}}}
}

和服务器一样,重点也是在第 65-67 行设置解码器。

EchoClientHandler 内容如下:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.concurrent.atomic.AtomicInteger;
/*** Created by Administrator on 2017/5/17.* 用于对网络事件进行读写操作*/
public class EchoClientHandler extends ChannelInboundHandlerAdapter {/*** 因为 Netty 采用线程池,所以这里使用原子操作类来进行计数*/private static AtomicInteger atomicInteger = new AtomicInteger();/*** 当客户端和服务端 TCP 链路建立成功之后,Netty 的 NIO 线程会调用 channelActive 方法*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {/*** 连续发送 10 条数据*/for (int i = 0; i < 10; i++) {/** DelimiterBasedFrameDecoder 解码器自定义的分隔符是 "$_",所以发送消息时要在结尾处加上*/String reqMsg = (i + 1) + "-我是客户端 " + Thread.currentThread().getName() + "$_";byte[] reqMsgByte = reqMsg.getBytes("UTF-8");ByteBuf reqByteBuf = Unpooled.buffer(reqMsgByte.length);/** writeBytes:将指定的源数组的数据传输到缓冲区* 调用 ChannelHandlerContext 的 writeAndFlush 方法将消息发送给服务器*/reqByteBuf.writeBytes(reqMsgByte);/** 每次发送的同时进行刷新*/ctx.writeAndFlush(reqByteBuf);}}/*** 当服务端返回应答消息时,channelRead 方法被调用,从 Netty 的 ByteBuf 中读取并打印应答消息*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {/**与服务器同理* 这个 msg 已经是解码成功的消息,所以不再需要像以前一样使用 ByteBuf 进行编码* 直接转为 string 字符串即可*/String body = (String) msg;System.out.println((atomicInteger.addAndGet(1)) + "---" + Thread.currentThread().getName() + ",Server return Message:" + body);}/*** 当发生异常时,打印异常 日志,释放客户端资源*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {/**释放资源*/ctx.close();}
}

运行结果

先运行服务器,在运行客户端。

客户端控制台输出如下:

nioEventLoopGroup-2-1,客户端初始化管道...
nioEventLoopGroup-3-1,客户端初始化管道...
Thread-0,客户端发起异步连接..........
Thread-1,客户端发起异步连接..........
1---nioEventLoopGroup-2-1,Server return Message:1-我是客户端 nioEventLoopGroup-2-1
2---nioEventLoopGroup-2-1,Server return Message:2-我是客户端 nioEventLoopGroup-2-1
3---nioEventLoopGroup-3-1,Server return Message:1-我是客户端 nioEventLoopGroup-3-1
4---nioEventLoopGroup-2-1,Server return Message:3-我是客户端 nioEventLoopGroup-2-1
5---nioEventLoopGroup-2-1,Server return Message:4-我是客户端 nioEventLoopGroup-2-1
6---nioEventLoopGroup-2-1,Server return Message:5-我是客户端 nioEventLoopGroup-2-1
7---nioEventLoopGroup-3-1,Server return Message:2-我是客户端 nioEventLoopGroup-3-1
8---nioEventLoopGroup-2-1,Server return Message:6-我是客户端 nioEventLoopGroup-2-1
9---nioEventLoopGroup-2-1,Server return Message:7-我是客户端 nioEventLoopGroup-2-1
10---nioEventLoopGroup-3-1,Server return Message:3-我是客户端 nioEventLoopGroup-3-1
11---nioEventLoopGroup-2-1,Server return Message:8-我是客户端 nioEventLoopGroup-2-1
12---nioEventLoopGroup-2-1,Server return Message:9-我是客户端 nioEventLoopGroup-2-1
13---nioEventLoopGroup-3-1,Server return Message:4-我是客户端 nioEventLoopGroup-3-1
14---nioEventLoopGroup-3-1,Server return Message:5-我是客户端 nioEventLoopGroup-3-1
15---nioEventLoopGroup-3-1,Server return Message:6-我是客户端 nioEventLoopGroup-3-1
16---nioEventLoopGroup-3-1,Server return Message:7-我是客户端 nioEventLoopGroup-3-1
17---nioEventLoopGroup-3-1,Server return Message:8-我是客户端 nioEventLoopGroup-3-1
18---nioEventLoopGroup-2-1,Server return Message:10-我是客户端 nioEventLoopGroup-2-1
19---nioEventLoopGroup-3-1,Server return Message:9-我是客户端 nioEventLoopGroup-3-1
20---nioEventLoopGroup-3-1,Server return Message:10-我是客户端 nioEventLoopGroup-3-1
服务器控制台输出如下:

main,服务器开始监听端口,等待客户端连接.........
nioEventLoopGroup-3-2,服务器初始化通道...
nioEventLoopGroup-3-1,服务器初始化通道...
1--->nioEventLoopGroup-3-1,The server receive  order : 1-我是客户端 nioEventLoopGroup-2-1
2--->nioEventLoopGroup-3-2,The server receive  order : 1-我是客户端 nioEventLoopGroup-3-1
3--->nioEventLoopGroup-3-1,The server receive  order : 2-我是客户端 nioEventLoopGroup-2-1
4--->nioEventLoopGroup-3-1,The server receive  order : 3-我是客户端 nioEventLoopGroup-2-1
5--->nioEventLoopGroup-3-1,The server receive  order : 4-我是客户端 nioEventLoopGroup-2-1
6--->nioEventLoopGroup-3-1,The server receive  order : 5-我是客户端 nioEventLoopGroup-2-1
7--->nioEventLoopGroup-3-2,The server receive  order : 2-我是客户端 nioEventLoopGroup-3-1
8--->nioEventLoopGroup-3-1,The server receive  order : 6-我是客户端 nioEventLoopGroup-2-1
9--->nioEventLoopGroup-3-1,The server receive  order : 7-我是客户端 nioEventLoopGroup-2-1
10--->nioEventLoopGroup-3-1,The server receive  order : 8-我是客户端 nioEventLoopGroup-2-1
11--->nioEventLoopGroup-3-2,The server receive  order : 3-我是客户端 nioEventLoopGroup-3-1
12--->nioEventLoopGroup-3-1,The server receive  order : 9-我是客户端 nioEventLoopGroup-2-1
13--->nioEventLoopGroup-3-2,The server receive  order : 4-我是客户端 nioEventLoopGroup-3-1
14--->nioEventLoopGroup-3-2,The server receive  order : 5-我是客户端 nioEventLoopGroup-3-1
15--->nioEventLoopGroup-3-2,The server receive  order : 6-我是客户端 nioEventLoopGroup-3-1
16--->nioEventLoopGroup-3-2,The server receive  order : 7-我是客户端 nioEventLoopGroup-3-1
17--->nioEventLoopGroup-3-2,The server receive  order : 8-我是客户端 nioEventLoopGroup-3-1
18--->nioEventLoopGroup-3-1,The server receive  order : 10-我是客户端 nioEventLoopGroup-2-1
19--->nioEventLoopGroup-3-2,The server receive  order : 9-我是客户端 nioEventLoopGroup-3-1
20--->nioEventLoopGroup-3-2,The server receive  order : 10-我是客户端 nioEventLoopGroup-3-1

DelimiterBasedFrameDecoder 自定义分隔符解码器,解决 TCP 粘包相关推荐

  1. netty解决TCP粘包/拆包导致的半包读写问题的三种方案

    解决方案一:LineBasedFrameDecoder+StringDecoder来解决TCP的粘包/拆包问题 只需要在客户端和服务端加上45.46两行代码并且在发送消息的时候加上换行符即可解决TCP ...

  2. golang解决TCP粘包问题

    6行代码解决golang TCP粘包 转自:https://studygolang.com/articles/12483 什么是TCP粘包问题以及为什么会产生TCP粘包,本文不加讨论.本文使用gola ...

  3. Linux socket编程(一):客户端服务端通信、解决TCP粘包

    一.服务端程序 服务端程序工作流程: 创建socket →\rightarrow→ 绑定监听的IP地址和端口 →\rightarrow→ 监听客户端连接 →\rightarrow→ 接受/发送数据.对 ...

  4. Netty解决TCP粘包/拆包导致的半包读写问题

    一.TCP粘包/拆包问题说明 TCP是个"流"协议,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包拆分,所以在业务上认为,一 ...

  5. c#解决TCP“粘包”问题

    c#解决TCP"粘包"问题 参考文章: (1)c#解决TCP"粘包"问题 (2)https://www.cnblogs.com/wangjun8868/p/71 ...

  6. 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)

    一.粘包/拆包概念 TCP是一个"流"协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的 ...

  7. golang 解决 TCP 粘包问题

    什么是 TCP 粘包问题以及为什么会产生 TCP 粘包,本文不加讨论.本文使用 golang 的 bufio.Scanner 来实现自定义协议解包. 协议数据包定义 本文模拟一个日志服务器,该服务器接 ...

  8. swoole 解决tcp粘包问题

    Tcp粘包问题 tcp在发送数据的时候因为存在数据缓存的关系,对于数据在发送的时候在 短时间内 如果连续发送很多小的数据的时候就会有可能一次性一起发送,还有就是对于大的数据就会分开连续发送多次 Tcp ...

  9. Golang解决TCP粘包拆包问题

    协议定义 报文长度(4字节) 报文内容[]byte 服务端代码 package mainimport ("encoding/binary""fmt"" ...

  10. 【Netty】TCP粘包和拆包

    一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...

最新文章

  1. 2020牛客多校第3场:Two Matchings[找规律+dp]
  2. SAP 如何从表中区分内外向交货单
  3. cocos2d_x 常用函数解析
  4. 聚集索引和非聚集索引实例
  5. vue模拟加载更多功能(数据追加)
  6. php 类定义抽象方法吗,PHP面向对象:接口与抽象方法
  7. 度量计算机外部传输单位,用来度量计算机外部设备传输率的是什么度量单位?...
  8. 我身边的计算机网,电脑在我身边作文700字
  9. “3D几何与视觉技术”全球在线研讨会第六期第七期
  10. zabbix监控业务进程变动
  11. hashmap 线程不安全
  12. 《爱你就像爱生命》你好哇,陌生人
  13. HashMap底层数据结构
  14. 惠普HP CQ40 519TX XP系统安装以及XP驱动
  15. 在VS中一个项目下两个以上源文件怎么通过编译
  16. 又是一年叶落时(二)
  17. Dell H310配置no-raid直通模式
  18. 服务器相关 HTTP 请求错误
  19. Android软件架构
  20. Andersen Global在莫桑比克扩展业务

热门文章

  1. ps3 安装linux,PlayStation 3上安装Ubuntu Linux[图文]
  2. 拓端tecdat|matlab使用样条插值重采样估计INR数据研究
  3. 拓端tecdat|用R语言实现神经网络预测股票实例
  4. 拓端tecdat|matlab对MCMC贝叶斯方法用于加筋复合板的冲击载荷识别
  5. 拓端tecdat|用TensorFlow实现MNIST
  6. 拓端tecdat|R语言进行数值模拟:模拟泊松回归模型的数据
  7. Tensorflow训练简单神经网络
  8. python装饰器用法
  9. 人脸检测-MTCNN算法笔记和代码解读
  10. DeepFake技术--实际操作