netty4.0.x源码分析—channel

来源:互联网 发布:淘宝没有图片搜索 编辑:程序博客网 时间:2024/05/17 21:59

1、channel总体机构图

nio channel的总体结构图如下:

2、关键类和接口分析

2.1  基于NioServerSocketChannel进行分析

1)Channel

Channel是顶层接口,继承了AttributeMap, ChannelOutboundInvoker, ChannelPropertyAccess, Comparable<Channel>,它作为一个具体IO能力的组件提供给开发者,包括read, write, connect, and bind等操作。另外还提供了Channel配置的功能,以及获取Channel所在的eventloop的功能。


2)AbstractChannel

AbstractChannel实现Channel接口,关键代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private final Channel parent;   
  2. private final long hashCode = ThreadLocalRandom.current().nextLong();  
  3. private final Unsafe unsafe;  
  4. private final DefaultChannelPipeline pipeline;  
  5. private final ChannelFuture succeededFuture = new SucceededChannelFuture(thisnull);  
  6. private final VoidChannelPromise voidPromise = new VoidChannelPromise(thistrue);  
  7. private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(thisfalse);  
  8. private final CloseFuture closeFuture = new CloseFuture(this);  
  9.   
  10. private volatile SocketAddress localAddress;  
  11. private volatile SocketAddress remoteAddress;  
  12. private volatile EventLoop eventLoop;  
  13. private volatile boolean registered;  
  14.   
  15. /** Cache for the string representation of this channel */  
  16. private boolean strValActive;  
  17. private String strVal;<pre name="code" class="java">    /** 
  18.  * Creates a new instance. 
  19.  * 
  20.  * @param parent 
  21.  *        the parent of this channel. {@code null} if there's no parent. 
  22.  */  
  23. protected AbstractChannel(Channel parent) {  
  24.     this.parent = parent;  
  25.     unsafe = newUnsafe();  
  26.     pipeline = new DefaultChannelPipeline(this);  
  27. }  
比较重要的对象是pipeline和unsafe,它们提供对read,write,bind等操作的具体实现。


3)AbstractNioChannel

AbstractNioChannel继承AbstractChannel,从这个类开始涉及到JDK的socket,参考如下关键代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1.     private final SelectableChannel ch;  
  2.     protected final int readInterestOp;  
  3.     private volatile SelectionKey selectionKey;  
  4.     private volatile boolean inputShutdown;  
  5. <pre name="code" class="java">    @Override  
  6.     protected void doRegister() throws Exception {  
  7.         boolean selected = false;  
  8.         for (;;) {  
  9.             try {  
  10.                 selectionKey = javaChannel().register(eventLoop().selector, 0this);  
  11.                 return;  
  12.             } catch (CancelledKeyException e) {  
  13.                 if (!selected) {  
  14.                     // Force the Selector to select now as the "canceled" SelectionKey may still be  
  15.                     // cached and not removed because no Select.select(..) operation was called yet.  
  16.                     eventLoop().selectNow();  
  17.                     selected = true;  
  18.                 } else {  
  19.                     // We forced a select operation on the selector before but the SelectionKey is still cached  
  20.                     // for whatever reason. JDK bug ?  
  21.                     throw e;  
  22.                 }  
  23.             }  
  24.         }  
  25.     }  

  /** * Create a new instance * * @param parent the parent {@link Channel} by which this instance was created. May be {@code null} * @param ch the underlying {@link SelectableChannel} on which it operates * @param readInterestOp the ops to set to receive data from the {@link SelectableChannel} */ protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) { super(parent); this.ch = ch; this.readInterestOp = readInterestOp; try { ch.configureBlocking(false); } catch (IOException e) { try { ch.close(); } catch (IOException e2) { if (logger.isWarnEnabled()) { logger.warn( "Failed to close a partially initialized socket.", e2); } } throw new ChannelException("Failed to enter non-blocking mode.", e); } }从上面的代码可以看出,这里定义真正的Socket Channel(SelectableChannel),关心的事件,注册后的key。将Socket设置为非阻塞,这是所有异步IO的关键,也就是说不管多么好的框架,底层基础还是不会变,可见学好基础的重要性啊,^_^。这里重点要关注一下register函数,这个函数是将Channel和事件循环进行关联的关键。每个事件循环都有一个自己的selector,channel实际上是注册到了相应eventloop的selector中,这也是Nio Socket编程的基础。

从这个类中已经可以看到netty的channel是如何和socket 的nio channel关联的了,以及channel是如何和eventloop关联的了。


4)AbstractNioMessageChannel

这个类继承AbstractNioChannel,主要是提供了一个newUnsafe方法返回NioMessageUnsafe对象的实例(实现read方法)。另外还定义doReadMessages和doWriteMessage两个抽象方法。


5)ServerSocketChannel和ServerChannel

这两个接口主要是定义了一个config方法,以及获取网络地址的方法。


6)NioServerSocketChannel

NioServerSocketChannel继承AbstractNioMessageChannel,实现ServerSocketChannel,它是一个具体类,提供给开发者使用。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses 
  3.  * NIO selector based implementation to accept new connections. 
  4.  */  
  5. public class NioServerSocketChannel extends AbstractNioMessageChannel  
  6.                              implements io.netty.channel.socket.ServerSocketChannel {  
  7.   
  8.     private static final ChannelMetadata METADATA = new ChannelMetadata(false);  
  9.   
  10.     private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);  
  11.   
  12.     private static ServerSocketChannel newSocket() {  
  13.         try {  
  14.             return ServerSocketChannel.open();  
  15.         } catch (IOException e) {  
  16.             throw new ChannelException(  
  17.                     "Failed to open a server socket.", e);  
  18.         }  
  19.     }  
  20.   
  21.     private final ServerSocketChannelConfig config;  
  22.   
  23.     /** 
  24.      * Create a new instance 
  25.      */  
  26.     public NioServerSocketChannel() {  
  27.         super(null, newSocket(), SelectionKey.OP_ACCEPT);  
  28.         config = new DefaultServerSocketChannelConfig(this, javaChannel().socket());  
  29.     }  
  30.   
  31.     @Override  
  32.     protected ServerSocketChannel javaChannel() {  
  33.         return (ServerSocketChannel) super.javaChannel();  
  34.     }  
  35.   
  36.     @Override  
  37.     protected void doBind(SocketAddress localAddress) throws Exception {  
  38.         javaChannel().socket().bind(localAddress, config.getBacklog());  
  39.     }  
  40.   
  41.     @Override  
  42.     protected void doClose() throws Exception {  
  43.         javaChannel().close();  
  44.     }  
  45.   
  46.     @Override  
  47.     protected int doReadMessages(List<Object> buf) throws Exception {  
  48.         SocketChannel ch = javaChannel().accept();  
  49.   
  50.         try {  
  51.             if (ch != null) {  
  52.                 buf.add(new NioSocketChannel(this, ch));  
  53.                 return 1;  
  54.             }  
  55.         } catch (Throwable t) {  
  56.             logger.warn("Failed to create a new channel from an accepted socket.", t);  
  57.   
  58.             try {  
  59.                 ch.close();  
  60.             } catch (Throwable t2) {  
  61.                 logger.warn("Failed to close a socket.", t2);  
  62.             }  
  63.         }  
  64.   
  65.         return 0;  
  66.     }  
  67.   
  68.     // Unnecessary stuff  
  69.     @Override  
  70.     protected boolean doConnect(  
  71.             SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {  
  72.         throw new UnsupportedOperationException();  
  73.     }  
  74.   
  75.     @Override  
  76.     protected void doFinishConnect() throws Exception {  
  77.         throw new UnsupportedOperationException();  
  78.     }  
  79.   
  80.     @Override  
  81.     protected SocketAddress remoteAddress0() {  
  82.         return null;  
  83.     }  
  84.   
  85.     @Override  
  86.     protected void doDisconnect() throws Exception {  
  87.         throw new UnsupportedOperationException();  
  88.     }  
  89.   
  90.     @Override  
  91.     protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {  
  92.         throw new UnsupportedOperationException();  
  93.     }  
  94. }  
从这个具体类中,我们可以看到,调用JDK函数ServerSocketChannel.open();生成了底层ServerSocketChannel对象,将NioServerSocketChannel和ServerSocketChannel相关联,并且传递了感兴趣的事件OP_ACCEPT给父类。实现了doReadMessage函数,实际上就是accept一个SocketChanel。

2.2  基于NioSocketChannel进行分析

在NioServerSocketChannel中介绍过的类和接口,这里不再介绍。其实和NioServerSocketChannel差不多,只是它是基于Byte的。

1)AbstractNioByteChannel

这个类继承AbstractNioChannel,主要也是提供了一个newUnsafe方法返回NioByteUnsafe对象的实例(实现read方法)。另外还定义doReadBytes和doWriteBytes两个抽象方法。


2)SocketChannel

这个接口继承了Channel接口,定义了多个shutdown方法,以及一个parent方法,返回该SocketChannel相应的ServerSocketChannel。


3)NioSocketChannel

这个类继承AbstractNioByteChannel,并且实现SocketChannel接口,是一个具体类,提供给开发者使用。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * {@link io.netty.channel.socket.SocketChannel} which uses NIO selector based implementation. 
  3.  */  
  4. public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {  
  5.   
  6.     private static final ChannelMetadata METADATA = new ChannelMetadata(false);  
  7.   
  8.     private static SocketChannel newSocket() {  
  9.         try {  
  10.             return SocketChannel.open();  
  11.         } catch (IOException e) {  
  12.             throw new ChannelException("Failed to open a socket.", e);  
  13.         }  
  14.     }  
  15.   
  16.     private final SocketChannelConfig config;  
  17.   
  18.     /** 
  19.      * Create a new instance 
  20.      */  
  21.     public NioSocketChannel() {  
  22.         this(newSocket());  
  23.     }  
  24.   
  25.     /** 
  26.      * Create a new instance using the given {@link SocketChannel}. 
  27.      */  
  28.     public NioSocketChannel(SocketChannel socket) {  
  29.         this(null, socket);  
  30.     }  
  31.   
  32.     /** 
  33.      * Create a new instance 
  34.      * 
  35.      * @param parent    the {@link Channel} which created this instance or {@code null} if it was created by the user 
  36.      * @param socket    the {@link SocketChannel} which will be used 
  37.      */  
  38.     public NioSocketChannel(Channel parent, SocketChannel socket) {  
  39.         super(parent, socket);  
  40.         config = new DefaultSocketChannelConfig(this, socket.socket());  
  41.     }  
  42.   
  43.     @Override  
  44.     protected SocketChannel javaChannel() {  
  45.         return (SocketChannel) super.javaChannel();  
  46.     }  
  47.   
  48.     @Override  
  49.     public boolean isActive() {  
  50.         SocketChannel ch = javaChannel();  
  51.         return ch.isOpen() && ch.isConnected();  
  52.     }  
  53.   
  54.     @Override  
  55.     public boolean isInputShutdown() {  
  56.         return super.isInputShutdown();  
  57.     }  
  58.   
  59.     @Override  
  60.     public InetSocketAddress localAddress() {  
  61.         return (InetSocketAddress) super.localAddress();  
  62.     }  
  63.   
  64.     @Override  
  65.     public InetSocketAddress remoteAddress() {  
  66.         return (InetSocketAddress) super.remoteAddress();  
  67.     }  
  68.   
  69.     @Override  
  70.     public boolean isOutputShutdown() {  
  71.         return javaChannel().socket().isOutputShutdown() || !isActive();  
  72.     }  
  73.   
  74.     @Override  
  75.     public ChannelFuture shutdownOutput() {  
  76.         return shutdownOutput(newPromise());  
  77.     }  
  78.   
  79.     @Override  
  80.     public ChannelFuture shutdownOutput(final ChannelPromise promise) {  
  81.         EventLoop loop = eventLoop();  
  82.         if (loop.inEventLoop()) {  
  83.             try {  
  84.                 javaChannel().socket().shutdownOutput();  
  85.                 promise.setSuccess();  
  86.             } catch (Throwable t) {  
  87.                 promise.setFailure(t);  
  88.             }  
  89.         } else {  
  90.             loop.execute(new Runnable() {  
  91.                 @Override  
  92.                 public void run() {  
  93.                     shutdownOutput(promise);  
  94.                 }  
  95.             });  
  96.         }  
  97.         return promise;  
  98.     }  
  99.   
  100.     @Override  
  101.     protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {  
  102.         if (localAddress != null) {  
  103.             javaChannel().socket().bind(localAddress);  
  104.         }  
  105.   
  106.         boolean success = false;  
  107.         try {  
  108.             boolean connected = javaChannel().connect(remoteAddress);  
  109.             if (!connected) {  
  110.                 selectionKey().interestOps(SelectionKey.OP_CONNECT);  
  111.             }  
  112.             success = true;  
  113.             return connected;  
  114.         } finally {  
  115.             if (!success) {  
  116.                 doClose();  
  117.             }  
  118.         }  
  119.     }  
  120.   
  121.     @Override  
  122.     protected void doFinishConnect() throws Exception {  
  123.         if (!javaChannel().finishConnect()) {  
  124.             throw new Error();  
  125.         }  
  126.     }  
  127.   
  128.     @Override  
  129.     protected void doDisconnect() throws Exception {  
  130.         doClose();  
  131.     }  
  132.   
  133.     @Override  
  134.     protected void doClose() throws Exception {  
  135.         javaChannel().close();  
  136.     }  
  137.   
  138.     @Override  
  139.     protected int doReadBytes(ByteBuf byteBuf) throws Exception {  
  140.         return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());  
  141.     }  
  142.   
  143.     @Override  
  144.     protected int doWriteBytes(ByteBuf buf) throws Exception {  
  145.         final int expectedWrittenBytes = buf.readableBytes();  
  146.         final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes);  
  147.         return writtenBytes;  
  148.     }  
  149.   
  150.     @Override  
  151.     protected long doWriteFileRegion(FileRegion region) throws Exception {  
  152.         final long position = region.transfered();  
  153.         final long writtenBytes = region.transferTo(javaChannel(), position);  
  154.         return writtenBytes;  
  155.     }  
  156.   
  157.     @Override  
  158.     protected void doWrite(ChannelOutboundBuffer in) throws Exception {  
  159.         for (;;) {  
  160.             // Do non-gathering write for a single buffer case.  
  161.             final int msgCount = in.size();  
  162.             if (msgCount <= 1) {  
  163.                 super.doWrite(in);  
  164.                 return;  
  165.             }  
  166.   
  167.             // Ensure the pending writes are made of ByteBufs only.  
  168.             ByteBuffer[] nioBuffers = in.nioBuffers();  
  169.             if (nioBuffers == null) {  
  170.                 super.doWrite(in);  
  171.                 return;  
  172.             }  
  173.   
  174.             int nioBufferCnt = in.nioBufferCount();  
  175.             long expectedWrittenBytes = in.nioBufferSize();  
  176.   
  177.             final SocketChannel ch = javaChannel();  
  178.             long writtenBytes = 0;  
  179.             boolean done = false;  
  180.             for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {  
  181.                 final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);  
  182.                 if (localWrittenBytes == 0) {  
  183.                     break;  
  184.                 }  
  185.                 expectedWrittenBytes -= localWrittenBytes;  
  186.                 writtenBytes += localWrittenBytes;  
  187.                 if (expectedWrittenBytes == 0) {  
  188.                     done = true;  
  189.                     break;  
  190.                 }  
  191.             }  
  192.   
  193.             if (done) {  
  194.                 // Release all buffers  
  195.                 for (int i = msgCount; i > 0; i --) {  
  196.                     in.remove();  
  197.                 }  
  198.   
  199.                 // Finish the write loop if no new messages were flushed by in.remove().  
  200.                 if (in.isEmpty()) {  
  201.                     clearOpWrite();  
  202.                     break;  
  203.                 }  
  204.             } else {  
  205.                 // Did not write all buffers completely.  
  206.                 // Release the fully written buffers and update the indexes of the partially written buffer.  
  207.   
  208.                 for (int i = msgCount; i > 0; i --) {  
  209.                     final ByteBuf buf = (ByteBuf) in.current();  
  210.                     final int readerIndex = buf.readerIndex();  
  211.                     final int readableBytes = buf.writerIndex() - readerIndex;  
  212.   
  213.                     if (readableBytes < writtenBytes) {  
  214.                         in.progress(readableBytes);  
  215.                         in.remove();  
  216.                         writtenBytes -= readableBytes;  
  217.                     } else if (readableBytes > writtenBytes) {  
  218.                         buf.readerIndex(readerIndex + (int) writtenBytes);  
  219.                         in.progress(writtenBytes);  
  220.                         break;  
  221.                     } else { // readableBytes == writtenBytes  
  222.                         in.progress(readableBytes);  
  223.                         in.remove();  
  224.                         break;  
  225.                     }  
  226.                 }  
  227.   
  228.                 setOpWrite();  
  229.                 break;  
  230.             }  
  231.         }  
  232.     }  
  233. }  
从代码中可以看出,调用了SocketChannel.open();创建SocketChannel对象,将NioSocketChannel和SocketChannel关联。主要是实现了发送数据的doWrite函数。

3、总结

NioSocketChannel和NioServerSocketChannel这两个具体类是提供给开发者使用的。从上面的分析可以看出,实际上他们底层关联的还是JDK的SocketChannel和ServerSocketChannel。netty的Socket Channel是对JDK的Socket Channel的封装,它将Channel和loop关联,在loop中处理Channel的事件通知。

备注:Channel是netty的核心数据结构,这篇文章只是对Channel的Socket部分进行简单分析,不过通过它基本上已经能够了解netty是如何将它的Channel和上一篇的event关联的,以及它是如何将channel和JDK的channel关联的。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 水龙头起泡器不起泡怎么办 14mm乘8mm残留怎么办 宝宝吃了螺丝冒怎么办 收割机滚筒皮带轮键槽滚了怎么办 微信界面变小了怎么办 拉杆箱螺丝掉了怎么办 洗衣机应急门锁没有拉绳怎么办? 奔驰glc发动机声音大怎么办 淋膜机模具螺丝拧不动怎么办 一字螺丝滑丝了怎么办 螺丝拧歪卡住了怎么办 车牌螺丝拧歪了怎么办 空心墙打膨胀螺丝打不上怎么办 沉孔内六角螺丝滑丝怎么办 内六角螺丝滑了怎么办? 三色灯不变光了怎么办 卧室灯不变色了怎么办 圆柱齿轮减速机噪音大怎么办 轴与套间隙生锈怎么办 汽车停小区被刮怎么办 下楼梯摔跤了 屁股疼 怎么办 剧烈咳嗽震的肚子疼怎么办 饺子粘在盘子上怎么办 生饺子粘在盘子怎么办 饺子粘在案板上怎么办 饺子冷冻粘起了怎么办 冰箱饺子冻住了怎么办 水饺都冻一块了怎么办 wps卸载了后文件打不开怎么办 六角螺母拧滑了怎么办 梅花内六角螺丝扭滑丝了怎么办 眼镜螺丝滑丝了怎么办 大螺丝拆不下来怎么办 一字螺丝扭不动怎么办 带帽的螺丝拧花怎么办 螺丝拧不出来了怎么办 小六角螺丝滑丝怎么办 螺丝拧不下来了怎么办 固定水龙头的螺母扭不动怎么办 小螺丝帽拧花了怎么办 6角螺丝帽拧圆了怎么办