1、channel总体机构图

nio channel的总体结构图如下:

netty4.0.x源码分析—channel_.net

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接口,关键代码如下:


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(this, null);  
6. private final VoidChannelPromise voidPromise = new VoidChannelPromise(this, true);  
7. private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);  
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. new DefaultChannelPipeline(this);  
27. }

比较重要的对象是pipeline和unsafe,它们提供对read,write,bind等操作的具体实现。


3)AbstractNioChannel

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

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. 0, this);  
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. 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,它是一个具体类,提供给开发者使用。


    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. 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. new NioSocketChannel(this, ch));  
    53. return 1;  
    54.             }  
    55. catch (Throwable t) {  
    56. "Failed to create a new channel from an accepted socket.", t);  
    57.   
    58. try {  
    59.                 ch.close();  
    60. catch (Throwable t2) {  
    61. "Failed to close a socket.", t2);  
    62.             }  
    63.         }  
    64.   
    65. return 0;  
    66.     }  
    67.   
    68. // Unnecessary stuff  
    69. @Override  
    70. protected boolean doConnect(  
    71. 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接口,是一个具体类,提供给开发者使用。


    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. 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. 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. 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. 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. 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关联的。