Netty4实战第八章:Netty提供的ChannelHandler和编解码器
来源:互联网 发布:油性皮肤洗面奶知乎 编辑:程序博客网 时间:2024/06/04 23:31
本章主要内容
- SSL/TLS加密Netty应用
- 构建HTTP/HTTPS应用
- 处理空闲连接和超时问题
- 解码分隔符和以长度为基础的协议
- 写大量数据
- 序列化大量数据
Netty提供了很多常用协议(例如HTTP)的实现,所以开发者不需要重复造这些轮子。这一章我们会学习如何使用SSL/TLS加密Netty应用,怎么样编写可扩展的HTTP服务器,怎么使用联合协议如WebSockets或谷歌的SPDY构建高性能服务端。这些都是很常见,很多应用中都必须用的组件。本章还会介绍一些压缩知识,当数据比较大时,这个也是很影响应用性能的。
一、使用SSL/TLS加密Netty应用
网络传输数据默认情况下是不安全的,传输的数据一般都是纯文本或是很容易被解码的二进制数据。当需要传输一些私密的数据的时候就可能会出现问题,毕竟黑客不是吃干饭的。
加密算法的出现就是解决这种问题的。TLS和SSL是比较知名的应用在网络应用层协议上的加密协议。例如我们常用的HTTPS和SMTPS就是使用它们加密的。因为TLS和SSL经常是组合到应用层协议之上的,所以应用开发者很少注意到它们,但实际上很多地方都已经使用了它们。
对于SSL/TLS,Java也提供了抽象层,就是SslContext和SslEngine。实际上,SslContext可以用来获取一个SslEngine,SslEngine可以用来加密和解密。它的高度可配置性用于支持指定的密码或其他的,不过这不是本书将知识点,感兴趣的同学可以下面去查查相关资料。
Netty继承了Java的SSL引擎并增加很多功能以更适应基于Netty的应用。Netty提供了一个名字叫SslHandler的ChannelHandler,它包装了一个SslEngine用来加密和解密网络数据。下面展示了SslHandler主要逻辑。
下面的代码展示了如何使用ChannelInitializer将SslHandler加入到ChannelPipeline中。
public class SslChannelInitializer extends ChannelInitializer<Channel> { private final SSLContext context; private final boolean client; private final boolean startTls; public SslChannelInitializer(SSLContext context, boolean client, boolean startTls) { this.context = context; this.client = client; this.startTls = startTls; } @Override protected void initChannel(Channel ch) throws Exception { SSLEngine engine = context.createSSLEngine(); engine.setUseClientMode(client); ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls)); } }
有一点很重要,几乎所有的场景中SslHandler在ChannelPipeline中的位置都是第一个。可能会有一些其他情况,不过一般情况都是这个规则。前面的ChannelHandlers章节我们已经学习过了,ChannelPipeline收到数据时处理流程类似一个LIFO(后进先出)队列,发送数据时处理流程类似FIFO(先进先出)队列。所以把SslHandler放到第一位,可以保证其他ChannelHandler在数据加密之前进行处理,而数据通过网络传输时又是已经加密的。
SslHandler有很多有用的方法,如下表。可以通过这些方法修改它的行为或者获取SSL/TLS握手完成的通知,握手就是双方互相验证并选择双方支持的加密密码。SSL/TLS的握手会自动执行。
名称
描述
setHandshakeTimeout(...)
setHandshakeTimeoutMillis(...)
getHandshakeTimeoutMillis()
设置/获取握手超时时间
setCloseNotifyTimeout(...)
setCloseNotifyTimeoutMillis(...)
getCloseNotifyTimeoutMillis()
设置/获取关闭通知超时时间
handshakeFuture(...)
获取一个握手的Future,一旦握手完成就会获得通知
close(...)
发送close_notify到指定Channel,不过这个方法已经过时,
官方建议用Channel的close方法或ChannelHandlerContext的close方法
二、构建Netty的HTTP/HTTPS应用
无论是PC机还是现在的移动端,HTTP/HTTPS都是非常成功非常常用的协议。几乎每一个公司都有一个可以通过HTTP/HTTPS访问的主页,不过HTTP/HTTPS的作用远不止此。例如现在很多云服务也都是提供HTTP/HTTPS的API对外服务的。
Netty也提供了很多关于HTTP的ChannelHandler,所以使用它们的时候不需要开发者编写编解码器等。
2.1、Netty的HTTP编解码器
HTTP使用的是请求-响应的模式,也就是客户端发出一个HTTP请求,然后服务端回复一个HTTP响应。Netty提供了各种各样的HTTP的编码器和解码器,所以开发HTTP的应用变得很容易。下面两幅图展示了完整的HTTP请求和响应的结构。
从上图可以看出来,完整的HTTP请求和响应包含的消息不止一个。它们往往以LastHttpContent结尾。上面这些HTTP的消息类型都是实现了HttpObject接口的。下表列出了常用的HTTP解码器和编码器。
名称
描述
HttpRequestEncoder
编码HttpRequest和HttpContent转成字节
HttpResponseEncoder
编码HttpResponse和HttpContent转成字节
HttpRequestDecoder
解码字节转成HttpRequest和HttpContent
HttpResponseDecoder
解码字节转成HttpResponse和HttpContent
public class HttpDecoderEncoderInitializer extends ChannelInitializer<Channel> { private final boolean client; public HttpDecoderEncoderInitializer(boolean client) { this.client = client; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (client) { pipeline.addLast("decoder", new HttpResponseDecoder()); pipeline.addLast("encoder", new HttpRequestEncoder()); } else { pipeline.addLast("decoder", new HttpRequestDecoder()); pipeline.addLast("encoder", new HttpResponseEncoder()); } } }
如果需要在ChannelPipeline编码和解码,有更简单易用的编解码器完成这个工作。可以使用HttpClientCodec或HttpServerCodec代替上面的编码器和解码器。
当ChannelPipeline中准备好了HTTP的编码器和解码器,应用就可以处理各种不同的HttpObject消息了。不过HTTP请求和响应都包含多个消息,需要处理各个部分并且还可能需要组合它们。这就比较麻烦了。为了解决这个问题,Netty提供了一个组合器,将消息组合到FullHttpRequest和FullHttpResponse中,所以开发者不用担心收到的消息不完整。
2.2、HTTP消息组合
当内存有余量时,我们往往只想处理完整的HTTP消息。因此,Netty提供了一个HttpObjectAggregator。通过HttpObjectAggregator,Netty会将HTTP消息组合到FullHttpResponse和FullHttpRequest中,在下一个ChannelHandler中只需要处理它们就可以了。这样就不用担心消息不完整,因为处理的都是完整消息对象。
消息组合很简单,就是添加一个ChannelHandler而已。
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> { private final boolean client; public HttpAggregatorInitializer(boolean client) { this.client = client; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (client) { pipeline.addLast("codec", new HttpClientCodec()); } else { pipeline.addLast("codec", new HttpServerCodec()); } pipeline.addLast("aggegator", new HttpObjectAggregator(512 * 1024)); } }
可以看出,很简单的代码Netty就可以帮助我们组合HTTP消息。不过要注意的是,为了保护你的服务端例如DoS攻击,应该为消息设置一个最大上限。上限取多少,取决于应用的实际情况了,比如日访问量,并发量以及内存容量等。
2.3、HTTP压缩
一般我们使用HTTP的时候,为了减小数据传输量,都会采取一定的压缩方法,特别是在移动端,流量还是比较珍贵的。当然,压缩数据也是有代价的,会提供CPU的负载。不过很多时候,其实我们的服务器CPU都是有富裕的,所以很多应用中使用压缩技术是一个正确的选择。
Netty提供了gzip和deflate的实现,一个用来压缩,一个用来解压缩。压缩与解压代码如下。
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> { private final boolean client; public HttpAggregatorInitializer(boolean client) { this.client = client; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (client) { pipeline.addLast("codec", new HttpClientCodec()); pipeline.addLast("decompressor", new HttpContentDecompressor()); } else { pipeline.addLast("codec", new HttpServerCodec()); pipeline.addLast("compressor", new HttpContentCompressor()); } } }
2.4、使用HTTPS
HTTP是明文传输的,有的时候我们保护我们传输的数据,通过HTTPS就可以了。
public class HttpsCodecInitializer extends ChannelInitializer<Channel> { private final SSLContext context; private final boolean client; public HttpsCodecInitializer(SSLContext context, boolean client) { this.context = context; this.client = client; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); SSLEngine engine = context.createSSLEngine(); engine.setUseClientMode(client); pipeline.addFirst("ssl", new SslHandler(engine)); if (client) { pipeline.addLast("codec", new HttpClientCodec()); } else { pipeline.addLast("codec", new HttpServerCodec()); } } }
上面的例子也可以看出来,Netty提供的ChannelPipeline很方面我们扩展应用。Netty提供的这些HTTP相关的实现帮助我们很方便的开发出基于HTTP协议的应用。下一小节我们将要学习一个扩展了HTTP协议的协议:WebSocket。
2.5、使用WebSocket
不可否认,HTTP是非常不错的,但是如果服务端想要实时发布自己的数据怎么办呢?可能之前大家了解过这方面的解决办法,比如AJAX轮询请求等,但这些方法都不是最优的,而且性能比较差。
这也就是为什么会出现WebSocket协议的原因。WebSocket允许服务端和客户端直接交换数据,不需要使用请求-响应的模式。刚开是WebSocket只能传输纯本文数据,不过现在不一样了,它也可以传输二进制数据,这样利用WebSocket就可以构建出很多丰富功能的应用。
本书不会太过详细讲解WebSocket,感兴趣的同学可以查查其他资料。不过,为了方便大家学习,这里简单介绍一下WebSocket服务端和客户端传输数据的基本结构,如下图。
握手还是通过HTTP完成的,最大的区别就是握手之后传输数据就不用再发送HTTP标识相关的数据了,一次握手,互相可以发送很多数据。
Netty也提供了很多WebSocket的实现,来简化我们的开发工作。使用WebSocket要处理很多不同类型的消息,如下表。
名称
说明
BinaryWebSocketFrame
二进制数据消息类型
TextWebSocketFrame
文本数据消息类型
ContinuationWebSocketFrame
二进制或文本数据,一般用于有多个消息类型的情况
CloseWebSocketFrame
关闭请求消息类型
PingWebSocketFrame
类似请求的消息类型
PongWebSocketFrame
类似响应的消息类型
为了节省时间,这里只简单讲解一下使用Netty在WebSocket服务端方面的应用,因为一般情况下,都不会使用Netty编写客户端应用,最常用的客户端还是浏览器,至于Netty的WebSocket客户端应用,和服务端差不多,具体可以参考Netty源码。
Netty提供了很多种方式使用WebSocket,不过最常用的还是WebSocketServerProtocolHandler。
public class WebSocketServerInitializer extends ChannelInitializer<Channel> { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast( new HttpServerCodec(), new HttpObjectAggregator(65536), new WebSocketServerProtocolHandler("/websocket"), new TextFrameHandler(), new BinaryFrameHandler(), new ContinuationFrameHandler()); } public static final class TextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { @Override public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { } } public static final class BinaryFrameHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> { @Override public void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception { } } public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> { @Override public void channelRead0(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception { } } }
可以看到,只是添加了WebSocket相关的Handler即可。当然如果需要加密,也就再添加一个SslHandler即可,不过记住,加密的要放到第一个位置。
2.6、SPDY
SPDY是谷歌为了提供网页响应速度而基于HTTP的增强协议,通过压缩、加密等方式提高网页响应速度,提升用户体验。但是,IETF对SPDY进行了标准化,很多好东西都移到了HTTP/2中,所以谷歌宣布放弃了对SPDY的支持,转去支持HTTP/2了。很多浏览器、服务器应用开发商都放弃了SPDY了,如Chrome,Nginx。但是本书原著有这一小节,所以我翻译的时候还是提一下,但相关知识不再翻译了,因为以后也没人用了,就不要浪费时间学这些过时的知识了。
三、空闲连接与超时处理
有些时候我们的应用需要处理空闲连接的情况和超时问题。很多时候我们对于连接都会有一个心跳监测机制,也就是每隔一段时间向对方发送一个消息包,用来检查对方是否还存活。另一种情况是针对空闲太久的连接直接断开。
所以来说,很多完整的应用处理空闲连接也是一个比较核心的部分,还好Netty提供了几个类用来处理这个问题。下表列出了Netty常用的处理空闲连接和超时问题的ChannelHandler。
名称
描述
IdleStateHandler
如果连接空闲时间过长会触发IdleStateEvent
ReadTimeoutHandler
超过时间没收到数据,这个就会抛出一个ReadTimeoutException,
并关闭Channel
WriteTimeoutHandler
超过时间写操作没完成就会抛出WriteTimeoutException
并关闭Channel
使用最多的就是IdleStateHandler了,下面我们会仔细研究一下它。下面的代码展示了如果超过60秒没有收到数据或发送数据,IdleStateHandler如何得到通知的。这个例子中会发送一个心跳检测给对方,如果检测失败就关闭连接。
public class IdleStateHandlerInitializer extends ChannelInitializer<Channel> { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //添加IdleStateHandler pipeline.addLast(new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS)); pipeline.addLast(new HeartbeatHandler()); } public static final class HeartbeatHandler extends ChannelInboundHandlerAdapter { private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("HEARTBEAT", CharsetUtil.ISO_8859_1)); @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { //发送心跳检测,失败就关闭Channel ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()) .addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { //不是IdleStateEvent就传递给下一个ChannelHandler super.userEventTriggered(ctx, evt); } } }}
四、解码以分隔符和内容长度为基础的协议
使用Netty开发网络应用,难免会遇到需要解码分隔符或内容长度为基础的协议。这一小节就来介绍下Netty提供了哪些类来帮助开发者处理这种协议。
4.1、分隔符基础的协议
如果需要处理分隔符基础的协议或在它之上构建应用。例如SMTP, POP3, IMAP和Telnet等协议都是这种类型的。Netty为此提供了一些ChannelHandler,可以帮助开发者很容易根据分隔符提取数据序列。
名称
描述
DelimiterBasedFameDecoder
根据指定的分隔符分割数据
LineBasedFrameDecoder
根据\r \n符号分割数据,也就是按行分割,
它的性能比DelimiterBasedFameDecoder快
下图简要展示了如何按行分割数据的。public class LineBasedHandlerInitializer extends ChannelInitializer<Channel> { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LineBasedFrameDecoder(65 * 1024)); pipeline.addLast(new FrameHandler()); } public static final class FrameHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override public void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { } }}如果你的应用的分割比较特殊,就可以选择使用DelimiterBasedFrameDecoder,它和LineBasedFrameDecoder用法差不多,只不过在创建实例的时候需要传入分隔符。
这个类也可以用来实现自己的分隔符协议。假设我们一个应用需要处理命令行指令,命令是由命令名称和参数组成。名称和参数通过空格分割。下面我们通过继承LineBasedFrameDecoder来实现我们自己的分割器。
public class CmdHandlerInitializer extends ChannelInitializer<Channel> { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new CmdDecoder(65 * 1024)); pipeline.addLast(new CmdHandler()); } public static final class Cmd { private final ByteBuf name; private final ByteBuf args; public Cmd(ByteBuf name, ByteBuf args) { this.name = name; this.args = args; } public ByteBuf name() { return name; } public ByteBuf args() { return args; } } public static final class CmdDecoder extends LineBasedFrameDecoder { public CmdDecoder(int maxLength) { super(maxLength); } @Override protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { ByteBuf frame = (ByteBuf) super.decode(ctx, buffer); if (frame == null) { return null; } int index = frame.indexOf(frame.readerIndex(), frame.writerIndex(), (byte) ' '); return new Cmd(frame.slice(frame.readerIndex(), index), frame.slice(index + 1, frame.writerIndex())); } } public static final class CmdHandler extends SimpleChannelInboundHandler<Cmd> { @Override public void channelRead0(ChannelHandlerContext ctx, Cmd msg) throws Exception { } }}
4.2、长度为基础的协议
开发网络应用经常也会遇到以长度为基础的协议。为此Netty也提供了两个不同的分割器帮助开发者提取数据。
名称
描述
FixedLengthFrameDecoder
根据固定长度提取数据
LengthFieldBasedFrameDecoder
根据头信息中的长度数据分割数据
为了更加了解它们,让我们看看它们的工作结构,先看一下FixedLengthFrameDecoder的。 从上图可以很明显的看出来,FixedLengthFrameDecoder是按固定长度提取的,每个数据块都是8字节。
另外一些时候,协议中会将数据长度封装到头信息中,这个时候可以使用LengthFieldBasedFrameDecoder,它会从头信息中读取长度,然后按此长度提取数据。
如果长度信息是头数据中的一部分,也可以通过LengthFieldBasedFrameDecoder的构造函数配置。可以指定长度信息位置和具体长度,具体可以参考API文档。
FixedLengthFrameDecoder是非常易用的,所以这里我们简单举一个LengthFieldBasedFrameDecoder的例子。
public class LengthBasedInitializer extends ChannelInitializer<Channel> { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //指定长度信息在前8字节 pipeline.addLast(new LengthFieldBasedFrameDecoder(65 * 1024, 0, 8)); pipeline.addLast(new FrameHandler()); } public static final class FrameHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override public void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { } }}
五、写大量数据
在异步框架中以有效的方式写大量数据通常都是一个难点,例如需要在网络网络饱和的时候停止写数据,又或者你把内存使用完了会出现OutOfMemoryError的错误。
Netty允许使用零内存复制的技术发送文件内容,也就是说以最大的性能,通过内核空间直接将文件系统中的数据放到网络栈中。这个功能只适用于直接发送文件内容而不需要应用去操作文件内容;当你的应用要对文件内容进行操作,就需要将内容复制到用户空间去。至于上面的工作都是由Netty去完成,开发者不用关心底层具体细节。通过零内存复制技术发送文件内容,需要将一个DefaultFileRegion写到Channel,ChannelHandlerContext或ChannelPipeline,如下面的代码。
FileInputStream in = new FileInputStream(file); FileRegion region = new DefaultFileRegion(in.getChannel(), 0, file.length()); channel.writeAndFlush(region) .addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { Throwable cause = future.cause(); } } });但是当你需要发送大量非文件内容的时候该怎么办呢?
Netty提供了一个特殊的ChannelHandler,名字叫ChunkedWriteHandler,它可以处理ChunkedInput的实现的大量数据,Netty提供了下表这些实现。
名称
描述
ChunkedFile
可以写文件,一般是在操作系统不支持零内存复制的时候才用它
ChunkedNioFile
也是文件,只不过换成NIO的方式,也是在操作系统不支持零内存复制的时候才用它
ChunkedNioStream
将ReadableByteChannel的内容转到它里面
ChunkedStream
可以将InputStream的内容转到它里面
ChunkedStream是最常用的,下面举个简单例子。
public class ChunkedWriteHandlerInitializer extends ChannelInitializer<Channel> { private final File file; public ChunkedWriteHandlerInitializer(File file) { this.file = file; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new WriteStreamHandler()); } public final class WriteStreamHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); ctx.writeAndFlush(new ChunkedStream(new FileInputStream(file))); } }}
六、序列化数据
当你需要在网络中传输Java对象时,Java提供了ObjectOutputStream和ObjectInputStream来序列化对象。不过也有一些其他方法可以做到,下面来看一下Netty提供的方式。
6.1、JDK序列化
如果你的应用的对端使用ObjectOutputStream和ObjectInputStream并且你要保持兼容性,或者你又不想依赖别的Java库,JDK序列化是一个不错的选择。
名称
描述
CompatibleObjectDecoder
兼容JDK的反序列化器,对端也不需要使用Netty
CompatibleObjectEncoder
兼容JDK的序列化器,对端也不需要使用Netty
CompactObjectDecoder
基于JDK反序列化的自定义实现,一般来说只有在想提高性能又不想依赖外部库的
情况下才会使用这个,否则其他的库性能更好
CompactObjectEncoder
基于JDK序列化的自定义实现,一般来说只有在想提高性能又不想依赖外部库的
情况下才会使用这个,否则其他的库性能更好
6.2、JBoss Marshalling序列化
如果想使用外部的序列化库,JBoss的Marshalling是一个不错的选择。它的速度比JDK原生的大概快3倍。
Netty提供了一种兼容JDK序列化的Marshalling序列化类,性能也不错;另一种就是对端也使用Marshalling,这样就会最大化性能。
名称
描述
CompatibleMarshallingDecoder
兼容JDK反序列化,对端不需要使用Netty
CompatibleMarshallingEncoder
兼容JDK序列化,对端不需要使用Netty
MarshallingDecoder
Marshalling反序列化
MarshallingEncoder
Marshalling序列化
public class MarshallingInitializer extends ChannelInitializer<Channel> { private final MarshallerProvider marshallerProvider; private final UnmarshallerProvider unmarshallerProvider; public MarshallingInitializer(UnmarshallerProvider unmarshallerProvider, MarshallerProvider marshallerProvider) { this.marshallerProvider = marshallerProvider; this.unmarshallerProvider = unmarshallerProvider; } @Override protected void initChannel(Channel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new MarshallingDecoder(unmarshallerProvider)); pipeline.addLast(new MarshallingEncoder(marshallerProvider)); pipeline.addLast(new ObjectHandler()); } public static final class ObjectHandler extends SimpleChannelInboundHandler<Serializable> { @Override public void channelRead0(ChannelHandlerContext channelHandlerContext, Serializable serializable) throws Exception { } }}
6.3、ProtoBuf序列化
Netty也提供了ProtoBuf序列化的相关类来简化开发。ProtoBuf是谷歌开源的一个序列化与反序列化框架,它支持很多不同开发语言,所以有很好的夸平台性。
名称
描述
ProtobufDecoder
反序列化
ProtobufEncoder
序列化
ProtobufVarint32FrameDecoder
根据谷歌协议的长度128字段反序列化
public class ProtoBufInitializer extends ChannelInitializer<Channel> { private final MessageLite lite; public ProtoBufInitializer(MessageLite lite) { this.lite = lite; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new ProtobufVarint32FrameDecoder()); pipeline.addLast(new ProtobufEncoder()); pipeline.addLast(new ProtobufDecoder(lite)); pipeline.addLast(new ObjectHandler()); } public static final class ObjectHandler extends SimpleChannelInboundHandler<Object> { @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { } }}
七、总结
本章主要介绍了Netty提供的常用的ChannelHandler和编解码器。这些都可以帮助开发者快速开发出自己的网络应用,减少重复造轮子的代码。
通过本章的例子,也基本了解了如何组合这些ChannelHandler和编解码器,来完成应用需求。Netty提供的这些ChannelHandler和编解码器也是经过很多测试和验证的,是比较可靠强大的。
当然本章也只是简单介绍了一些常用的类,很有很多其他的,要介绍完估计都可以令写一本书了,所以如果感兴趣的同学可以在下面参考详细的API文档。
下一章主要学习如何启动服务并组合ChannelHandler完成业务需求。
- Netty4实战第八章:Netty提供的ChannelHandler和编解码器
- netty in action第八章-附带的ChannelHandler和Codec
- Netty4实战第六章:ChannelHandler
- Netty4实战第七章:编解码器
- Netty In Action中文版 - 第八章:附带的ChannelHandler和Codec
- Netty In Action中文版 - 第八章:附带的ChannelHandler和Codec
- Netty4实战第十四章:自定义编解码器
- Netty实战读书笔记二:ChannelHandler和ChannelPipeline
- Netty4实战第十章:Netty应用的单元测试
- Netty4实战第三章:Netty基础
- Netty4实战第九章:启动Netty应用
- Netty封装的ChannelHandler
- Netty4实战第一章:Netty和Java NIO APIs
- netty的编解码器介绍
- netty的编解码器介绍
- 《Netty in Action》中文版—第六章 ChannelHandler和ChannelPipeline
- Netty4实战第二章:第一个Netty应用
- Netty4.1.x ChannelHandler
- AtCoder Grand Contest 001 EBBQ Hard
- win7 64位操作系统中Oracle 11g + plsql安装教程详解(图解)
- Codeforces 869B. The Eternal Immortality
- 数据结构之单链表的基本实现
- 还是超前校正
- Netty4实战第八章:Netty提供的ChannelHandler和编解码器
- 【NOIP2017提高A组模拟10.7】Adore
- Java类的初始化过程
- Python2 字典
- Oracle客户端安装以及PL/SQL Developer安装方法
- Python中的深拷贝和浅拷贝详解
- C语言数据结构——单链表
- java操作pdf制作电子签章
- 前端之路——第八篇:用之前学过的知识,复刻百度首页