netty NioServerSocketChannel解析

netty ServerBootStrap解析: http://donald-draper.iteye.com/blog/2392572
netty 通道接口定义: http://donald-draper.iteye.com/blog/2392740
netty 抽象通道初始化: http://donald-draper.iteye.com/blog/2392801
netty 抽象Unsafe定义: http://donald-draper.iteye.com/blog/2393053
netty 通道Outbound缓冲区: http://donald-draper.iteye.com/blog/2393098
netty 抽象通道后续: http://donald-draper.iteye.com/blog/2393166
netty 抽象nio通道: http://donald-draper.iteye.com/blog/2393269
netty 抽象nio字节通道: http://donald-draper.iteye.com/blog/2393323
netty 抽象nio消息通道: http://donald-draper.iteye.com/blog/2393364
引言
上一篇我们看了抽象nio消息通道,先来回顾一下:
抽象Nio消息通道AbstractNioMessageChannel,写通道Outbound缓冲区消息,即遍历通道Outbound缓冲区刷新链,当写消息请求为空时,从选择key兴趣集中移除写操作事件,否则,委托doWriteMessage方法,将消息写到底层通道,doWriteMessage方法待子类扩展,写完,将写请求从刷新链上移除,否则,如果需要,添加写事件到选择key的兴趣事件集。

nio消息Unsafe(NioMessageUnsafe)读操作,从通道接收缓冲区读取数据,通知通道处理读取数据,触发Channel管道线的fireChannelRead事件,待数据读取完毕,触发Channel管道线的fireChannelReadComplete事件,如果在读数据的过程中,通道关闭,则触发通道输入关闭事件(fireUserEventTriggered),如果在读数据的过程中,发生异常,则触发通道fireExceptionCaught事件,如果读任务完毕,且不需自动读,则从选择key兴趣事件集移除读操作事件

今天终于到我们的目的了nio 服务端socket通道,NioServerSocketChannel,
package io.netty.channel.socket.nio;

import io.netty.channel.ChannelException;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.util.internal.SocketUtils;
import io.netty.channel.nio.AbstractNioMessageChannel;
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
import io.netty.channel.socket.ServerSocketChannelConfig;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.List;

/**
 * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses
 * NIO selector based implementation to accept new connections.
 */
public class NioServerSocketChannel extends AbstractNioMessageChannel
                             implements io.netty.channel.socket.ServerSocketChannel {
    private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);//通道元数据
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();//选择器提供者
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
    private final ServerSocketChannelConfig config;//通道配置
}

从上面来看,nio服务端socket通道内部有两个变量,一个为选择器提供者,一个为通道配置。
来看构造
/**
 * Create a new instance
 */
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

/**
 * Create a new instance using the given {@link SelectorProvider}.
 */
public NioServerSocketChannel(SelectorProvider provider) {
    this(newSocket(provider));
}

/**
 * Create a new instance using the given {@link ServerSocketChannel}.
 */
public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    //创建通道配置
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}


来看创建socket通道,
private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        /**
         *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
         *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
         *
         *  See [url=https://github.com/netty/netty/issues/2308]#2308[/url].
	 委托给选择器提供者,打开一个通道
         */
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException(
                "Failed to open a server socket.", e);
    }
}

关于通道配置NioServerSocketChannelConfig,我们nio服务端socket通道的内部方法看完,再来看
回头看通道配置,


来看其他方法:

//获取本地socket地址
@Override
public InetSocketAddress localAddress() {
    return (InetSocketAddress) super.localAddress();
}
//获取通道元数据
@Override
public ChannelMetadata metadata() {
    return METADATA;
}
//获取通道配置
@Override
public ServerSocketChannelConfig config() {
    return config;
}
//判断通道,是否激活,主要通过通道关联socket的isBound方法判断
@Override
public boolean isActive() {
    return javaChannel().socket().isBound();
}
//获取关联socket通道
@Override
protected ServerSocketChannel javaChannel() {
    return (ServerSocketChannel) super.javaChannel();
}
//远端地址为空
@Override
public InetSocketAddress remoteAddress() {
    return null;
}
//安全获取本地socket地址
@Override
protected SocketAddress localAddress0() {
    return SocketUtils.localSocketAddress(javaChannel().socket());
}
//关闭通道
@Override
protected void doClose() throws Exception {
    javaChannel().close();
}

//绑定socket地址
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        //如果jdk版本大于1.7 则使用通道bind方法,绑定socket地址
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        //否则使用通道关联Socket的bind方法,绑定socket地址
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

从上面来看,通道实际绑定socket地址,首先判断jdk版本信息,如果jdk版本大于1.7 则使用通道bind方法,绑定socket地址,
否则为通道关联Socket的bind方法。

@Override
protected int doReadMessages(List<Object> buf) throws Exception {
    //接受通道连接,并创建与客户端交互的socket通道
    SocketChannel ch = SocketUtils.accept(javaChannel());

    try {
        if (ch != null) {
	    //将创建的与客户端交互的socket通道,添加到结果集
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    } catch (Throwable t) {
        logger.warn("Failed to create a new channel from an accepted socket.", t);

        try {
            ch.close();
        } catch (Throwable t2) {
            logger.warn("Failed to close a socket.", t2);
        }
    }

    return 0;
}
//SocketUtils
//安全接受socket连接
 public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
    try {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<SocketChannel>() {
            @Override
            public SocketChannel run() throws IOException {
                return serverSocketChannel.accept();
            }
        });
    } catch (PrivilegedActionException e) {
        throw (IOException) e.getCause();
    }
}


读取的消息委托给谁来处理呢,这要回到SeverBootStrap这篇文章,主要是ServerBootstrapAcceptor。
netty ServerBootStrap解析: http://donald-draper.iteye.com/blog/2392572

下面我们来看简单说一下,从ServerBootStrap初始化通道开始:

下面我们来看初始化通道,这个是重点:
//SeverBootStrap
@Override  
void init(Channel channel) throws Exception {  
    final Map<ChannelOption<?>, Object> options = options0();  
    synchronized (options) {  
        //设置父Server通道选项  
        setChannelOptions(channel, options, logger);  
    }  
  
    final Map<AttributeKey<?>, Object> attrs = attrs0();  
    synchronized (attrs) {  
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {  
            @SuppressWarnings("unchecked")  
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();  
            //设置父Server通道属性  
            channel.attr(key).set(e.getValue());  
        }  
    }  
   //获取Server通道的Channel管道  
    ChannelPipeline p = channel.pipeline();  
    final EventLoopGroup currentChildGroup = childGroup;  
    final ChannelHandler currentChildHandler = childHandler;  
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;  
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs;  
  
    synchronized (childOptions) {  
        currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));  
    }  
  
    synchronized (childAttrs) {  
        currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));  
    }  
  
    p.addLast(new ChannelInitializer<Channel>() {  
        @Override  
        public void initChannel(final Channel ch) throws Exception {  
            final ChannelPipeline pipeline = ch.pipeline();  
            ChannelHandler handler = config.handler();  
            if (handler != null) {  
            //将通道处理器添加到通道内部的Channel管道内  
                pipeline.addLast(handler);  
            }  
            ch.eventLoop().execute(new Runnable() {  
                @Override  
                public void run() {  
            //将Server引导配置监听器添加到通道内部的Channel管道内 ,这个是重点
                    pipeline.addLast(new ServerBootstrapAcceptor(  
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));  
                }  
            });  
        }  
    });  
}  

我们来看引导配置监听器,实际为一个Inbound通道处理器

  
 private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {  
        private final EventLoopGroup childGroup;//与客户端交互通道注册的事件循环组  
        private final ChannelHandler childHandler;//与客户端交互通道的通道处理器  
        private final Entry<ChannelOption<?>, Object>[] childOptions;//与客户端交互通道的选项配置  
        private final Entry<AttributeKey<?>, Object>[] childAttrs;//与客户端交互通道的属性  
        private final Runnable enableAutoReadTask;  
      
        ServerBootstrapAcceptor(  
                final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,  
                Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {  
            this.childGroup = childGroup;  
            this.childHandler = childHandler;  
            this.childOptions = childOptions;  
            this.childAttrs = childAttrs;  
      
            // Task which is scheduled to re-enable auto-read.  
            // It's important to create this Runnable before we try to submit it as otherwise the URLClassLoader may  
            // not be able to load the class because of the file limit it already reached.  
            // 此任务用于开启通道自动读取配置,将会被所在的事件循环调度。  
            // See https://github.com/netty/netty/issues/1328  
            enableAutoReadTask = new Runnable() {  
                @Override  
                public void run() {  
             //开启通道自动读取配置  
                    channel.config().setAutoRead(true);  
                }  
            };  
        }  
       //通道读取操作
        @Override  
        @SuppressWarnings("unchecked")  
        public void channelRead(ChannelHandlerContext ctx, Object msg) {  
            //与客户端交互通道 ,这个就是在nio服务端socket通道中,doReadMessages方法接受客户端连接,
	    //创建的客户端交互socket通道
            final Channel child = (Channel) msg;  
            //配置与客户端交互通道的通道处理器  
            child.pipeline().addLast(childHandler);  
            //配置与客户端交互通道的选项  
            setChannelOptions(child, childOptions, logger);  
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {  
        //配置与客户端交互通道的属性  
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());  
            }  
      
            try {  
        //注册与客户端交互通道到childGroup事件循环组  
                childGroup.register(child).addListener(new ChannelFutureListener() {  
                    @Override  
                    public void operationComplete(ChannelFuture future) throws Exception {  
                        if (!future.isSuccess()) {  
                 //注册失败,则关闭通道  
                            forceClose(child, future.cause());  
                        }  
                    }  
                });  
            } catch (Throwable t) {  
                forceClose(child, t);  
            }  
        }  
         //关闭通道  
        private static void forceClose(Channel child, Throwable t) {  
            child.unsafe().closeForcibly();  
            logger.warn("Failed to register an accepted channel: {}", child, t);  
        }  
      
        @Override  
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
            final ChannelConfig config = ctx.channel().config();  
            if (config.isAutoRead()) {  
                // stop accept new connections for 1 second to allow the channel to recover  
         //发生异常,则停止接受连接请求1秒钟,允许通道恢复  
                // See https://github.com/netty/netty/issues/1328  
                config.setAutoRead(false);  
                ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);  
            }  
            // still let the exceptionCaught event flow through the pipeline to give the user  
            // a chance to do something with it  
     //触发异常  
            ctx.fireExceptionCaught(cause);  
        }  
    }  


从上面来看,doReadMessages方法,实际为当接受客户端的连接请求时,创建一个与客户端交互的socket通道,并添加到读操作结果集中,实际为socket通道集。并将socket通道集交给ServerBootStrap的引导配置监听器ServerBootstrapAcceptor处理,Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。 客户端连接服务端时,首先向服务端发送连接请求数据,服务端接受到连接请求时,创建一个与客户端交互的socket通道。

再来看其他方法
// Unnecessary stuff
//由于服务端通道用于接受客户端的请求,所有不支持连接,写消息,消息过滤等等操作
@Override
protected boolean doConnect(
        SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected void doFinishConnect() throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected SocketAddress remoteAddress0() {
    return null;
}

@Override
protected void doDisconnect() throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected final Object filterOutboundMessage(Object msg) throws Exception {
    throw new UnsupportedOperationException();
}

我们再回到Nio服务端通道配置,
//Nio服务端通道配置, 为NioServerSocketChannel的内部类,这个我们单独列一篇文章来说
private final class NioServerSocketChannelConfig extends DefaultServerSocketChannelConfig {
    private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) {
        super(channel, javaSocket);
    }

    @Override
    protected void autoReadCleared() {
        clearReadPending();
    }
}


总结:

nio服务端socket通道NioServerSocketChannel内部有两个变量,一个为选择器提供者SelectorProvider,一个为通道配置ServerSocketChannelConfig。

通道实际绑定socket地址,首先判断jdk版本信息,如果jdk版本大于1.7 则使用通道bind方法,绑定socket地址,否则为通道关联Socket的bind方法。

doReadMessages方法,实际为当接受客户端的连接请求时,创建一个与客户端交互的socket通道,并添加到读操作结果集中,实际为socket通道集。并将socket通道集交给ServerBootStrap的引导配置监听器ServerBootstrapAcceptor处理,Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。 客户端连接服务端时,首先向服务端发送连接请求数据,服务端接受到连接请求时,创建一个与客户端交互的socket通道。

由于服务端通道用于接受客户端的请求,所有不支持连接,写消息,消息过滤等等操作。

猜你喜欢

转载自donald-draper.iteye.com/blog/2393443