最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

第八章:附带的ChannelHandler和Codec

来源:动视网 责编:小OO 时间:2025-09-29 19:31:17
文档

第八章:附带的ChannelHandler和Codec

目录[-]使用SSLTLS创建安全的Netty程序1.使用Netty创建HTTPHTTPS程序2.Netty的HTTP编码器解码器和编解码器1.HTTP消息聚合2.HTTP压缩3.使用HTTPS4.WebSocket5.SPDY6.处理空闲连接和超时3.解码分隔符和基于长度的协议4.分隔符协议1.长度为基础的协议2.写大数据5.序列化数据6.普通的JDK序列化1.通过JBoss编组序列化2.使用ProtoBuf序列化3.第八章:附带的ChannelHandler和Codec本章介绍使用SSL/
推荐度:
导读目录[-]使用SSLTLS创建安全的Netty程序1.使用Netty创建HTTPHTTPS程序2.Netty的HTTP编码器解码器和编解码器1.HTTP消息聚合2.HTTP压缩3.使用HTTPS4.WebSocket5.SPDY6.处理空闲连接和超时3.解码分隔符和基于长度的协议4.分隔符协议1.长度为基础的协议2.写大数据5.序列化数据6.普通的JDK序列化1.通过JBoss编组序列化2.使用ProtoBuf序列化3.第八章:附带的ChannelHandler和Codec本章介绍使用SSL/


目录

[-]

使用SSLTLS 创建安全的Netty 程序1.

使用Netty

创建HTTPHTTPS

程序2.Netty 的HTTP 编码器解码器和编解码器1.HTTP 消息聚合

2.HTTP 压缩

3.使用HTTPS

4.WebSocket

5.SPDY

6.处理空闲连接和超时

3.解码分隔符和基于长度的协议

4.分隔符协议

1.长度为基础的协议

2.写大数据

5.序列化数据

6.普通的JDK 序列化

1.通过JBoss 编组序列化

2.使用ProtoBuf 序列化

3.第八章:附带的ChannelHandler 和Codec

本章介绍

使用SSL/TLS 创建安全的Netty 程序

使用Netty 创建HTTP/HTTPS 程序

处理空闲连接和超时

解码分隔符和基于长度的协议

写大数据

序列化数据

上一章讲解了如何创建自己的编解码器,我们现在可以用上一章的知识来编写自己的编解码器。不过Netty 提供了一些标准的

ChannelHandler 和Codec 。Netty 提供了很多协议的支持,所以我们不必自己发明轮子。Netty 提供的这些实现可以解决我们的大部分需求。本章讲解Netty 中使用SSL/TLS 编写安全的应用程序,编写HTTP 协议服务器,以及使用如WebSocket 或Google 的SPDY 协议来使HTTP 服务获得更好的性能;这些都是很常见的应用,本章还会介绍数据压缩,在数据量比较大的时候,压缩数据是很有必要的。

8.1 使用SSL/TLS 创建安全的Netty 程序

通信数据在网络上传输一般是不安全的,因为传输的数据可以发送纯文本或二进制的数据,很容易被破解。我们很有必要对网络上的数据进行加密。SSL 和TLS 是众所周知的标准和分层的协议,它们可以确保数据时私有的。例如,使用HTTPS 或SMTPS 都使用了SSL/TLS 对数据进行了加密。

对于SSL/TLS ,Java 中提供了抽象的SslContext 和SslEngine 。实际上,SslContext 可以用来获取SslEngine 来进行加密和解密。使用指定的加密技术是高度可配置的,但是这不在本章范围。Netty 扩展了Java 的SslEngine ,添加了一些新功能,使其更适合基于Netty 的应用程序。Netty 提供的这个扩展是SslHandler ,是SslEngine 的包装类,用来对网络数据进行加密和解密。

下图显示SslHandler 实现的数据流:

上图显示了如何使用ChannelInitializer 将SslHandler 添加到ChannelPipeline ,看下面代码:

[java]

public class SslChannelInitializer extends ChannelInitializer { 01.

02.

private final SSLContext context; 03.

private final boolean client; 04.

private final boolean startTls; 05.

06.

public SslChannelInitializer(SSLContext context, boolean client, boolean startTls) { 07.

this .context = context; 08.

this .client = client;

09.

10.

11.

}

12.

@Override

13.

14.

protected void initChannel(Channel ch) throws Exception {

15.

SSLEngine engine = context.createSSLEngine();

engine.setUseClientMode(client);

16.

17.

ch.pipeline().addFirst("ssl

18.

}

}

19.

需要注意一点,SslHandler必须要添加到ChannelPipeline的第一个位置,可能有一些例外,但是最好这样来做。回想一下之前讲解的ChannelHandler,ChannelPipeline就像是一个在处理“入站”数据时先进先出,在处理“出站”数据时后进先出的队列。最先添加的SslHandler会啊在其他Handler处理逻辑数据之前对数据进行加密,从而确保Netty服务端的所有的Handler的变化都是安全的。

SslHandler提供了一些有用的方法,可以用来修改其行为或得到通知,一旦SSL/TLS完成握手(在握手过程中的两个对等通道互相验证对方,然后选择一个加密密码),SSL/TLS是自动执行的。看下面方法列表:

setHandshakeTimeout(long handshakeTimeout, TimeUnit unit),设置握手超时时间,ChannelFuture将得到通知

setHandshakeTimeoutMillis(long handshakeTimeoutMillis),设置握手超时时间,ChannelFuture将得到通知

getHandshakeTimeoutMillis(),获取握手超时时间值

setCloseNotifyTimeout(long closeNotifyTimeout, TimeUnit unit),设置关闭通知超时时间,若超时,ChannelFuture会关闭失败

setHandshakeTimeoutMillis(long handshakeTimeoutMillis),设置关闭通知超时时间,若超时,ChannelFuture会关闭失败

getCloseNotifyTimeoutMillis(),获取关闭通知超时时间

handshakeFuture(),返回完成握手后的ChannelFuture

close(),发送关闭通知请求关闭和销毁

8.2 使用Netty创建HTTP/HTTPS程序

HTTP/HTTPS是最常用的协议之一,可以通过HTTP/HTTPS访问网站,或者是提供对外公开的接口服务等等。Netty附带了使用HTTP

/HTTPS的handlers,而不需要我们自己来编写编解码器。

8.2.1 Netty的HTTP编码器,解码器和编解码器

HTTP是请求-响应模式,客户端发送一个http请求,服务就响应此请求。Netty提供了简单的编码解码HTTP协议消息的Handler。下图显示了http请求和响应:

如上面两个图所示,一个HTTP请求/响应消息可能包含不止一个,但最终都会有LastHttpContent消息。FullHttpRequest和FullHttpResponse是Netty提供的两个接口,分别用来完成http请求和响应。所有的HTTP消息类型都实现了HttpObject接口。下面是类关系图:

Netty提供了HTTP请求和响应的编码器和解码器,看下面列表:

HttpRequestEncoder,将HttpRequest或HttpContent编码成ByteBuf

HttpRequestDecoder,将ByteBuf解码成HttpRequest和HttpContent

HttpResponseEncoder,将HttpResponse或HttpContent编码成ByteBuf

HttpResponseDecoder,将ByteBuf解码成HttpResponse和HttpContent

看下面代码:

[java]

01.

public class HttpDecoderEncoderInitializer extends ChannelInitializer {

02.

03.

private final boolean client;

04.

05.

public HttpDecoderEncoderInitializer(boolean client) {

06.

this.client = client;

07.

}

08.

09.

@Override

10.

protected void initChannel(Channel ch) throws Exception {

11.

ChannelPipeline pipeline = ch.pipeline();

12.

if (client) {

13.

pipeline.addLast("decoder

14.

pipeline.addLast("

15.

} else {

16.

pipeline.addLast("decoder

17.

pipeline.addLast("encoder

18.

}

19.

}

20.

}

如果你需要在ChannelPipeline中有一个解码器和编码器,还分别有一个在客户端和服务器简单的编解码器:HttpClientCodec和HttpServerCodec。

在ChannelPipelien中有解码器和编码器(或编解码器)后就可以操作不同的HttpObject消息了;但是HTTP请求和响应可以有很多消息数据,你需要处理不同的部分,可能也需要聚合这些消息数据,这是很麻烦的。为了解决这个问题,Netty提供了一个聚合器,它将消息部分合并到FullHttpRequest和FullHttpResponse,因此不需要担心接收碎片消息数据。

8.2.2 HTTP消息聚合

处理HTTP时可能接收HTTP消息片段,Netty需要缓冲直到接收完整个消息。要完成的处理HTTP消息,并且内存开销也不会很大,Netty 为此提供了HttpObjectAggregator。通过HttpObjectAggregator,Netty可以聚合HTTP消息,使用FullHttpResponse和FullHttpRequest到ChannelPipeline中的下一个ChannelHandler,这就消除了断裂消息,保证了消息的完整。下面代码显示了如何聚合:

[java]

01.

/**

02.

* 添加聚合http消息的Handler

03.

*

04.

* @author c.k

05.

*

06.

*/

07.

public class HttpAggregatorInitializer extends ChannelInitializer {

08.

09.

private final boolean client;

10.

11.

public HttpAggregatorInitializer(boolean client) {

12.

this.client = client;

}

13.

14.

15.

@Override

16.

protected void initChannel(Channel ch) throws Exception {

17.

ChannelPipeline pipeline = ch.pipeline();

if (client) {

18.

19.

pipeline.addLast("codec

20.

} else {

21.

pipeline.addLast("codec

22.

}

23.

pipeline.addLast("aggegator

}

24.

25.

26.

}

如上面代码,很容使用Netty自动聚合消息。但是请注意,为了防止Dos攻击服务器,需要合理的消息的大小。应设置多大取决于实际的需求,当然也得有足够的内存可用。

8.2.3 HTTP压缩

使用HTTP时建议压缩数据以减少传输流量,压缩数据会增加CPU负载,现在的硬件设施都很强大,大多数时候压缩数据时一个好主意。Netty支持“gzip”和“deflate”,为此提供了两个ChannelHandler实现分别用于压缩和解压。看下面代码:

[java]

@Override

01.

02.

protected void initChannel(Channel ch) throws Exception {

03.

ChannelPipeline pipeline = ch.pipeline();

04.

if (client) {

05.

pipeline.addLast("codec

06.

//添加解压缩Handler

pipeline.addLast("decompressor

07.

08.

} else {

09.

pipeline.addLast("codec

10.

//添加解压缩Handler

11.

pipeline.addLast("decompressor

12.

}

pipeline.addLast("aggegator

13.

14.

}

8.2.4 使用HTTPS

网络中传输的重要数据需要加密来保护,使用Netty提供的SslHandler可以很容易实现,看下面代码:

[java]

01.

/**

02.

* 使用SSL对HTTP消息加密

03.

*

04.

* @author c.k

05.

*

*/

06.

07.

public class HttpsCodecInitializer extends ChannelInitializer {

08.

09.

private final SSLContext context;

10.

private final boolean client;

11.

12.

public HttpsCodecInitializer(SSLContext context, boolean client) {

13.

this.context = context;

14.

this.client = client;

15.

}

16.

17.

@Overrideprotected void initChannel(Channel ch) throws Exception {

18.

19.

SSLEngine engine = context.createSSLEngine();

20.

engine.setUseClientMode(client);

21.

ChannelPipeline pipeline = ch.pipeline();

22.

pipeline.addFirst("ssl

23.

if (client) {

pipeline.addLast("codec

24.

25.

} else {

26.

pipeline.addLast("codec

}

27.

28.

}

29.

30.

}

8.2.5 WebSocket

HTTP是不错的协议,但是如果需要实时发布信息怎么做?有个做法就是客户端一直轮询请求服务器,这种方式虽然可以达到目的,但是其缺点很多,也不是优秀的解决方案,为了解决这个问题,便出现了WebSocket。

WebSocket允许数据双向传输,而不需要请求-响应模式。早期的WebSocket只能发送文本数据,然后现在不仅可以发送文本数据,也可以发送二进制数据,这使得可以使用WebSocket构建你想要的程序。下图是WebSocket的通信示例图:

在应用程序中添加WebSocket支持很容易,Netty附带了WebSocket的支持,通过ChannelHandler来实现。使用WebSocket有不同的消息类型需要处理。下面列表列出了Netty中WebSocket类型:

BinaryWebSocketFrame,包含二进制数据

T extWebSocketFrame,包含文本数据

ContinuationWebSocketFrame,包含二进制数据或文本数据,BinaryWebSocketFrame和T extWebSocketFrame的结合体

CloseWebSocketFrame,WebSocketFrame代表一个关闭请求,包含关闭状态码和短语

PingWebSocketFrame,WebSocketFrame要求PongWebSocketFrame发送数据

PongWebSocketFrame,WebSocketFrame要求PingWebSocketFrame响应

为了简化,我们只看看如何使用WebSocket服务器。客户端使用可以看Netty自带的WebSocket例子。

Netty提供了许多方法来使用WebSocket,但最简单常用的方法是使用WebSocketServerProtocolHandler。看下面代码:

[java]

01.

/**

02.

* WebSocket Server,若想使用SSL加密,将SslHandler加载ChannelPipeline的最前面即可

03.

* @author c.k

04.

*

05.

*/

06.

public class WebSocketServerInitializer extends ChannelInitializer {

07.

08.

@Override

09.

protected void initChannel(Channel ch) throws Exception {

10.

ch.pipeline().addLast(new HttpServerCodec(),

11.

new HttpObjectAggregator(65536),

12.

new WebSocketServerProtocolHandler("/websocket"),

13.

new TextFrameHandler(),

14.

new BinaryFrameHandler(),

15.

new ContinuationFrameHandler());

16.

}

17.

18.

public static final class TextFrameHandler extends SimpleChannelInboundHandler {

19.

@Override

20.

protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

21.

// handler text frame}

22.

23.

}

24.

25.

public static final class BinaryFrameHandler extends SimpleChannelInboundHandler{ @Override

26.

27.

protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception {

28.

//handler binary frame

29.

}

30.

}

31.

32.

public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler{ @Override

33.

34.

protected void channelRead0(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception {

35.

//handler continuation frame

}

36.

37.

}

38.

}

8.2.6 SPDY

SPDY(读作“SPeeDY”)是Google开发的基于TCP的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。新协议的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。谷歌表示,引入SPDY协议后,在实验室测试中页面加载速度比原先快%。

SPDY的定位:

将页面加载时间减少50%。

最大限度地减少部署的复杂性。SPDY使用TCP作为传输层,因此无需改变现有的网络设施。

避免网站开发者改动内容。支持SPDY唯一需要变化的是客户端代理和Web服务器应用程序。

SPDY实现技术:

单个TCP连接支持并发的HTTP请求。

压缩报头和去掉不必要的头部来减少当前HTTP使用的带宽。

定义一个容易实现,在服务器端高效率的协议。通过减少边缘情况、定义易解析的消息格式来减少HTTP的复杂性。

强制使用SSL,让SSL协议在现存的网络设施下有更好的安全性和兼容性。

允许服务器在需要时发起对客户端的连接并推送数据。

SPDY具体的细节知识及使用可以查阅相关资料,这里不作赘述了。

8.3 处理空闲连接和超时

处理空闲连接和超时是网络应用程序的核心部分。当发送一条消息后,可以检测连接是否还处于活跃状态,若很长时间没用了就可以断开连接。Netty提供了很好的解决方案,有三种不同的ChannelHandler处理闲置和超时连接:

IdleStateHandler,当一个通道没有进行读写或运行了一段时间后出发IdleStateEvent

ReadTimeoutHandler,在指定时间内没有接收到任何数据将抛出ReadTimeoutException

WriteTimeoutHandler,在指定时间内有写入数据将抛出WriteTimeoutException

最常用的是IdleStateHandler,下面代码显示了如何使用IdleStateHandler,如果60秒内没有接收数据或发送数据,操作将失败,连接将关闭:

[java]

01.

public class IdleStateHandlerInitializer extends ChannelInitializer {

02.

03.

@Override

04.

protected void initChannel(Channel ch) throws Exception {

05.

ChannelPipeline pipeline = ch.pipeline();

06.

pipeline.addLast(new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS));

07.

pipeline.addLast(new HeartbeatHandler());

08.

}

09.

10.

public static final class HeartbeatHandler extends ChannelInboundHandlerAdapter {

private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer(

11.

12.

"HEARTBEAT

13.

14.

@Override

public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

15.

16.

if (evt instanceof IdleStateEvent) {

17.

ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);

} else {

18.

19.

super.userEventTriggered(ctx, evt);

20.

}

}

21.

22.

}

8.4 解码分隔符和基于长度的协议

使用Netty时会遇到需要解码以分隔符和长度为基础的协议,本节讲解Netty如何解码这些协议。

8.4.1 分隔符协议

经常需要处理分隔符协议或创建基于它们的协议,例如SMTP、POP3、IMAP、T elnet等等;Netty附带的handlers可以很容易的提取一些序列分隔:

DelimiterBasedFrameDecoder,解码器,接收ByteBuf由一个或多个分隔符拆分,如NUL或换行符

LineBasedFrameDecoder,解码器,接收ByteBuf以分割线结束,如"\\n"和"\\r\\n"

下图显示了使用"\\r\\n"分隔符的处理:

下面代码显示使用LineBasedFrameDecoder提取"\\r\\n"分隔帧:

[java]

/**

01.

02.

* 处理换行分隔符消息

* @author c.k

03.

04.

*

05.

*/

06.

public class LineBasedHandlerInitializer extends ChannelInitializer {

07.

08.

@Override

09.

protected void initChannel(Channel ch) throws Exception {

10.

ch.pipeline().addLast(new LineBasedFrameDecoder(65 * 1204), new FrameHandler());

11.

}

12.

13.

public static final class FrameHandler extends SimpleChannelInboundHandler {

14.

@Override

15.

protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {

16.

// do something with the frame

}

17.

18.

}

19.

}

如果框架的东西除了换行符还有别的分隔符,可以使用DelimiterBasedFrameDecoder,只需要将分隔符传递到构造方法中。如果想实现自己的以分隔符为基础的协议,这些解码器是有用的。例如,现在有个协议,它只处理命令,这些命令由名称和参数形成,名称和参数由一个空格分隔,实现这个需求的代码如下:

[java]

01.

/**

* 自定义以分隔符为基础的协议

02.

03.

* @author c.k

04.

*

05.

*/

06.

public class CmdHandlerInitializer extends ChannelInitializer {

07.

08.

@Override

protected void initChannel(Channel ch) throws Exception {

09.

10.

ch.pipeline().addLast(new CmdDecoder(65 * 1024), new CmdHandler());

11.

}

12.

13.

public static final class Cmd {

14.

private final ByteBuf name;

15.

private final ByteBuf args;

16.

17.

public Cmd(ByteBuf name, ByteBuf args) {

18.

this.name = name;

19.

this.args = args;

20.

}

21.

public ByteBuf getName() {

22.

23.

return name;

24.

}

26.

27.

return args;

28.

}

}

29.

30.

31.

public static final class CmdDecoder extends LineBasedFrameDecoder {

32.

33.

public CmdDecoder(int maxLength) {

34.

super(maxLength);

35.

}

36.

37.

@Override

38.

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {

39.

ByteBuf frame = (ByteBuf) super.decode(ctx, buffer);

40.

if (frame == null) {

41.

return null;

42.

}

43.

int index = frame.indexOf(frame.readerIndex(), frame.writerIndex(), (byte) ' ');

44.

return new Cmd(frame.slice(frame.readerIndex(), index), frame.slice(index + 1, frame.writerIndex()));

45.

}

46.

}

47.

48.

public static final class CmdHandler extends SimpleChannelInboundHandler {

49.

@Override

50.

protected void channelRead0(ChannelHandlerContext ctx, Cmd msg) throws Exception {

51.

// do something with the command

52.

}

53.

}

54.

55.

}

8.4.2 长度为基础的协议

一般经常会碰到以长度为基础的协议,对于这种情况Netty有两个不同的解码器可以帮助我们来解码:

FixedLengthFrameDecoder

LengthFieldBasedFrameDecoder

下图显示了FixedLengthFrameDecoder的处理流程:

如上图所示,FixedLengthFrameDecoder提取固定长度,例子中的是8字节。大部分时候帧的大小被编码在头部,这种情况可以使用LengthFieldBasedFrameDecoder,它会读取头度并提取帧的长度。下图显示了它是如何工作的:

如果长度字段是提取框架的一部分,可以在LengthFieldBasedFrameDecoder的构造方法中配置,还可以指定提供的长度。FixedLengthFrameDecoder很容易使用,我们重点讲解LengthFieldBasedFrameDecoder。下面代码显示如何使用LengthFieldBasedFrameDecoder提取8字节长度:

[java]

01.

public class LengthBasedInitializer extends ChannelInitializer {

02.

03.

@Override

protected void initChannel(Channel ch) throws Exception {

04.

05.

ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65*1024, 0, 8))

06.

.addLast(new FrameHandler());

}

07.

08.public static final class FrameHandler extends SimpleChannelInboundHandler{

09.

10.

@Override

11.

protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {

12.

//do something with the frame

13.

}

14.

}

15.

}

8.5 写大数据

写大量的数据的一个有效的方法是使用异步框架,如果内存和网络都处于饱满负荷状态,你需要停止写,否则会报OutOfMemoryError。Netty提供了写文件内容时zero-memory-copy机制,这种方法再将文件内容写到网络堆栈空间时可以获得最大的性能。使用零拷贝写文件的内容时通过DefaultFileRegion、ChannelHandlerContext、ChannelPipeline,看下面代码:

[java]

01.

@Override

02.

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

03.

File file = new File("test.txt");

FileInputStream fis = new FileInputStream(file);

04.

05.

FileRegion region = new DefaultFileRegion(fis.getChannel(), 0, file.length());

06.

Channel channel = ctx.channel();

07.

channel.writeAndFlush(region).addListener(new ChannelFutureListener() {

08.

09.

@Override

10.

public void operationComplete(ChannelFuture future) throws Exception {

11.

if(!future.isSuccess()){

12.

Throwable cause = future.cause();

13.

// do something

14.

}

15.

}

});

16.

17.

}

如果只想发送文件中指定的数据块应该怎么做呢?Netty提供了ChunkedWriteHandler,允许通过处理ChunkedInput来写大的数据块。下面是ChunkedInput的一些实现类:

ChunkedFile

ChunkedNioFile

ChunkedStream

ChunkedNioStream

看下面代码:

[java]

01.

public class ChunkedWriteHandlerInitializer extends ChannelInitializer {

02.

private final File file;

03.

04.

public ChunkedWriteHandlerInitializer(File file) {

05.

this.file = file;

06.

}

07.

08.

@Override

09.

protected void initChannel(Channel ch) throws Exception {

10.

ch.pipeline().addLast(new ChunkedWriteHandler())

11.

.addLast(new WriteStreamHandler());

12.

}

13.

public final class WriteStreamHandler extends ChannelInboundHandlerAdapter {

14.

15.

@Override

16.

public void channelActive(ChannelHandlerContext ctx) throws Exception {

17.

super.channelActive(ctx);

18.

ctx.writeAndFlush(new ChunkedStream(new FileInputStream(file)));

19.

}

20.

}

21.

}

8.6 序列化数据

开发网络程序过程中,很多时候需要传输结构化对象数据POJO,Java中提供了ObjectInputStream和ObjectOutputStream及其他的一些对象序列化接口。Netty中提供基于JDK序列化接口的序列化接口。

8.6.1 普通的JDK序列化

如果你使用ObjectInputStream和ObjectOutputStream,并且需要保持兼容性,不想有外部依赖,那么JDK的序列化是首选。Netty提供了下面的一些接口,这些接口放在io.netty.handler.codec.serialization包下面:

CompatibleObjectEncoder

CompactObjectInputStream

CompactObjectOutputStream

ObjectEncoder

ObjectDecoder

ObjectEncoderOutputStream

ObjectDecoderInputStream

8.6.2 通过JBoss编组序列化

如果你想使用外部依赖的接口,JBoss编组是个好方法。JBoss Marshalling序列化的速度是JDK的3倍,并且序列化的结构更紧凑,从而使序列化后的数据更小。Netty附带了JBoss编组序列化的实现,这些实现接口放在io.netty.handler.codec.marshalling包下面:

CompatibleMarshallingEncoder

CompatibleMarshallingDecoder

MarshallingEncoder

MarshallingDecoder

看下面代码:

[java]

01.

/**

02.

* 使用JBoss Marshalling

03.

* @author c.k

04.

*

05.

*/

06.

public class MarshallingInitializer extends ChannelInitializer {

07.

private final MarshallerProvider marshallerProvider;

08.

private final UnmarshallerProvider unmarshallerProvider;

09.

public MarshallingInitializer(MarshallerProvider marshallerProvider, UnmarshallerProvider unmarshallerProvider) {

10.

11.

this.marshallerProvider = marshallerProvider;

12.

this.unmarshallerProvider = unmarshallerProvider;

13.

}

14.

15.

@Override

16.

protected void initChannel(Channel ch) throws Exception {

17.

ch.pipeline().addLast(new MarshallingDecoder(unmarshallerProvider))

18.

.addLast(new MarshallingEncoder(marshallerProvider))

19.

.addLast(new ObjectHandler());

}

20.

21.

22.

public final class ObjectHandler extends SimpleChannelInboundHandler {

23.

@Override

24.

protected void channelRead0(ChannelHandlerContext ctx, Serializable msg) throws Exception {

25.

// do something

26.

}

27.

}

28.

}

8.6.3 使用ProtoBuf序列化

最有一个序列化方案是Netty附带的ProtoBuf。protobuf是Google开源的一种编码和解码技术,它的作用是使序列化数据更高效。并且谷歌提供了protobuf的不同语言的实现,所以protobuf在跨平台项目中是非常好的选择。Netty附带的protobuf放在io.netty.handler.codec.protobuf 包下面:

ProtobufDecoder

ProtobufEncoder

ProtobufV arint32FrameDecoder

ProtobufV arint32LengthFieldPrepender

看下面代码:

[java]

/**

01.

02.

* 使用protobuf序列化数据,进行编码解码

03.

* 注意:使用protobuf需要protobuf-java-2.5.0.jar* @author Administrator

04.

05.

*

06.

*/

07.

public class ProtoBufInitializer extends ChannelInitializer {

08.

09.

private final MessageLite lite;

10.

public ProtoBufInitializer(MessageLite lite) {

11.

12.

this.lite = lite;

13.

}

14.

15.

@Override

16.

protected void initChannel(Channel ch) throws Exception {

17.

ch.pipeline().addLast(new ProtobufVarint32FrameDecoder())

.addLast(new ProtobufEncoder())

18.

19.

.addLast(new ProtobufDecoder(lite))

20.

.addLast(new ObjectHandler());

21.

}

22.

public final class ObjectHandler extends SimpleChannelInboundHandler {

23.

24.

@Override

protected void channelRead0(ChannelHandlerContext ctx, Serializable msg) throws Exception {

25.

26.

// do something

27.

}

}

28.

29.

}

文档

第八章:附带的ChannelHandler和Codec

目录[-]使用SSLTLS创建安全的Netty程序1.使用Netty创建HTTPHTTPS程序2.Netty的HTTP编码器解码器和编解码器1.HTTP消息聚合2.HTTP压缩3.使用HTTPS4.WebSocket5.SPDY6.处理空闲连接和超时3.解码分隔符和基于长度的协议4.分隔符协议1.长度为基础的协议2.写大数据5.序列化数据6.普通的JDK序列化1.通过JBoss编组序列化2.使用ProtoBuf序列化3.第八章:附带的ChannelHandler和Codec本章介绍使用SSL/
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top