Netty4.1源码 :ChannelOutboundHandler



 

/**
 * Handles an I/O event or intercepts an I/O operation, and forwards it to its next handler in
 * its {@link ChannelPipeline}.
 * 处理IO事件、拦截IO操作或跳转到下一个ChannelHandler处理
 * <h3>子类型</h3>
 * ChannelHandler 本身未提供IO操作的方法,你通常需要实现它的两个子接口:
 *  1.ChannelInboundHandler  处理inbound IO事件
 *  2.ChannelOutboundHandler 处理outbound IO操作。
 *  或者使用方使的Adapter class:
 *   1.ChannelInboundHandlerAdapter 处理inbound IO事件
 *   2.ChannelOutboundHandlerAdapter  处理outbound IO操作。
 *   3.ChannelDuplexHandler 处理inbound IO事件 和 outbound IO事件
 *
 * <h3>The context object</h3>
 * 每一个ChannelHandler 都对应一个独立的ChannelHandlerContext(ChannelHandlerContext持有ChannelHandler) 
 * ChannelHandlerContext 是ChannelPipeline与ChannelHandler 的粘合剂。
 * 事件来源于ChannelPipeline-》ChannelHandlerContext-》 ChannelHandler。io方法
 * 								上一个(或下一个)ChannelHandlerContext-》 ChannelHandler。io方法
 *								上一个(或下一个)ChannelHandlerContext-》 ChannelHandler。io方法
 * ChannelHandlerContext的attr(AttributeKey)可以保存状态信息(线程安全)
 * <h3>使用AttributeKey 保存状态信息</h3>
 *
 *
 * <h4>Using {@link AttributeKey}s</h4>
 *
 * public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
 *     private final AttributeKey<Boolean> auth = AttributeKey.valueOf("auth")};
 *     public void channelRead(ChannelHandlerContext ctx, Message message) {
 *         Attribute<Boolean> attr = ctx.attr(auth);
 *         Channel ch = ctx.channel();
 *         if (message instanceof LoginMessage) {
 *             authenticate((LoginMessage) o);
 *             attr.set(true);
 *         } else (message instanceof GetDataMessage) {
 *             if (Boolean.TRUE.equals(attr.get())) {
 *                 ch.write(fetchSecret((GetDataMessage) o));
 *             } else {
 *                 fail();
 *             }
 *         }
 *     }
 *     ...
 * }
 *
 * <h4>The {@code @Sharable} annotation</h4>
 * 如果一个ChannelHandler被标记@Sharable,该ChannelHandler可以被添加到一个或多个ChannelPipeline多次。
 *  该ChannelHandler必须是线程安全的。
 * 如果一个ChannelHandler未标记@Sharable,每添加到ChannelPipeline之前,需要创建一个新的实例,再添到ChannelPipeline中
 *
 */
public interface ChannelHandler {
        /**
    	 * 当ChannelHandler被添加到ChannelPipeline,并准备好处理IO事件时(register完成后),触发。
	 * 被调用时机: 
	 *  1.  Bootstrap b;b.handler(new ChannelInitializer<SocketChannel>() {//ignore} 当regisger操作(在selector上注
	 *       册channel)完成后触发
	 * 2.channel.pipeline.addxxxx(ChannelHandler)方法时,如果已注册的,则立即触发否则等注册后触发
         **/
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

    /**
	 * 当ChannelHandler被从ChannelPipeline移除时(不处理IO事件)
     */
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

  
    /**
	* 如果一个ChannelHandler被标记@Sharable,该ChannelHandler可以被添加到一个或多个ChannelPipeline多次。
	*  该ChannelHandler必须是线程安全的。
	* 如果一个ChannelHandler未标记@Sharable,每添加到ChannelPipeline之前,需要创建一个新的实例,再添到ChannelPipeline中
	*
    **/
    @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Sharable {
        // no value
    }
}

 

/**
 * {@link ChannelHandler} which will get notified for IO-outbound-operations.
 */
public interface ChannelOutboundHandler extends ChannelHandler {
    /**
     *
     * 绑定到本地端口
     */
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

    /**
     * 连接到远程地址
     *
     */
    void connect(
            ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception;

    /**
     *
     * 网络连接断开
     */
    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    /**
     * 关闭网络连接
     *
     */
    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    /**
	 * 从EventLoop上注销Channel
     */
    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    /**
     * 读取数据
     */
    void read(ChannelHandlerContext ctx) throws Exception;

    /**
	* 写入数据至缓冲区,需要调用Flush才真正写入channel
    **/
    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;

    /**
     *
     * 将写入缓冲区数据发送到对端网络
     */
    void flush(ChannelHandlerContext ctx) throws Exception;
}


 
 /**
 *
 * 一个类实现了ChannelOutboundHandler的所有方法,
 * 实现仅仅是把事件转发给下一个ChannelHandler,即本类不处理任务事件,可以在子类中
 * 覆盖方法实现逻辑。目的:子类可以覆盖需要的方法,其它方法采用默认实现--简单。
 * </p>
 */
public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {

    
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
            ChannelPromise promise) throws Exception {
        ctx.bind(localAddress, promise);
    } 
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception {
        ctx.connect(remoteAddress, localAddress, promise);
    } 
    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
        ctx.disconnect(promise);
    } 
    public void close(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
        ctx.close(promise);
    } 
    public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        ctx.deregister(promise);
    } 
    public void read(ChannelHandlerContext ctx) throws Exception {
        ctx.read();
    } 
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ctx.write(msg, promise);
    } 
    public void flush(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}


/**
 *
 * 对象--》byte 编码器骨架类,扩展了ChannelOutboundHandlerAdapter ,覆盖了write方法,
 * 只处理指定类型的对象:
 *  1.对于指定类型:对象转换byte写入ByteBuf,再交由后续ChannelOutboundHandler处理
 *  2.对于非指定类型:直接交由后续ChannelOutboundHandler处理
 *
 *  其子类需要实现 encode(ChannelHandlerContext ctx, I msg, ByteBuf out) 方法,将I msg 对象
 * 转换为byte 写入ByteBuf out
 *
 * 以下为整型转换为ByteBuf的示例(Integer为指定类型):
 *  
 *     public class IntegerEncoder extends MessageToByteEncoder<Integer> {
 *        
 *         public void encode(ChannelHandlerContextctx, Integer msg, ByteBuf out)
 *                 throws Exception {
 *             out.writeInt(msg);
 *         }
 *     }
 *  
 */
public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf buf = null;
        try {
            if (acceptOutboundMessage(msg)) {//是否为指定类型
               
                I cast = (I) msg;
                buf = allocateBuffer(ctx, cast, preferDirect);
                try {
                    encode(ctx, cast, buf);//对象转换为byte写入buf
                } finally {
                    ReferenceCountUtil.release(cast);
                }

                if (buf.isReadable()) {//如果已转换为ByteBuf(buf可读取),则ByteBuf交由后续ChannelOutboundHandler处理
                    ctx.write(buf, promise);
                } else {//如果对象未写入ByteBuf(buf不可读取),则空的ByteBuf交由后续ChannelOutboundHandler处理
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                }
                buf = null;
            } else {//非指定类型交由后续ChannelOutboundHandler处理
                ctx.write(msg, promise);
            }
        } catch (EncoderException e) {
            throw e;
        } catch (Throwable e) {
            throw new EncoderException(e);
        } finally {
            if (buf != null) {
                buf.release();
            }
        }
    }
    /**
     *  将I msg 对象转换为byte 写入ByteBuf out
     */
    protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
}



/**
 *  对象--》对象 编码器骨架抽像类,扩展了ChannelOutboundHandlerAdapter ,覆盖了write方法,
 * 只处理指定类型的对象:
 *  1.对于指定类型:对象转换一个或多个其它类型,再交由后续ChannelOutboundHandler处理
 *  2.对于非指定类型:直接交由后续ChannelOutboundHandler处理
 *
 * 目的:可以用于复杂对象拆分为更小粒度的简单对象。之后再对简单对象写入byte-->网络流
 * 
 * 其子类需要实现 encode(ChannelHandlerContext ctx, I msg, List<Object> out) 方法,将I msg 对象
 * 转换一个或多个对象 写入List<Object> out
 *
 *  以下为Integer转换为String的编码器示例(Integer为指定类型) :
 *     public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer>{
 *
 *         public void encode({@link ChannelHandlerContext} ctx, {@link Integer} message, List&lt;Object&gt; out)
 *                 throws {@link Exception} {
 *             out.add(message.toString());
 *         }
 *     }
 *
 */
public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {

    

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        CodecOutputList out = null;
        try {
            if (acceptOutboundMessage(msg)) {//是否为指定类型
                out = CodecOutputList.newInstance();
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                try {
                    encode(ctx, cast, out);//对象转换为另一类型写入CodecOutputList out 
                } finally {
                    ReferenceCountUtil.release(cast);
                }

                if (out.isEmpty()) {//如果未写入CodecOutputList out ,则抛出异常
                    out.recycle();
                    out = null;

                    throw new EncoderException(
                            StringUtil.simpleClassName(this) + " must produce at least one message.");
                }
            } else {//非指定类型交由后续ChannelOutboundHandler处理
                ctx.write(msg, promise);
            }
        } catch (EncoderException e) {
            throw e;
        } catch (Throwable t) {
            throw new EncoderException(t);
        } finally {
            if (out != null) {//对解码后的每一个对象,调用后续ChannOutboundHandler处理
                final int sizeMinusOne = out.size() - 1;
                if (sizeMinusOne == 0) {
                    ctx.write(out.get(0), promise);
                } else if (sizeMinusOne > 0) {
                    // Check if we can use a voidPromise for our extra writes to reduce GC-Pressure
                    // See https://github.com/netty/netty/issues/2525
                    ChannelPromise voidPromise = ctx.voidPromise();
                    boolean isVoidPromise = promise == voidPromise;
                    for (int i = 0; i < sizeMinusOne; i ++) {
                        ChannelPromise p;
                        if (isVoidPromise) {
                            p = voidPromise;
                        } else {
                            p = ctx.newPromise();
                        }
                        ctx.write(out.getUnsafe(i), p);
                    }
                    ctx.write(out.getUnsafe(sizeMinusOne), promise);
                }
                out.recycle();
            }
        }
    }

    /**
	 * 将I msg 对象转换一个或多个对象 写入List<Object> out
     */
    protected abstract void encode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
}


/**
 * 字符串编码器
 * 字符串转换为ByteBuf
 * 
 * {@link ChannelPipeline} pipeline = ...;
 *
 * // Decoders
 * pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(80));
 * pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
 *
 * // Encoder
 * pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
 *
 * 设置了以上ChannelOutboundHandler,ctx.write方法就可以直接使用字符串了。
 * 由StringDecoder将其转换为ByteBuf
 * <pre>
 * void channelRead({@link ChannelHandlerContext} ctx, {@link String} msg) {
 *     ch.write("Did you say '" + msg + "'?\n");
 * }
 */
@Sharable
public class StringEncoder extends MessageToMessageEncoder<CharSequence> {
    @Override
    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {
        if (msg.length() == 0) {
            return;
        }
        out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset));
    }
}



/**
 * 字节数组编码器
 * 字节数组转换为ByteBuf 
 * 
 *
 * // Decoders
 * pipeline.addLast("frameDecoder",
 *                  new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
 * pipeline.addLast("bytesDecoder",  new ByteArrayDecoder());
 *
 * // Encoder
 * pipeline.addLast("frameEncoder", new {@link LengthFieldPrepender}(4));
 * pipeline.addLast("bytesEncoder", new {@link ByteArrayEncoder}());
 * </pre>
 * 设置了以上ChannelOutboundHandler,ctx.write方法就可以直接使用字符串了。
 * 由StringDecoder将其转换为ByteBuf
 * <pre>
 * void channelRead({@link ChannelHandlerContext} ctx, byte[] bytes) {
 *     ctx.write(new byte []{0x41,0x42});
 * }
 * </pre>
 */
@Sharable
public class ByteArrayEncoder extends MessageToMessageEncoder<byte[]> {
    @Override
    protected void encode(ChannelHandlerContext ctx, byte[] msg, List<Object> out) throws Exception {
        out.add(Unpooled.wrappedBuffer(msg));
    }
}


/**
 * 编码器 :ByteBuf转换为Base64编码格式的ByteBuf
 * // Decoders
 * pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(80,  Delimiters.nulDelimiter() ));
 * pipeline.addLast("base64Decoder", new   Base64Decoder ());
 *
 * // Encoder
 * pipeline.addLast("base64Encoder", new   Base64Encoder ());
 * </pre>
 */
@Sharable
public class Base64Encoder extends MessageToMessageEncoder<ByteBuf> {
    
    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        out.add(Base64.encode(msg, msg.readerIndex(), msg.readableBytes(), breakLines, dialect));
    }
	
}


/**
 * 编码器 ,在报文头增加长度字段,长度字段值为报文长度。
 * 目的:用于解码时,能分清完整报文
 *
 * 下面使用 new LengthFieldPrepender (2) 编码器对下面12个字节进行编码
 * 
 * +----------------+
 * | "HELLO, WORLD" |
 * +----------------+
 * </pre>
 * 编码后:
 * <pre>
 * +--------+----------------+
 * + 0x000C | "HELLO, WORLD" |
 * +--------+----------------+
 *
 * 解码时使用LengthFieldBasedFrameDecoder 就可以解出完整报文
 */
@Sharable
public class LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf> {
	 /**
     * 构建器
     *
     * @param lengthFieldLength 长度字段占用字节数,只允许 1,2,3,4,8
	 * 
     * @param lengthAdjustment   lengthFieldLength值=实际报文长度基础上或+或-多少字节 ,0 为不调整
     * @param lengthIncludesLengthFieldLength lengthFieldLength值 是否包括长度字段本身的字节数 true or false
     *
     * @throws IllegalArgumentException
     *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
     */
    public LengthFieldPrepender(int lengthFieldLength, int lengthAdjustment, boolean lengthIncludesLengthFieldLength) {
        this(ByteOrder.BIG_ENDIAN, lengthFieldLength, lengthAdjustment, lengthIncludesLengthFieldLength);
    }
}
	
	
	
/**
 * 分割符编码器
 * 
 * 使用基个分割符做为报文间分融标记,默认使用换行符做为分割符
 * 发送报文时,在报文结尾处,增加分割符来标记报文结束
 * // Decoders
 * pipeline.addLast("frameDecoder", new {@link LineBasedFrameDecoder}(80));
 * pipeline.addLast("stringDecoder", new {@link StringDecoder}(CharsetUtil.UTF_8));
 *
 * // Encoder
 * pipeline.addLast("lineEncoder", new LineEncoder(LineSeparator.UNIX, CharsetUtil.UTF_8));
 * </pre>
 
 * <pre>
 * void channelRead({@link ChannelHandlerContext} ctx, {@link String} msg) {
 *     ch.write("Did you say '" + msg + "'?");
 * }
 * </pre>
 */
@Sharable
public class LineEncoder extends MessageToMessageEncoder<CharSequence> {
    /**
     * Creates a new instance with the specified line separator and character set.
     */
    public LineEncoder(LineSeparator lineSeparator, Charset charset) {
        this.charset = ObjectUtil.checkNotNull(charset, "charset");
        this.lineSeparator = ObjectUtil.checkNotNull(lineSeparator, "lineSeparator").value().getBytes(charset);
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {
        ByteBuf buffer = ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset, lineSeparator.length);
        buffer.writeBytes(lineSeparator);
        out.add(buffer);
    }
}

猜你喜欢

转载自java12345678.iteye.com/blog/2355896