/** * 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<Object> 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); } }