Netty 系列六(编解码器).
一、概念
网络传输的单位是字节,如何将应用程序的数据转换为字节,以及将字节转换为应用程序的数据,就要说到到我们该篇介绍的编码器和解码器。
将应用程序的数据转换为网络格式,以及将网络格式转换为应用程序的数据的组件分别叫作编码器和解码器,同时具有这两种功能的单一组件叫作编解码器。 Netty 提供了一系列用来创建所有这些编码器、解码器以及编解码器的工具,还可以按需定制通用的消息转换编解码器。
Netty 的编(解)码器实现了 ChannelHandlerAdapter,也是一种特殊的 ChannelHandler,所以依赖于 ChannelPipeline,可以将多个编(解)码器链接在一起,以实现复杂的转换逻辑。
对于编码器和解码器来说,其过程也是相当的简单:一旦消息被编码或者解码,它就会被 ReferenceCountUtil.release(message)调用自动释放。如果你需要保留引用以便稍后使用,那么你可以调用 ReferenceCountUtil.retain(message)方法。这将会增加该引用计数,从而防止该消息被释放。
二、编码器
编码器将应用程序的数据转换为网络格式,出站消息时被调用,主要有两类:
1、将消息编码为字节:MessageToByteEncoder
public abstract class MessageToByteEncoder<I> extends ChannelHandlerAdapter{}
public class ShortToByteEncoder extends MessageToByteEncoder<Short> {
/**
* 1、类型为 I 的出站消息被编码为 ByteBuf
* 2、该 ByteBuf 随后将会被转发给 ChannelPipeline中的下一个 ChannelOutboundHandler。
*/
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Short aShort, ByteBuf byteBuf) throws Exception {
byteBuf.writeShort(aShort);
}
}
2、将消息编码为消息:MessageToMessageEncoder
public abstract class MessageToMessageEncoder<I> extends ChannelHandlerAdapter{}
public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer> {
/**
* 1、类型为 I 的出站消息被编码为目标类型 存入List 中
* 2、该 List 随后将会被转发给 ChannelPipeline中的下一个 ChannelOutboundHandler。
*/
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
out.add(String.valueOf(msg));
}
}
三、解码器
解码器将网络格式转换为应用程序的数据,入站消息时被调用。
解码器比编码器多了一个 decodeLast 方法,原因是解码器通常需要在 Channel 关闭之后产生最后一个消息。这显然不适用于编码器的场景 —— 在连接被关闭之后仍然产生一个消息是毫无意义的。所以,当 Channel 的状态变为非活动时,这个方法将会被调用一次。可以重写该方法以提供特殊的处理。
解码器主要有两类:
1、将字节解码为消息:ByteToMessageDecoder 和 ReplayingDecoder
public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {}
public class MyDecoder extends ByteToMessageDecoder {
private static final int MAX_FRAME_SIZE = 1024;
/**
* 1、该方法被调用时,将会传入一个包含了传入数据的 ByteBuf,以及一个用来添加解码消息的 List.
* 2、对该方法的调用将会重复进行,直到确定没有新的元素被添加到该 List,或者Butebuf 没有更多可读取的字节为止。
* 3、List 的内容将会被传递给 ChannelPipeline 中的下一个 ChannelInboundHandler。
*/
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
int readableBytes = byteBuf.readableBytes();
//不能让解码器缓冲大量的数据以致于耗尽可用的内存
if (readableBytes > MAX_FRAME_SIZE){
//跳过所有的可读字节
byteBuf.skipBytes(readableBytes);
throw new TooLongFrameException("数据超过可缓存字节...");
}
//假设需要解析 int 类型的消息(int 4个字节)
if (readableBytes > 4){
list.add(byteBuf.readInt());
}
}
}
----分割线----
//类型参数 S 指定了用于状态管理的类型,其中 Void 代表不需要状态管理。
public abstract class ReplayingDecoder<S> extends ByteToMessageDecoder{}
public class MyReplayingDecoder extends ReplayingDecoder<Void> {
/**
* 1、ReplayingDecoder 扩展了 ByteToMessageDecoder,并且自定义了 ByteBuf 的实现 ReplayingDecoderByteBuf。
* 2、ReplayingDecoderByteBuf 对要转换的消息的字节数进行内部管理,如果没有足够的字节使用,将会抛出一个 Signal,由ReplayingDecoder进行处理。
*
* @param byteBuf 传入的 ByteBuf 实际上是 ReplayingDecoderByteBuf*/
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
list.add(byteBuf.readInt());
}
}
继承 ByteToMessageDecoder 和继承 ReplayingDecoder 有什么区别呢?ReplayingDecoder 唯一的好处就是解码的时候不用进行字节数的判断,如上 ,因为它交由自定义的 ReplayingDecoderByteBuf 去处理了。但是 ReplayingDecoder 的效率稍慢于ByteToMessageDecoder。一般我们在这两个解码器中进行抉择的准则是:如果使用 ByteToMessageDecoder 不会引入太多的复杂性,那么请使用它;否则,请使用 ReplayingDecoder!
2、将消息解码为消息:MessageToMessageDecoder
public abstract class MessageToMessageEncoder<I> extends ChannelHandlerAdapter{}
public class IntegerToStringDecoder extends MessageToMessageEncoder<Integer> {
/**
* 1、对于每个需要被解码为另一种格式的入站消息来说,该方法将会被调用。
* 2、解码消息随后会被传递给 ChannelPipeline 中的下一个 ChannelInboundHandler
*/
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Integer integer, List<Object> list) throws Exception {
list.add(String.valueOf(integer));
}
}
四、编解码器
Netty 的抽象编解码器类捆绑一个解码器/编码器对,主要用于在同一个类中管理入站和出站数据和消息的转换。
个人觉得这个编解码器略显鸡肋呀,还是喜欢将编码器和解码器分开来写。因为 Netty 设计的一个基本准则就是:尽可能地将两种功能(编码器、解码器)分开,最大化代码的可重用性和可扩展性。
编解码器也主要有两类:
1、字节消息编解码器:ByteToMessageCodec
public abstract class ByteToMessageCodec<I> extends ChannelHandlerAdapter {}
public class MyBytetoMessageCodec extends ByteToMessageCodec<Short> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
while (in.readableBytes() > 4){
out.add(in.readInt());
}
}
@Override
protected void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out) throws Exception {
out.writeShort(msg);
}
}
2、消息转换编解码器:MessageToMessageCodec
public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelHandlerAdapter {}
public class WebSocketConvertHandler extends MessageToMessageCodec<WebSocketFrame, WebSocketConvertHandler.MyWebSocketFrame> {
/**
* 1、对于每个 OUTBOUND_IN 类型的消息,这个方法都会被调用。
* 2、这个消息将会被编码为 INBOUND_IN 类型的消息。
* 3、然后被转发给 ChannelPipeline 中的下一个 ChannelOutboundHandler
*/
@Override
protected void encode(ChannelHandlerContext ctx, MyWebSocketFrame msg, List<Object> out) throws Exception {
ByteBuf byteBuf = msg.getByteBuf().duplicate().retain();
switch (msg.getFrameType()) {
case BINARY:
out.add(new BinaryWebSocketFrame(byteBuf));
break;
case TEXT:
out.add(new TextWebSocketFrame(byteBuf));
break;
case CLOSE:
out.add(new CloseWebSocketFrame(true, 0, byteBuf));
break;
case CONTINUATION:
out.add(new ContinuationWebSocketFrame(byteBuf));
break;
case PONG:
out.add(new PongWebSocketFrame(byteBuf));
break;
case PING:
out.add(new PingWebSocketFrame(byteBuf));
default:
break;
}
}
/**
* 1、传入 INBOUND_IN 类型的消息,该方法会被调用。
* 2、这个消息会被解码为 OUTBOUND_IN 类型的消息。
* 3、然后被转发给 ChannelPipeline 中的下一个 ChannelInboundHandler
*/
@Override
protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
ByteBuf byteBuf = msg.content().duplicate().retain();
if (msg instanceof BinaryWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.BINARY, byteBuf));
} else if (msg instanceof CloseWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.CLOSE, byteBuf));
} else if (msg instanceof TextWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.TEXT, byteBuf));
} else if (msg instanceof PingWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.PING, byteBuf));
} else if (msg instanceof PongWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.PONG, byteBuf));
} else if (msg instanceof ContinuationWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.CONTINUATION, byteBuf));
} else {
throw new IllegalStateException("Unsupported websocket msg " + msg);
}
}
public static final class MyWebSocketFrame {
public enum FrameType {
BINARY,
CLOSE,
PING,
PONG,
TEXT,
CONTINUATION
}
private final FrameType frameType;
private final ByteBuf byteBuf;
public MyWebSocketFrame(FrameType frameType, ByteBuf byteBuf) {
this.frameType = frameType;
this.byteBuf = byteBuf;
}
public FrameType getFrameType() {
return frameType;
}
public ByteBuf getByteBuf() {
return byteBuf;
}
}
}
这个类写的还是很棒的~~
参考资料:《Netty IN ACTION》
演示源代码:https://github.com/JMCuixy/NettyDemo
Netty 系列六(编解码器).的更多相关文章
- Netty4.x中文教程系列(六) 从头开始Bootstrap
Netty4.x中文教程系列(六) 从头开始Bootstrap 其实自从中文教程系列(五)一直不知道自己到底想些什么.加上忙着工作上出现了一些问题.本来想就这么放弃维护了.没想到有朋友和我说百度搜索推 ...
- Netty4.x中文教程系列(五)编解码器Codec
Netty4.x中文教程系列(五)编解码器Codec 上一篇文章详细解释了ChannelHandler的相关构架设计,版本和设计逻辑变更等等. 这篇文章主要在于讲述Handler里面的Codec,也就 ...
- 6. 彤哥说netty系列之Java NIO核心组件之Buffer
--日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第六篇. 简介 上一章我们一起学习了Java NIO的核心组件Channel,它可以看作是实体与实体之间的连接,而且需要与Buffer交 ...
- Netty系列之源码解析(一)
本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 当前:Netty 源码解析(一)开始 Netty 源码解析(二): Netty 的 Channel ...
- CSS 魔法系列:纯 CSS 绘制各种图形《系列六》
我们的网页因为 CSS 而呈现千变万化的风格.这一看似简单的样式语言在使用中非常灵活,只要你发挥创意就能实现很多比人想象不到的效果.特别是随着 CSS3 的广泛使用,更多新奇的 CSS 作品涌现出来. ...
- WCF编程系列(六)以编程方式配置终结点
WCF编程系列(六)以编程方式配置终结点 示例一中我们的宿主程序非常简单:只是简单的实例化了一个ServiceHost对象,然后调用open方法来启动服务.而关于终结点的配置我们都是通过配置文件来 ...
- SQL Server 2008空间数据应用系列六:基于SQLCRL的空间数据可编程性
原文:SQL Server 2008空间数据应用系列六:基于SQLCRL的空间数据可编程性 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2008 ...
- R语言数据分析系列六
R语言数据分析系列六 -- by comaple.zhang 上一节讲了R语言作图,本节来讲讲当你拿到一个数据集的时候怎样下手分析,数据分析的第一步.探索性数据分析. 统计量,即统计学里面关注的数据集 ...
- java基础解析系列(六)---深入注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...
随机推荐
- Windows 10 IoT Core 17127 for Insider 版本更新
昨天,微软发布了Windows 10 IoT Core 17127 for Insider 版本更新,本次更新只修正了一些Bug,没有发布新的特性.相比于17120,修复了一个已知的问题. 一些已知的 ...
- 谈谈npm依赖管理
引言 现在的前端开发几乎都离不开nodejs的包管理器npm,比如前端在搭建本地开发服务以及打包编译前端代码等都会用到.在前端开发过程中,经常用到npm install来安装所需的依赖,至于其中的技术 ...
- [Swift]Alamofire:设置网络请求超时时间【timeout】的两种方式
两种方式作用相同,是同一套代码的两种表述. 第一种方式:集聚. 直接设置成员属性(全局属性),这种方法不能灵活修改网络请求超时时间timeout. 声明为成员属性: // MARK: - 设置为全局变 ...
- wav转txt格式的代码实现(c,python)
平时经常做音频算法,经常用得到wav转txt的转换,这里就做个备忘,自己写了一些小代码来实现这个目标: 第一个是c代码的实现: #include <stdio.h> #include &l ...
- shell 中let无法使用的原因
运行 sh let.sh 时,却显示 let: not found 百度之后知道: /bin/sh指向了dash而不是bash,dash不支持let命令. 解决方法: 法1.使用 bash ...
- Java 中的 String 真的是不可变吗?
我们都知道 Java 中的 String 类的设计是不可变的,来看下 String 类的源码. public final class String implements java.io.Seriali ...
- iOS学习——(转)iOS中关于通知的使用
在移动端开打过程中,经常会用到通知和推送,例如有短信来了需要通知提示,手机横屏了需要通知提示,插上耳机了需要通知提示等等,我们可以根据这些通知采取对应的动作.iOS系统自身定义了很对通知,但是在开发过 ...
- hbase之createTable完整的netty实现执行流程
hbase的客户端代码并不想hive一样用java编写,shell调用,而是使用ruby编写. 在admin.rb文件中方法create,其中接受两个参数,其中第二个参数类型为变长参数. 而在crea ...
- shiro 返回json字符串 + 自定义filter
前言: 在前后端分离的项目中, 在使用shiro的时候, 我们绝大部分时候, 并不想让浏览器跳转到那个页面去, 而是告诉前端, 你没有登录, 或者没有访问权限. 那这时候, 我们就需要返回json字符 ...
- 2.Magicodes.NET框架之路——策略管理
闲话策略 策略,有很多解释.但鄙人个人比较看重这点: 策略,是为了实现某个目标或者针对某些问题而制定的应对方案,以最终实现目标.比如为实现生娃而XXOO. 因此在本框架中,策略(Strategy),则 ...