上面的文章已经说过了decoder,它的用处是将读取的byte数据转化为用户自己定义的数据类型
这一篇文章要说的encoder,它的作用与前面提到的decoder就相反了,用于将用户定义的类型转化为byte类型,这样才能通过channel发送出去。。。
我们还是先来看看它的继承体系吧:
可以看到它继承自messageadapter,这种类型我们在前面已经有过说明,adapter的作用就是比较粗糙的实现方法,用户可以继承它然后重写自己感兴趣的方法,
而且在前面我们知道在pipeline上面调用write方法的时候,netty会从pipeline的后面向前寻找合适的outboundhandler用于处理要写的数据,而且是先将数据存放到handler的buffer里面,真正的写数据则是调用flush方法实现的。。。
首先我们要来看看这里将会调用的flush方法:
@Override
public final void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ChannelHandlerUtil.handleFlush(ctx, promise, isCloseOnFailedFlush(), this);
}
这里调用了ChannelHandlerUtil的flush方法,我们再来看看这个方法是具体怎么实现的吧:
public static <T> void handleFlush(
ChannelHandlerContext ctx, ChannelPromise promise, boolean closeOnFailedFlush,
SingleOutboundMessageHandler<T> handler) throws Exception {
MessageBuf<Object> in = ctx.outboundMessageBuffer(); //取得当前outbuffer
final int inSize = in.size();
if (inSize == 0) {
ctx.flush(promise);
return;
}
int processed = 0;
try {
if (!handler.beginFlush(ctx)) {
throw new IncompleteFlushException(
"beginFlush(..) rejected the flush request by returning false. " +
"none of " + inSize + " message(s) fulshed.");
}
for (;;) { //一个for循环将数据全部都取出来
Object msg = in.poll();
if (msg == null) {
break;
}
if (!handler.acceptOutboundMessage(msg)) {
addToNextOutboundBuffer(ctx, msg);
processed ++;
continue;
}
@SuppressWarnings("unchecked")
T imsg = (T) msg;
try {
handler.flush(ctx, imsg); //这里将会调用用户定义的flush方法,将message转化为byte类型存到下一个handler
processed ++;
} finally {
BufUtil.release(imsg);
}
}
} catch (Throwable t) {
IncompleteFlushException pfe;
if (t instanceof IncompleteFlushException) {
pfe = (IncompleteFlushException) t;
} else {
String msg = processed + " out of " + inSize + " message(s) flushed";
if (t instanceof Signal) {
Signal abort = (Signal) t;
abort.expect(ABORT);
pfe = new IncompleteFlushException("aborted: " + msg);
} else {
pfe = new IncompleteFlushException(msg, t);
}
}
fail(ctx, promise, closeOnFailedFlush, pfe);
}
try {
handler.endFlush(ctx);
} catch (Throwable t) {
if (promise.isDone()) {
logger.warn("endFlush() raised a masked exception due to failed flush().", t);
} else {
fail(ctx, promise, closeOnFailedFlush, t);
}
}
if (!promise.isDone()) {
ctx.flush(promise); //这里又会向上寻找outboundhandler,把数据flush出去,这里最终将会找到pipeline上面默认的headerhandler
}
}
上面的代码将会调用MessageToByteEncoder类型的flush方法,这个方法将会调用用户定义的encode方法,用于将数据写到下一个handler的buffer里面,最终还会调用当前handler,也就是encoder的context的flush方法,将会向上寻找outboundhandler的flush方法,其实最终会找到默认的headerhandler。。。。
好吧,我们来看看MessageToByteEncoder类型的flush方法的实现吧: @Override
public void flush(ChannelHandlerContext ctx, I msg) throws Exception {
try {
//这里是写到下一个handler的outbuffer里面去
encode(ctx, msg, ctx.nextOutboundByteBuffer());
} catch (CodecException e) {
throw e;
} catch (Exception e) {
throw new CodecException(e);
}
}
这里调用了用户定义的encode方法,不过需要注意的是这里传进去的buffer是下一个outboundhandler的outbuf,这里在复习一下前面的知识:
(1)operationhandler相关的动作都是从pipeline的后面向前进行handler的调用的
(2)在pipeline的最前面有一个netty默认的outboundhandler,它用于真正的将数据发送出去,它里面会包含一个unsafe对象,这个unsafe对象就是所属的channel的unsafe对象,最终会调用这个unsafe对象的相关方法将数据发送出去。。
如果我们要自己编写encoder,那么最终pipeline的构成如下图:
这样,当我们直接调用pipeline的write方法写我们自定义的类型的对象的时候,数据首先是写到了我们自己定义的encoder,然后当flush的时候,decoder会调用我们自己定义的方法将这些数据转化为byte类型,保存到默认的outboundhandler的buffer里面,然后再由其将数据发送出去
好了那么整个encode的流程也就走通了。。