分割符和定长解码
来源:互联网 发布:看板设计软件 编辑:程序博客网 时间:2024/06/05 03:39
0 概述
TCP以流的方式进行数据传输,上层的应用协议为了对消息进行区分,往往采用以下四种方式
消息的长度固定,累计读取到长度总和为定长的len的报文之后,都认为读取到了一个完整的消息。
将回车换行符作为消息的结束符,例如FTP协议,这种方式在文本协议中应用的比较广泛。
将特殊的分隔符作为消息的结束标志,回车换行符本身就是一种特殊的符号。
通过在消息头中定义长度字段来标识消息的总长度。
netty 对上面的几种做了统一 的抽象,提供了4种解码器来解决对应的问题。
1 特定分割符解码
netty 提供了DelimiterBasedFrameDecoder作为分割符解码器。
公共类
public class DelimiterConstant { //分割符 public final static String DELIMITER_STR="$*"; public final static ByteBuf DELIMITER_BUF = Unpooled.copiedBuffer(DELIMITER_STR.getBytes()); public final static Integer MAX_FRAME_LEN=1000;}
服务端实现
import io.netty.bootstrap.ServerBootstrap;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;/** * Created by hsc on 17/10/14. */public class NettyServer { private static void startServer(final int port) throws Exception { final EventLoopGroup boss = new NioEventLoopGroup(1); EventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); try { serverBootstrap.channel(NioServerSocketChannel.class) .group(boss, worker) .childOption(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //注意DelimiterBasedFrameDecoder和ServerHandler顺序 ch.pipeline().addLast(new DelimiterBasedFrameDecoder(DelimiterConstant.MAX_FRAME_LEN, DelimiterConstant.DELIMITER_BUF)); ch.pipeline().addLast(new ServerHandler()); } }); ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); //等待服务端监听端口关闭 channelFuture.channel().closeFuture().sync(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) throws Exception { startServer(8080); }}
SeverHandler 实现
public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof ByteBuf) { ByteBuf byteBuf = (ByteBuf) msg; byte[] data = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(data); System.out.println(new String(data)); String responseMsg="收到"+ DelimiterConstant.DELIMITER_STR; ByteBuf sendMsg= Unpooled.copiedBuffer(responseMsg.getBytes()); ctx.writeAndFlush(sendMsg); } }}
客户端实现
import com.hsc.study.codec.DelimiterConstant;import io.netty.bootstrap.Bootstrap;import io.netty.channel.*;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.DelimiterBasedFrameDecoder;import io.netty.handler.codec.LineBasedFrameDecoder;import io.netty.handler.codec.string.StringDecoder;/** * Created by apple on 17/10/1. */public class NettyClient { public static void main(String[] args) { startClient(); } private static void startClient() { EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); try { bootstrap.channel(NioSocketChannel.class) .group(eventLoopGroup) .option(ChannelOption.SO_KEEPALIVE, true) .handler(new ChannelInitializer<NioSocketChannel>() { protected void initChannel(NioSocketChannel ch) throws Exception { ch.pipeline().addLast(new DelimiterBasedFrameDecoder(DelimiterConstant.MAX_FRAME_LEN, DelimiterConstant.DELIMITER_BUF)); ch.pipeline().addLast(new ClientHandler()); } }); //发起异步连接操作 ChannelFuture future= bootstrap.connect("127.0.0.1", 8080).sync(); //等待客户端链路关闭 future.channel().closeFuture().sync(); } catch (Exception ex) { ex.printStackTrace(); } finally { eventLoopGroup.shutdownGracefully(); } }}
ClientHandler 实现
import com.hsc.study.codec.DelimiterConstant;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;/** * Created by apple on 17/10/1. */public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("客户端开始读数据"); ByteBuf buf = (ByteBuf) msg; byte[] data = new byte[buf.readableBytes()]; buf.readBytes(data); System.out.println(new String(data)); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //向服务端发送100 个hello world String message = "hello world" + DelimiterConstant.DELIMITER_STR; for (int i = 0; i < 10; i++) { ByteBuf buf = Unpooled.copiedBuffer(message.getBytes()); ctx.writeAndFlush(buf); } }}
不难发现当我们 DelimiterBasedFrameDecoder maxFrameLength设置比较小时候比如为8,也就是说在八字节无法读取到分割符,netty就会抛如下异常。
io.netty.handler.codec.TooLongFrameException: frame length exceeds 8: 11 - discarded at io.netty.handler.codec.DelimiterBasedFrameDecoder.fail(DelimiterBasedFrameDecoder.java:300) at io.netty.handler.codec.DelimiterBasedFrameDecoder.decode(DelimiterBasedFrameDecoder.java:266) at io.netty.handler.codec.DelimiterBasedFrameDecoder.decode(DelimiterBasedFrameDecoder.java:216) at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1359) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:935) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:645) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:580) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:497) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:459) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138) at java.lang.Thread.run(Thread.java:748)
2 定长解码
netty 中提供了FixedLengthFrameDecoder 作为定长解码器,使用方式和前面类似,这里就不在赘述。
ch.pipeline().addLast(new FixedLengthFrameDecoder(1024));
参考文献
[1] Netty 权威指南(第二版),李林峰著
阅读全文
0 0
- 分割符和定长解码
- netty分割符和定长解码器的应用
- 定长分割字符串
- netty,分割符编解码
- 指针和定长数组
- Netty的分隔符和定长解码器应用
- Netty权威指南之分隔符和定长解码器
- netty之分隔符和定长解码器解决之道
- Netty (四) 分隔符和定长解码器的使用
- [netty]--分隔符解码器DelimiterBasedFrameDecoder和定长解码器FixedLengthFrameDecoder
- 七、分隔符和定长解码器的应用(1)
- 八、分隔符和定长解码器的应用(2)
- Netty中分隔符和定长解码器的应用
- 分隔符和定长解码器在netty中的应用
- Netty分隔符解码器和定长解码器的应用方案
- 《netty权威指南》5分隔符和定长解码器的应用
- Netty学习之旅(三)(分隔符和定长解码器)
- Netty权威指南 第2版学习笔记5——分隔符和定长解码器的应用
- LINUX 下监听文件过多出现报错Error:watch ENOSPC
- 1003. 我要通过!(20)
- laravel
- 三段式状态机的写法总结
- java练习题4
- 分割符和定长解码
- SPOJ10606 BALNUM
- Android7.0的systemUI的一些文章链接
- Android移动开发-Android数据加密与解密的实现
- 进程间通讯-无名管道
- NGUI与UGUI的区别及其优缺点
- Recyclerview小项目总结:加载妹子图片
- 从零开始学爬虫(一)
- Python常用运算符