Netty-ChannelHandle的业务处理

ChannelHandle结构

在这里插入图片描述

ChannelHandler基础接口

在这里插入图片描述
  基础接口里面定义的基础通用方法。增加handler,移除handler,异常处理。

ChannelInboundHandler

public interface ChannelInboundHandler extends ChannelHandler {/*** The {@link Channel} of the {@link ChannelHandlerContext} was registered with its {@link EventLoop}*/void channelRegistered(ChannelHandlerContext ctx) throws Exception;/*** The {@link Channel} of the {@link ChannelHandlerContext} was unregistered from its {@link EventLoop}*/void channelUnregistered(ChannelHandlerContext ctx) throws Exception;/*** The {@link Channel} of the {@link ChannelHandlerContext} is now active*/void channelActive(ChannelHandlerContext ctx) throws Exception;/*** The {@link Channel} of the {@link ChannelHandlerContext} was registered is now inactive and reached its* end of lifetime.*/void channelInactive(ChannelHandlerContext ctx) throws Exception;/*** Invoked when the current {@link Channel} has read a message from the peer.*/void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;/*** Invoked when the last message read by the current read operation has been consumed by* {@link #channelRead(ChannelHandlerContext, Object)}.  If {@link ChannelOption#AUTO_READ} is off, no further* attempt to read an inbound data from the current {@link Channel} will be made until* {@link ChannelHandlerContext#read()} is called.*/void channelReadComplete(ChannelHandlerContext ctx) throws Exception;/*** Gets called if an user event was triggered.*/void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;/*** Gets called once the writable state of a {@link Channel} changed. You can check the state with* {@link Channel#isWritable()}.*/void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;/*** Gets called if a {@link Throwable} was thrown.*/@Override@SuppressWarnings("deprecation")void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

  可以看到,channelInboundHandler集成channelhandler接口,新增方法:channelRegistered,channelUnregistered,channelActive,channelInactive,channelRead,channelReadComplete,userEventTriggered,channelWritabilityChanged。

ChannelHandlerAdapter

public abstract class ChannelHandlerAdapter implements ChannelHandler {// Not using volatile because it's used only for a sanity check.boolean added;/*** Throws {@link IllegalStateException} if {@link ChannelHandlerAdapter#isSharable()} returns {@code true}*/protected void ensureNotSharable() {if (isSharable()) {throw new IllegalStateException("ChannelHandler " + getClass().getName() + " is not allowed to be shared");}}/*** Return {@code true} if the implementation is {@link Sharable} and so can be added* to different {@link ChannelPipeline}s.*/public boolean isSharable() {/*** Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a* {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different* {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of* {@link Thread}s are quite limited anyway.** See <a href="https://github.com/netty/netty/issues/2289">#2289</a>.*/Class<?> clazz = getClass();Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();Boolean sharable = cache.get(clazz);if (sharable == null) {sharable = clazz.isAnnotationPresent(Sharable.class);cache.put(clazz, sharable);}return sharable;}/*** Do nothing by default, sub-classes may override this method.*/@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {// NOOP}/*** Do nothing by default, sub-classes may override this method.*/@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {// NOOP}/*** Calls {@link ChannelHandlerContext#fireExceptionCaught(Throwable)} to forward* to the next {@link ChannelHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.** @deprecated is part of {@link ChannelInboundHandler}*/@Skip@Override@Deprecatedpublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.fireExceptionCaught(cause);}
}

  ChannelHandlerAdapter 是一个抽象类,实现接口ChannelHandler。exceptionCaught方法被实现。

ChannelInboundHandlerAdapter

public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {/*** Calls {@link ChannelHandlerContext#fireChannelRegistered()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelRegistered(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelRegistered();}/*** Calls {@link ChannelHandlerContext#fireChannelUnregistered()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelUnregistered(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelUnregistered();}/*** Calls {@link ChannelHandlerContext#fireChannelActive()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelActive();}/*** Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelInactive();}/*** Calls {@link ChannelHandlerContext#fireChannelRead(Object)} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ctx.fireChannelRead(msg);}/*** Calls {@link ChannelHandlerContext#fireChannelReadComplete()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelReadComplete();}/*** Calls {@link ChannelHandlerContext#fireUserEventTriggered(Object)} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {ctx.fireUserEventTriggered(evt);}/*** Calls {@link ChannelHandlerContext#fireChannelWritabilityChanged()} to forward* to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Overridepublic void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {ctx.fireChannelWritabilityChanged();}/*** Calls {@link ChannelHandlerContext#fireExceptionCaught(Throwable)} to forward* to the next {@link ChannelHandler} in the {@link ChannelPipeline}.** Sub-classes may override this method to change behavior.*/@Skip@Override@SuppressWarnings("deprecation")public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)throws Exception {ctx.fireExceptionCaught(cause);}
}

  ChannelInboundHandlerAdapter 是入站的一个落地实现。继承ChannelHandlerAdapter 实现接口ChannelInboundHandler 。默认反应方法实现调用ChannelHandlerContext中的事件发布。

ChannelHandlerContext

public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {/*** Return the {@link Channel} which is bound to the {@link ChannelHandlerContext}.*/Channel channel();/*** Returns the {@link EventExecutor} which is used to execute an arbitrary task.*/EventExecutor executor();/*** The unique name of the {@link ChannelHandlerContext}.The name was used when then {@link ChannelHandler}* was added to the {@link ChannelPipeline}. This name can also be used to access the registered* {@link ChannelHandler} from the {@link ChannelPipeline}.*/String name();/*** The {@link ChannelHandler} that is bound this {@link ChannelHandlerContext}.*/ChannelHandler handler();/*** Return {@code true} if the {@link ChannelHandler} which belongs to this context was removed* from the {@link ChannelPipeline}. Note that this method is only meant to be called from with in the* {@link EventLoop}.*/boolean isRemoved();@OverrideChannelHandlerContext fireChannelRegistered();@OverrideChannelHandlerContext fireChannelUnregistered();@OverrideChannelHandlerContext fireChannelActive();@OverrideChannelHandlerContext fireChannelInactive();@OverrideChannelHandlerContext fireExceptionCaught(Throwable cause);@OverrideChannelHandlerContext fireUserEventTriggered(Object evt);@OverrideChannelHandlerContext fireChannelRead(Object msg);@OverrideChannelHandlerContext fireChannelReadComplete();@OverrideChannelHandlerContext fireChannelWritabilityChanged();@OverrideChannelHandlerContext read();@OverrideChannelHandlerContext flush();/*** Return the assigned {@link ChannelPipeline}*/ChannelPipeline pipeline();/*** Return the assigned {@link ByteBufAllocator} which will be used to allocate {@link ByteBuf}s.*/ByteBufAllocator alloc();/*** @deprecated Use {@link Channel#attr(AttributeKey)}*/@Deprecated@Override<T> Attribute<T> attr(AttributeKey<T> key);/*** @deprecated Use {@link Channel#hasAttr(AttributeKey)}*/@Deprecated@Override<T> boolean hasAttr(AttributeKey<T> key);
}

  ChannelHandlerContext 继承ChannelInboundInvoker,ChannelOutboundInvoker。可以实现入站,出站事件的处理。

ChannelInboundInvoker

public interface ChannelInboundInvoker {/*** A {@link Channel} was registered to its {@link EventLoop}.** This will result in having the  {@link ChannelInboundHandler#channelRegistered(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelRegistered();/*** A {@link Channel} was unregistered from its {@link EventLoop}.** This will result in having the  {@link ChannelInboundHandler#channelUnregistered(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelUnregistered();/*** A {@link Channel} is active now, which means it is connected.** This will result in having the  {@link ChannelInboundHandler#channelActive(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelActive();/*** A {@link Channel} is inactive now, which means it is closed.** This will result in having the  {@link ChannelInboundHandler#channelInactive(ChannelHandlerContext)} method* called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelInactive();/*** A {@link Channel} received an {@link Throwable} in one of its inbound operations.** This will result in having the  {@link ChannelInboundHandler#exceptionCaught(ChannelHandlerContext, Throwable)}* method  called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireExceptionCaught(Throwable cause);/*** A {@link Channel} received an user defined event.** This will result in having the  {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)}* method  called of the next  {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireUserEventTriggered(Object event);/*** A {@link Channel} received a message.** This will result in having the {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)}* method  called of the next {@link ChannelInboundHandler} contained in the  {@link ChannelPipeline} of the* {@link Channel}.*/ChannelInboundInvoker fireChannelRead(Object msg);/*** Triggers an {@link ChannelInboundHandler#channelReadComplete(ChannelHandlerContext)}* event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.*/ChannelInboundInvoker fireChannelReadComplete();/*** Triggers an {@link ChannelInboundHandler#channelWritabilityChanged(ChannelHandlerContext)}* event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.*/ChannelInboundInvoker fireChannelWritabilityChanged();
}

  入站事件触发

ChannelOutboundInvoker

public interface ChannelOutboundInvoker {/*** Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method* called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture bind(SocketAddress localAddress);/*** Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.* <p>* If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with* a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException}* will be used.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress);/*** Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress);/*** Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture disconnect();/*** Request to close the {@link Channel} and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of* an error.** After it is closed it is not possible to reuse it again.* <p>* This will result in having the* {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture close();/*** Request to deregister from the previous assigned {@link EventExecutor} and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.* <p>* This will result in having the* {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.**/ChannelFuture deregister();/*** Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.** The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method* called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);/*** Request to connect to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation* completes, either because the operation was successful or because of an error.** The given {@link ChannelFuture} will be notified.** <p>* If the connection fails because of a connection timeout, the {@link ChannelFuture} will get failed with* a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException}* will be used.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);/*** Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.** The given {@link ChannelPromise} will be notified and also returned.* <p>* This will result in having the* {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);/*** Request to disconnect from the remote peer and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of an error.** The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture disconnect(ChannelPromise promise);/*** Request to close the {@link Channel} and notify the {@link ChannelFuture} once the operation completes,* either because the operation was successful or because of* an error.** After it is closed it is not possible to reuse it again.* The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture close(ChannelPromise promise);/*** Request to deregister from the previous assigned {@link EventExecutor} and notify the* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of* an error.** The given {@link ChannelPromise} will be notified.* <p>* This will result in having the* {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelFuture deregister(ChannelPromise promise);/*** Request to Read data from the {@link Channel} into the first inbound buffer, triggers an* {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)} event if data was* read, and triggers a* {@link ChannelInboundHandler#channelReadComplete(ChannelHandlerContext) channelReadComplete} event so the* handler can decide to continue reading.  If there's a pending read operation already, this method does nothing.* <p>* This will result in having the* {@link ChannelOutboundHandler#read(ChannelHandlerContext)}* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the* {@link Channel}.*/ChannelOutboundInvoker read();/*** Request to write a message via this {@link ChannelHandlerContext} through the {@link ChannelPipeline}.* This method will not request to actual flush, so be sure to call {@link #flush()}* once you want to request to flush all pending data to the actual transport.*/ChannelFuture write(Object msg);/*** Request to write a message via this {@link ChannelHandlerContext} through the {@link ChannelPipeline}.* This method will not request to actual flush, so be sure to call {@link #flush()}* once you want to request to flush all pending data to the actual transport.*/ChannelFuture write(Object msg, ChannelPromise promise);/*** Request to flush all pending messages via this ChannelOutboundInvoker.*/ChannelOutboundInvoker flush();/*** Shortcut for call {@link #write(Object, ChannelPromise)} and {@link #flush()}.*/ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);/*** Shortcut for call {@link #write(Object)} and {@link #flush()}.*/ChannelFuture writeAndFlush(Object msg);/*** Return a new {@link ChannelPromise}.*/ChannelPromise newPromise();/*** Return an new {@link ChannelProgressivePromise}*/ChannelProgressivePromise newProgressivePromise();/*** Create a new {@link ChannelFuture} which is marked as succeeded already. So {@link ChannelFuture#isSuccess()}* will return {@code true}. All {@link FutureListener} added to it will be notified directly. Also* every call of blocking methods will just return without blocking.*/ChannelFuture newSucceededFuture();/*** Create a new {@link ChannelFuture} which is marked as failed already. So {@link ChannelFuture#isSuccess()}* will return {@code false}. All {@link FutureListener} added to it will be notified directly. Also* every call of blocking methods will just return without blocking.*/ChannelFuture newFailedFuture(Throwable cause);/*** Return a special ChannelPromise which can be reused for different operations.* <p>* It's only supported to use* it for {@link ChannelOutboundInvoker#write(Object, ChannelPromise)}.* </p>* <p>* Be aware that the returned {@link ChannelPromise} will not support most operations and should only be used* if you want to save an object allocation for every write operation. You will not be able to detect if the* operation  was complete, only if it failed as the implementation will call* {@link ChannelPipeline#fireExceptionCaught(Throwable)} in this case.* </p>* <strong>Be aware this is an expert feature and should be used with care!</strong>*/ChannelPromise voidPromise();
}

  出站事件触发

AbstractChannelHandlerContext

  AbstractChannelHandlerContext是一个抽象类,实现ChannelHandlerContext。
  在这个抽象类里面,fire开头方法和invoke开头方法。
  1. fire开头方法,实现也是调用invoke开头方法。只不过入参的channelHandlerContext通过方法findContextInbound查询下一个入站或者出站的方法。例如FireChannelRead方法:

   @Overridepublic ChannelHandlerContext fireChannelRead(final Object msg) {invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);return this;}private AbstractChannelHandlerContext findContextInbound(int mask) {AbstractChannelHandlerContext ctx = this;EventExecutor currentExecutor = executor();do {ctx = ctx.next;} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));return ctx;}

  2. invoke开头方法是静态方法,执行对应事件的方法。里面有个逻辑,获取当前context中的EventExecutor是不是NioEventLoop线程,是的话直接执行方法调用,不是的话,添加到NioEventLoop任务队列中。例如invokeChannelRead方法:

    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeChannelRead(m);} else {executor.execute(new Runnable() {@Overridepublic void run() {next.invokeChannelRead(m);}});}}

ChannelPipeline

  ChannelPipeline也实现了ChannelInboundInvoker,ChannelOutboundInvoker接口。具有入站出站事件执行的能力。ChannelHandlerContext也实现同样的接口。他们的区别是。ChannelHandlerContext方法执行是file开头对应的方法。ChannelPipeline方法执行调用的是ChannelHandlerContext中的invoke方法,入站是方法传入ChannelPipeline的head节点,出站方法传入ChannelPipeline的tail节点。
  以下是ChannelPipeline的默认实现DefaultChannelPipeline,中fire方法实现。可以看到调用的就是ChannelHandlerContext中的静态invoke开头方法。
在这里插入图片描述

Inbound入站事件生命周期

  在Netty中,ChannelHandler的生命周期与Channel的状态紧密相关,主要涉及到以下几个回调方法:

  1. handlerAdded: 当一个新的ChannelHandler被添加到ChannelPipeline时调用。
  2. channelRegistered: 当Channel成功注册到EventLoop上时调用。
  3. channelActive: 当Channel激活,可以开始接收和发送数据时调用。
  4. channelRead: 当从Channel中读取到数据时调用。
  5. channelReadComplete: 当一次读取操作完成时调用。
  6. channelInactive: 当Channel变为非激活状态时调用。
  7. channelUnregistered: 当Channel从EventLoop上注销时调用。
  8. handlerRemoved: 当ChannelHandler从ChannelPipeline中移除时调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/440035.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

定制红酒:为您的爱情、友情、亲情定制专属红酒

红酒&#xff0c;这种充满浪漫与情感的饮品&#xff0c;早已超越了单纯的味觉享受&#xff0c;成为人们表达情感、传递心意的载体。云仓酒庄洒派定制红酒&#xff0c;正是为那些珍视爱情、友情、亲情的人们提供了一种表达情感的新方式。 ① 爱情之酒 当你们即将步入婚姻的礼堂…

Python qt.qpa.xcb: could not connect to display解决办法

遇到问题&#xff1a;qt.qpa.xcb: could not connect to display 解决办法&#xff0c;在命令行输入&#xff1a; export DISPLAY:0 然后重新跑python程序&#xff0c;解决&#xff01; 参考博客&#xff1a;qt.qpa.xcb: could not connect to displayqt.qpa.plugin: Could …

GC8549——应用于打印机,白色家电等产品中,12V,大电流,短地短电源保护,可替代LV8548/LV8549/ONSEMI

GC8549 是一款双通道 12V 直流电机驱动芯片&#xff0c;为摄像机、消费类产品、玩具和其他低压或者电池供电的运动控制类应用提供了集成的电机驱动解决方案。芯片一般用来驱动两个直流电机或者驱动一个步进电机。可以工作在 3.8~12V 的电源电压上&#xff0c;每通道能提供高达 …

Utreexo:优化Bitcoin UTXO集合的基于哈希的动态累加器

1. 引言 前序博客&#xff1a; Utreexo&#xff1a;比特币UTXO merkle tree proof以节约节点存储空间 MIT Digital Currency Initiative 的 Thaddeus Dryja 2019年论文 Utreexo: A dynamic hash-based accumulator optimized for the Bitcoin UTXO set。 开源代码实现见&…

Hive(15)中使用sum() over()实现累积求和和滑动求和

目的&#xff1a; 三个常用的排序函数row_number(),rank()和dense_rank()。这三个函数需要配合开窗函数over()来实现排序功能。但over()的用法远不止于此&#xff0c;本文咱们来介绍如何实现累计求和和滑动求和。 1、数据介绍 三列数据&#xff0c;分别是员工的姓名、月份和…

React16源码: React中处理LegacyContext相关的源码实现

LegacyContext 老的 contextAPI 也就是我们使用 childContextTypes 这种声明方式来从父节点为它的子树提供 context 内容的这么一种方式遗留的contextAPI 在 react 17 被彻底移除了&#xff0c;就无法使用了那么为什么要彻底移除这个contextAPI的使用方式呢&#xff1f;因为它…

leetcode 27.移除元素(python版)

需求 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度…

河南郑州:“车辆医生”——检修列车 护航春运

&#xff08;通讯员&#xff1a;刘雨 李子豪&#xff09;1月26日&#xff0c;铁路系统的“春运大考”缓缓拉开帷幕。在中国铁路郑州局集团有限公司郑州车辆段郑州客整所内&#xff0c;一列列精检细修的客车整装待发。繁忙的客整所内&#xff0c;穿着工装、拿着检车锤的客车检车…

亚马逊、速卖通、虾皮等平台测评自养号,虚拟信用卡的使用

在上一篇文章中&#xff0c;我提到了一个完善的自养号测评系统需要具备的几个要求。在这篇文章中&#xff0c;我将重点探讨支付方式虚拟信用卡的问题&#xff0c;以及如何避免信用卡关联。 首先&#xff0c;需要了解信用卡关联风控机制的存在意义。在跨境电商亚马逊平台上&…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷9

某企业根据自身业务需求&#xff0c;实施数字化转型&#xff0c;规划和建设数字化平台&#xff0c;平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”&#xff0c;拟采用开源OpenStack搭建企业内部私有云平台&#xff0c;开源Kubernetes搭建云原生服务平台&#xff0c;选…

长度最小的子数组[中等]

一、题目 给定一个含有n个正整数的数组和一个正整数target。找出该数组中满足其总和大于等于target的长度最小的连续子数组[numsl, numsl1, ..., numsr-1, numsr]&#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回0。 示例 1&#xff1a; 输入&#xff…

C 练习实例51-学习使用按位与

按位与、或、异或 1.按位与 & 0&00 0&10 1&00 1&11 2.按位或 | 0|00 0|11 1|01 1|11 3.按位异或 ^ 0^00 0^11 1^01 1^10 (只要学过离散数学懂的都懂) &#xff08;都是双目运…