- 浏览: 984456 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
netty Inboudn/Outbound通道Invoker:http://donald-draper.iteye.com/blog/2388233
netty 异步任务-ChannelFuture:http://donald-draper.iteye.com/blog/2388297
netty 管道线定义-ChannelPipeline:http://donald-draper.iteye.com/blog/2388453
netty 默认Channel管道线初始化:http://donald-draper.iteye.com/blog/2388613
netty 默认Channel管道线-添加通道处理器:http://donald-draper.iteye.com/blog/2388726
引言:
上一篇文章我们看了Channel管道线添加通道处理器的过程,先来回顾一下:
添加通道处理器到管道头部,首次检查通道处理器是否为共享模式,如果非共享,且已添加,则抛出异常;检查通道处理器名在管道内,是否存在对应通道处理器上下文,已存在抛出异常;根据事件执行器,处理器名,及处理器,构造处理器上下文;添加处理器上限文到管道上下文链头;如果通道没有注册到事件循环,上下文添加到管道时,创建添加通道处理器回调任务,并将任务添加管道的回调任务链中,当通道注册到事件循环时,触发通道处理器的handlerAdded事件,已注册则创建一个线程,用于调用通道处理器的handlerAdded事件方法,及更新上下文状态为已添加,并交由事件执行器执行;最好调用callHandlerAdded0方法,确保调用通道处理器的handlerAdded事件方法,更新上下文状态为已添加。其他last(添加到管道尾部),before(添加指定上下文的前面),after(添加指定上下文的后面)操作,基本上与addfirst思路基本相同,不同的是添加到管道上下文链的位置。
今天来看一下移除通道处理器:
移除指定类型的通道处理器
上面的方法没有什么好说的,很简单。
从上面可以看出无论是,无论是根据名称,处理器句柄,还是根据类型,都是首先获取对应的
处理器上下文,
移除通道上下文方法,有几点要关注
1.
2.
3.
从上面可看出,实际移除通道处理器上下文为,首先从管道中移除对应的上下文,如果通道已经从事件循环反注册,
则添加移除回调任务到管道回调任务链,否则直接创建线程(触发上下文关联的处理器handlerRemoved事件,
更新上下文状态为已移除),有上下文关联的事件执行器执行。
有了上面的铺垫,下面两个方法应该很好理解
上述中的头部与尾部非为傀儡节点,为实际上下文的第一个和最后一个元素。
再来看一下替换通道处理器操作操作:
从上可以看无论是根据名称,处理器句柄,还是根据类型替换通道处理器,都是首先获取对应的处理器上下文,然后委托给replace(final AbstractChannelHandlerContext ctx, String newName, ChannelHandler newHandler)
方法。
从上面可以看出,其实替换上下文,就是添加新上下文到管道中原始上下文的位置,
并将原始上下文的前驱和后继同时指向新上下文,以便转发剩余的buf内容。
可以简单理解为添加新上下文,移除原始上下文,注意必须先添加,后移除,
因为移除操作会触发channelRead和flush事件,而这些事件处理必须在handlerAdded事件后
总结:
无论是根据名称,处理器句柄,还是根据类型移除通道处理器,都是首先获取对应的处理器上下文,从管道中移除对应的上下文,如果通道已经从事件循环反注册,则添加移除回调任务到管道回调任务链,否则直接创建线程(触发上下文关联的处理器handlerRemoved事件,更新上下文状态为已移除),有上下文关联的事件执行器执行。
无论是根据名称,处理器句柄,还是根据类型替换通道处理器,都是首先获取对应的
处理器上下文,然后添加新上下文到管道中原始上下文的位置,并将原始上下文的前驱和后继同时指向新上下文,以便转发剩余的buf内容;可以简单理解为添加新上下文,移除原始上下文,注意必须先添加,后移除,因为移除操作会触发channelRead和flush事件,而这些事件处理必须在handlerAdded事件后。
netty 异步任务-ChannelFuture:http://donald-draper.iteye.com/blog/2388297
netty 管道线定义-ChannelPipeline:http://donald-draper.iteye.com/blog/2388453
netty 默认Channel管道线初始化:http://donald-draper.iteye.com/blog/2388613
netty 默认Channel管道线-添加通道处理器:http://donald-draper.iteye.com/blog/2388726
引言:
上一篇文章我们看了Channel管道线添加通道处理器的过程,先来回顾一下:
添加通道处理器到管道头部,首次检查通道处理器是否为共享模式,如果非共享,且已添加,则抛出异常;检查通道处理器名在管道内,是否存在对应通道处理器上下文,已存在抛出异常;根据事件执行器,处理器名,及处理器,构造处理器上下文;添加处理器上限文到管道上下文链头;如果通道没有注册到事件循环,上下文添加到管道时,创建添加通道处理器回调任务,并将任务添加管道的回调任务链中,当通道注册到事件循环时,触发通道处理器的handlerAdded事件,已注册则创建一个线程,用于调用通道处理器的handlerAdded事件方法,及更新上下文状态为已添加,并交由事件执行器执行;最好调用callHandlerAdded0方法,确保调用通道处理器的handlerAdded事件方法,更新上下文状态为已添加。其他last(添加到管道尾部),before(添加指定上下文的前面),after(添加指定上下文的后面)操作,基本上与addfirst思路基本相同,不同的是添加到管道上下文链的位置。
今天来看一下移除通道处理器:
移除指定类型的通道处理器
@Override public final ChannelPipeline remove(ChannelHandler handler) { //先获取通道处理器对应的上下文,在委托给上下文移除方法 remove(getContextOrDie(handler)); return this; } //获取处理器对应的上下文 private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) { //实际获取上下文方法 AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler); if (ctx == null) { throw new NoSuchElementException(handler.getClass().getName()); } else { return ctx; } } @Override public final ChannelHandlerContext context(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } AbstractChannelHandlerContext ctx = head.next; for (;;) { if (ctx == null) { return null; } //上下文处理器句柄与handler相同,则返回 if (ctx.handler() == handler) { return ctx; } ctx = ctx.next; } }
//根据名字移除通道处理 @Override public final ChannelHandler remove(String name) { return remove(getContextOrDie(name)).handler(); } private AbstractChannelHandlerContext getContextOrDie(String name) { AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(name); if (ctx == null) { throw new NoSuchElementException(name); } else { return ctx; } } @Override public final ChannelHandlerContext context(String name) { if (name == null) { throw new NullPointerException("name"); } return context0(name); } private AbstractChannelHandlerContext context0(String name) { AbstractChannelHandlerContext context = head.next; while (context != tail) { //上下文名与指定的处理器名相同的返回上下文 if (context.name().equals(name)) { return context; } context = context.next; } return null; }
@SuppressWarnings("unchecked") //移除指定参数类型处理器 @Override public final <T extends ChannelHandler> T remove(Class<T> handlerType) { return (T) remove(getContextOrDie(handlerType)).handler(); } private AbstractChannelHandlerContext getContextOrDie(Class<? extends ChannelHandler> handlerType) { AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handlerType); if (ctx == null) { throw new NoSuchElementException(handlerType.getName()); } else { return ctx; } } @Override public final ChannelHandlerContext context(Class<? extends ChannelHandler> handlerType) { if (handlerType == null) { throw new NullPointerException("handlerType"); } AbstractChannelHandlerContext ctx = head.next; for (;;) { if (ctx == null) { return null; } //如果上下文关联的处理器类型为handlerType,则返回上下文 if (handlerType.isAssignableFrom(ctx.handler().getClass())) { return ctx; } ctx = ctx.next; } }
上面的方法没有什么好说的,很简单。
从上面可以看出无论是,无论是根据名称,处理器句柄,还是根据类型,都是首先获取对应的
处理器上下文,
private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) { //断言移除的上下文既不是头部也不是尾部 assert ctx != head && ctx != tail; synchronized (this) { //移除上下文 remove0(ctx); // If the registered is false it means that the channel was not registered on an eventloop yet. // In this case we remove the context from the pipeline and add a task that will call // ChannelHandler.handlerRemoved(...) once the channel is registered. if (!registered) { //如果通道已经从事件循环反注册,则添加移除回调任务 callHandlerCallbackLater(ctx, false); return ctx; } //获取通道处理器上下文事件执行器 EventExecutor executor = ctx.executor(); if (!executor.inEventLoop()) { //如果事件执行器不在当前事务循环中,则创建线程执行通道处理器移除相关事件及上下文状态更新, //线程委托给上下文事件执行器 executor.execute(new Runnable() { @Override public void run() { callHandlerRemoved0(ctx); } }); return ctx; } } // callHandlerRemoved0(ctx); return ctx; }
移除通道上下文方法,有几点要关注
1.
//移除上下文 remove0(ctx);
private static void remove0(AbstractChannelHandlerContext ctx) { AbstractChannelHandlerContext prev = ctx.prev; AbstractChannelHandlerContext next = ctx.next; prev.next = next; next.prev = prev; }
2.
if (!registered) { //如果通道已经从事件循环反注册,则添加移除回调任务 callHandlerCallbackLater(ctx, false); return ctx; }
//添加移除上下文回调任务到,管道回调任务链 private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) { assert !registered; PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx); PendingHandlerCallback pending = pendingHandlerCallbackHead; if (pending == null) { pendingHandlerCallbackHead = task; } else { // Find the tail of the linked-list. while (pending.next != null) { pending = pending.next; } pending.next = task; } }
private final class PendingHandlerRemovedTask extends PendingHandlerCallback { PendingHandlerRemovedTask(AbstractChannelHandlerContext ctx) { super(ctx); } @Override public void run() { //触发处理移除事件,并更新上下为状态为已移除状态 callHandlerRemoved0(ctx); } @Override void execute() { EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) { callHandlerRemoved0(ctx); } else { //如果移除线程不在当前事务循环中,移除任务交给上下文关联的事件执行器 try { executor.execute(this); } catch (RejectedExecutionException e) { if (logger.isWarnEnabled()) { logger.warn( "Can't invoke handlerRemoved() as the EventExecutor {} rejected it," + " removing handler {}.", executor, ctx.name(), e); } // remove0(...) was call before so just call AbstractChannelHandlerContext.setRemoved(). ctx.setRemoved(); } } } }
3.
//触发处理移除事件,并更新上下为状态为已移除 private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) { // Notify the complete removal. try { try { //触发上下文关联的处理器handlerRemoved事件 ctx.handler().handlerRemoved(ctx); } finally { //更新上下文状态为已移除 ctx.setRemoved(); } } catch (Throwable t) { fireExceptionCaught(new ChannelPipelineException( ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t)); } }
从上面可看出,实际移除通道处理器上下文为,首先从管道中移除对应的上下文,如果通道已经从事件循环反注册,
则添加移除回调任务到管道回调任务链,否则直接创建线程(触发上下文关联的处理器handlerRemoved事件,
更新上下文状态为已移除),有上下文关联的事件执行器执行。
有了上面的铺垫,下面两个方法应该很好理解
//移除管道头部的处理器 @Override public final ChannelHandler removeFirst() { if (head.next == tail) { throw new NoSuchElementException(); } return remove(head.next).handler(); } 移除管道尾部的处理器 @Override public final ChannelHandler removeLast() { if (head.next == tail) { throw new NoSuchElementException(); } return remove(tail.prev).handler(); }
上述中的头部与尾部非为傀儡节点,为实际上下文的第一个和最后一个元素。
再来看一下替换通道处理器操作操作:
@Override public final ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) { replace(getContextOrDie(oldHandler), newName, newHandler); return this; } @Override public final ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) { return replace(getContextOrDie(oldName), newName, newHandler); } @Override @SuppressWarnings("unchecked") public final <T extends ChannelHandler> T replace( Class<T> oldHandlerType, String newName, ChannelHandler newHandler) { return (T) replace(getContextOrDie(oldHandlerType), newName, newHandler); }
从上可以看无论是根据名称,处理器句柄,还是根据类型替换通道处理器,都是首先获取对应的处理器上下文,然后委托给replace(final AbstractChannelHandlerContext ctx, String newName, ChannelHandler newHandler)
方法。
private ChannelHandler replace( final AbstractChannelHandlerContext ctx, String newName, ChannelHandler newHandler) { assert ctx != head && ctx != tail; final AbstractChannelHandlerContext newCtx; synchronized (this) { checkMultiplicity(newHandler); if (newName == null) { newName = generateName(newHandler); } else { boolean sameName = ctx.name().equals(newName); if (!sameName) { checkDuplicateName(newName); } } //创建新处理器的上下文 newCtx = newContext(ctx.executor, newName, newHandler); //用新的处理上下文,替换管道上下文链中原始旧的上下文 replace0(ctx, newCtx); // If the registered is false it means that the channel was not registered on an eventloop yet. // In this case we replace the context in the pipeline // and add a task that will call ChannelHandler.handlerAdded(...) and // ChannelHandler.handlerRemoved(...) once the channel is registered. if (!registered) { //添加新上下文的添加回调任务 callHandlerCallbackLater(newCtx, true); //添加旧上下文的移除回调任务 callHandlerCallbackLater(ctx, false); return ctx.handler(); } EventExecutor executor = ctx.executor(); if (!executor.inEventLoop()) { executor.execute(new Runnable() { //如果 @Override public void run() { //先添加,后移除,因为移除操作会触发channelRead和flush事件,而这些事件处理必须在handlerAdded事件后 // Invoke newHandler.handlerAdded() first (i.e. before oldHandler.handlerRemoved() is invoked) // because callHandlerRemoved() will trigger channelRead() or flush() on newHandler and // those event handlers must be called after handlerAdded(). //更新新上下文对应的处理器的handlerAdded,并更新,新上下状态为已添加 callHandlerAdded0(newCtx); //更新旧上下文对应的处理器的handlerRemoved,并更新,新旧上下状态为已添加 callHandlerRemoved0(ctx); } }); return ctx.handler(); } } // Invoke newHandler.handlerAdded() first (i.e. before oldHandler.handlerRemoved() is invoked) // because callHandlerRemoved() will trigger channelRead() or flush() on newHandler and those // event handlers must be called after handlerAdded(). callHandlerAdded0(newCtx); callHandlerRemoved0(ctx); return ctx.handler(); } //替换上下文 private static void replace0(AbstractChannelHandlerContext oldCtx, AbstractChannelHandlerContext newCtx) { AbstractChannelHandlerContext prev = oldCtx.prev; AbstractChannelHandlerContext next = oldCtx.next; newCtx.prev = prev; newCtx.next = next; // Finish the replacement of oldCtx with newCtx in the linked list. // Note that this doesn't mean events will be sent to the new handler immediately // because we are currently at the event handler thread and no more than one handler methods can be invoked // at the same time (we ensured that in replace().) prev.next = newCtx; next.prev = newCtx; // update the reference to the replacement so forward of buffered content will work correctly //主要原始上下文的前驱和后继同时指向新上下文,以便转发剩余的buf内容 oldCtx.prev = newCtx; oldCtx.next = newCtx; }
从上面可以看出,其实替换上下文,就是添加新上下文到管道中原始上下文的位置,
并将原始上下文的前驱和后继同时指向新上下文,以便转发剩余的buf内容。
可以简单理解为添加新上下文,移除原始上下文,注意必须先添加,后移除,
因为移除操作会触发channelRead和flush事件,而这些事件处理必须在handlerAdded事件后
总结:
无论是根据名称,处理器句柄,还是根据类型移除通道处理器,都是首先获取对应的处理器上下文,从管道中移除对应的上下文,如果通道已经从事件循环反注册,则添加移除回调任务到管道回调任务链,否则直接创建线程(触发上下文关联的处理器handlerRemoved事件,更新上下文状态为已移除),有上下文关联的事件执行器执行。
无论是根据名称,处理器句柄,还是根据类型替换通道处理器,都是首先获取对应的
处理器上下文,然后添加新上下文到管道中原始上下文的位置,并将原始上下文的前驱和后继同时指向新上下文,以便转发剩余的buf内容;可以简单理解为添加新上下文,移除原始上下文,注意必须先添加,后移除,因为移除操作会触发channelRead和flush事件,而这些事件处理必须在handlerAdded事件后。
发表评论
-
netty NioSocketChannel解析
2017-09-29 12:50 1324netty 抽象BootStrap定义:http://dona ... -
netty Pooled字节buf分配器
2017-09-28 13:00 2063netty 字节buf定义:http://donald-dra ... -
netty Unpooled字节buf分配器
2017-09-26 22:01 2453netty 字节buf定义:http://donald-dra ... -
netty 抽象字节buf分配器
2017-09-26 08:43 1320netty 字节buf定义:http:// ... -
netty 复合buf概念
2017-09-25 22:31 1319netty 字节buf定义:http://donald-dra ... -
netty 抽象字节buf引用计数器
2017-09-22 12:48 1599netty 字节buf定义:http://donald-dra ... -
netty 抽象字节buf解析
2017-09-22 09:00 1848netty 通道接口定义:http://donald-drap ... -
netty 资源泄漏探测器
2017-09-21 09:37 1400netty 通道接口定义:http://donald-drap ... -
netty 字节buf定义
2017-09-20 08:31 2839netty 通道接口定义:http://donald-drap ... -
netty 默认通道配置后续
2017-09-18 08:36 2186netty 通道接口定义:http://donald-drap ... -
netty 默认通道配置初始化
2017-09-17 22:51 2041netty 通道接口定义:http://donald-drap ... -
netty 通道配置接口定义
2017-09-17 14:51 1082netty 通道接口定义:http://donald-drap ... -
netty NioServerSocketChannel解析
2017-09-16 13:01 1879netty ServerBootStrap解析:http:// ... -
netty 抽象nio消息通道
2017-09-15 15:30 1221netty 通道接口定义:http:/ ... -
netty 抽象nio字节通道
2017-09-14 22:39 1205netty 通道接口定义:http:/ ... -
netty 抽象nio通道解析
2017-09-14 17:23 959netty 通道接口定义:http://donald-drap ... -
netty 抽象通道后续
2017-09-13 22:40 1313netty Inboudn/Outbound通道Inv ... -
netty 通道Outbound缓冲区
2017-09-13 14:31 2193netty 通道接口定义:http:/ ... -
netty 抽象Unsafe定义
2017-09-12 21:24 1084netty 通道接口定义:http:/ ... -
netty 抽象通道初始化
2017-09-11 12:56 1859netty 管道线定义-ChannelPipeline:htt ...
相关推荐
赠送jar包:netty-transport-native-unix-common-4.1.73.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.73....
赠送jar包:netty-transport-native-unix-common-4.1.74.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.74....
赠送jar包:netty-transport-native-unix-common-4.1.73.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.73....
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
赠送jar包:netty-transport-native-unix-common-4.1.68.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.68.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.68....
赠送jar包:netty-transport-classes-epoll-4.1.74.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.74.Final-sources.jar;...
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
赠送jar包:reactor-netty-core-1.0.15.jar; 赠送原API文档:reactor-netty-core-1.0.15-javadoc.jar; 赠送源代码:reactor-netty-core-1.0.15-sources.jar; 赠送Maven依赖信息文件:reactor-netty-core-1.0.15....
赠送jar包:netty-resolver-dns-4.1.65.Final.jar; 赠送原API文档:netty-resolver-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-resolver-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:reactor-netty-http-1.0.11.jar; 赠送原API文档:reactor-netty-http-1.0.11-javadoc.jar; 赠送源代码:reactor-netty-http-1.0.11-sources.jar; 赠送Maven依赖信息文件:reactor-netty-...
赠送jar包:netty-codec-mqtt-4.1.74.Final.jar; 赠送原API文档:netty-codec-mqtt-4.1.74.Final-javadoc.jar; 赠送源代码:netty-codec-mqtt-4.1.74.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-codec-haproxy-4.1.73.Final.jar; 赠送原API文档:netty-codec-haproxy-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-haproxy-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-codec-dns-4.1.65.Final.jar; 赠送原API文档:netty-codec-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-codec-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-dns-...
赠送jar包:netty-codec-dns-4.1.65.Final.jar; 赠送原API文档:netty-codec-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-codec-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-dns-...
赠送jar包:netty-resolver-dns-4.1.65.Final.jar; 赠送原API文档:netty-resolver-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-resolver-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-codec-mqtt-4.1.73.Final.jar; 赠送原API文档:netty-codec-mqtt-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-mqtt-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-resolver-dns-4.1.73.Final.jar; 赠送原API文档:netty-resolver-dns-4.1.73.Final-javadoc.jar; 赠送源代码:netty-resolver-dns-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-codec-http-4.1.27.Final.jar; 赠送原API文档:netty-codec-http-4.1.27.Final-javadoc.jar; 赠送源代码:netty-codec-http-4.1.27.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-tcnative-classes-2.0.46.Final.jar; 赠送原API文档:netty-tcnative-classes-2.0.46.Final-javadoc.jar; 赠送源代码:netty-tcnative-classes-2.0.46.Final-sources.jar; 赠送Maven依赖信息...
赠送jar包:netty-codec-http-4.1.11.Final.jar; 赠送原API文档:netty-codec-http-4.1.11.Final-javadoc.jar; 赠送源代码:netty-codec-http-4.1.11.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...