当前位置:首页 > 开发 > 编程语言 > 网络编程 > 正文

Netty源码学习-ObjectEncoder和ObjectDecoder

发表于: 2013-12-05   作者:bylijinnan   来源:转载   浏览:
摘要: Netty中传递对象的思路很直观: Netty中数据的传递是基于ChannelBuffer(也就是byte[]); 那把对象序列化为字节流,就可以在Netty中传递对象了 相应的从ChannelBuffer恢复对象,就是反序列化的过程 Netty已经封装好ObjectEncoder和ObjectDecoder 先看ObjectEncoder ObjectEncoder是往外发送
Netty中传递对象的思路很直观:
Netty中数据的传递是基于ChannelBuffer(也就是byte[]);
那把对象序列化为字节流,就可以在Netty中传递对象了
相应的从ChannelBuffer恢复对象,就是反序列化的过程

Netty已经封装好ObjectEncoder和ObjectDecoder

先看ObjectEncoder
ObjectEncoder是往外发送对象,因此ObjectEncoder肯定是一个ChannelDownstreamHandler
ObjectEncoder extends OneToOneEncoder,而OneToOneEncoder implements ChannelDownstreamHandler
OneToOneEncoder主要是做两件事情
一是encode
二是把消息发送到紧接着的下一个ChannelDownstreamHandler
其中第一个操作交由子类实现,是一个抽象方法
关键源码如下:
public void handleDownstream(
            ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
        if (!(evt instanceof MessageEvent)) {
            ctx.sendDownstream(evt);
            return;
        }

        MessageEvent e = (MessageEvent) evt;
        Object originalMessage = e.getMessage();
        Object encodedMessage = encode(ctx, e.getChannel(), originalMessage);
        if (originalMessage == encodedMessage) {
            ctx.sendDownstream(evt);
        } else if (encodedMessage != null) {
            write(ctx, e.getFuture(), encodedMessage, e.getRemoteAddress());
        }
}

ObjectEncoder当中实现了encode方法:
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
        ChannelBufferOutputStream bout =
            new ChannelBufferOutputStream(dynamicBuffer(
                    estimatedLength, ctx.getChannel().getConfig().getBufferFactory()));
        bout.write(LENGTH_PLACEHOLDER);
        ObjectOutputStream oout = new CompactObjectOutputStream(bout);
        oout.writeObject(msg);
        oout.flush();
        oout.close();

        ChannelBuffer encoded = bout.buffer();
        encoded.setInt(0, encoded.writerIndex() - 4);
        return encoded;
    }

简单地说就是把对象序列化后,转为ChannelBuffer并返回
注意到,返回的ChannelBuffer的最前面4个字节(int),这个数字,代表了该对象转为字节流后,字节流的长度
因此decode时,就要用到LengthFieldBasedFrameDecoder
所以,ObjectDecoder extends LengthFieldBasedFrameDecoder 就一点也不奇怪了:
public class ObjectDecoder extends LengthFieldBasedFrameDecoder {
               /*lengthFieldLength = 4,initialBytesToStrip = 4(去掉前面4个字节)*/
		public ObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
			super(maxObjectSize, 0, 4, 0, 4);
			this.classResolver = classResolver;
		}
		@Override
		protected Object decode(
				ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
			
			//根据header(length) + content的格式,取得content
			ChannelBuffer frame = (ChannelBuffer) super.decode(ctx, channel, buffer);
			if (frame == null) {
				return null;
			}

			//反序列化
			return new CompactObjectInputStream(
					new ChannelBufferInputStream(frame), classResolver).readObject();
		}

		/*重写了父类的这个方法,避免了memory copy。本方法被decode方法调用
		在LengthFieldBasedFrameDecoder写道:If you are sure that the frame and its content are not accessed after
		the current {@link #decode(ChannelHandlerContext, Channel, ChannelBuffer)}
		call returns, you can even avoid memory copy by returning the sliced
		*/
		@Override
		protected ChannelBuffer extractFrame(ChannelBuffer buffer, int index, int length) {
			return buffer.slice(index, length);
		}

}

分析完毕

如果要自己实现ObjectEncoder和ObjectDecoder,可参考下面这篇文章:
http://www.coderli.com/netty-custom-object-codec

最后看一下Netty里面常用的StringEncoder和StringEncoder

由于String太常用了,因此ChannelBuffers这个工具类,
提供了String和ChannelBuffer的相互转化:
static String decodeString(ByteBuffer src, Charset charset) 
static ByteBuffer encodeString(CharBuffer src, Charset charset)


StringDecoder的decode很简单,ChannelBuffer直接转化为String:
	@Override
    protected Object decode(
            ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
        if (!(msg instanceof ChannelBuffer)) {
            return msg;
        }
        return ((ChannelBuffer) msg).toString(charset);
    }

而((ChannelBuffer) msg).toString(charset)方法最终调用的是:
return ChannelBuffers.decodeString(
                toByteBuffer(index, length), charset);


StringEncoder的encode就麻烦一点
encode最终会调用ChannelsBuffer的copiedBuffer方法:
    private static ChannelBuffer copiedBuffer(ByteOrder endianness, CharBuffer buffer, Charset charset) {
        CharBuffer src = buffer;
        ByteBuffer dst = ChannelBuffers.encodeString(src, charset);
        ChannelBuffer result = wrappedBuffer(endianness, dst.array());
        result.writerIndex(dst.remaining());
        return result;
    }

注意到,调用ChannelBuffers.encodeString返回的ByteBuffer还要转化为ChannelBuffer

Netty源码学习-ObjectEncoder和ObjectDecoder

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
背景 最忌工作中接触到Netty相关应用场景,之前看过mima的部分源码,所以最近看了Netty的部分源码和
类结构图: 不了解Executor接口原理的可以查看concurrent包中的api介绍,这里只介绍Netty中EventExe
Transport API的核心: Channel接口 类图表示Channel含有Pipeline和Config接口,pipeline上一节有所
Netty是一个基于JAVA NIO类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、
先看下netty的channel对象关联关系。channel是由channelfactory来创建的,channelfactory又分为clie
感谢网友【黄亿华】投递本稿。 上一篇文章我们概要介绍了Netty的原理及结构,下面几篇文章我们开始
上一篇文章我们概要介绍了Netty的原理及结构,下面几篇文章我们开始对Netty的各个模块进行比较详细
netty里面最重要的应该是ChannelHandler,这个里面也是用户编程直接打交道的接口,也是串行于Channe
本文采用版本为Jboss Netty-3.2.4.Final,Jboss Netty示例example、几十页的user guide是快速学习的
本文为原创,转载请注明出处 netty 4源码分析-write Netty的写操作由两个步骤组成: Write:将msg存
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号