netty4.0.x源码分析—channel

转载 2015年07月07日 16:28:58

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

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

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

  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接口,是一个具体类,提供给开发者使用。

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

相关文章推荐

【Quartz】cron 表达式配置

1、Quartz Cron 表达式支持到七个域 1、Quartz Cron 表达式支持到七个域 名称 是否必须 允许值 特殊字符 秒 是 0-59 , -...

springcloud(七):配置中心svn示例和refresh

上一篇springcloud(六):配置中心git示例留了一个小问题,当重新修改配置文件提交后,客户端获取的仍然是修改前的信息,这个问题我们先放下,待会再讲。国内很多公司都使用的svn来做代码的版本控...

netty4.0.x源码分析—event

备注:本文的分析基于netty4.0.9final版本 1、event总体结构图 event总体结构类图如下: 2、event关键类和接口分析 1)基于NioEventLoop对关键类和接口进...

Netty3 源码分析 - Channel

Netty3 源码分析 - Channel 何为通道(Channel)?代表的是一个网络套接字的连接点(nexus)。 一个通道抽象的内容包括: 1)当前通道状态,是否打开,是否绑定等;...

netty源码分析 之三 transport(channel)

在开始学习channel之前,先看下channel如何生成的? channel如何生成? AbstractBootstrap 通过channel方法来设置 channelFactory   ...
  • akfly
  • akfly
  • 2017年01月09日 16:43
  • 164

源码分析-netty-channel-channelFuture

ChannelFuture  The result of an asynchronous {@link Channel} I/O operation. 是异步Channel IO的操作结果。 ...
  • whdblog
  • whdblog
  • 2017年05月06日 13:09
  • 113

Netty源码解读(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《...

Netty源码解读(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《...

muduo源码分析之EventLoop、Channel、Poller的实现

muduo源码分析之 简单的EventLoop、Channel Poller实现

Flume 1.7 源码分析(四)从Source写数据到Channel

5 从Source写数据到Channel5.1 Source部分5.1.1 SourceRunnerSourceRunner就是专门用于运行Source的一个类。 在”物化配置”一节获取配置信...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:netty4.0.x源码分析—channel
举报原因:
原因补充:

(最多只允许输入30个字)