首页 > 编程语言 >Netty源码-07-Channel

Netty源码-07-Channel

时间:2022-11-16 22:35:57浏览次数:69  
标签:Netty NioServerSocketChannel 07 源码 link channel fd public Channel

一 类图关系

在Java的NIO体系中定义了ServerSocketChannel和SocketChannel

Netty为了支持Reactor线程模型和异步编程,自己也实现了与Java中对应的两个实现

  • NioServerSocketChannel
  • NioSocketChannel

从功能职责来看

  • NioServerSocketChannel负责连接数据
  • NioSocketChannel负责读写数据

从工作端来看

  • NioServerSocketChannel对接ServerSocket,仅使用在服务端
  • NioSocketChannel对接Socket,可以使用在服务端,也可以使用在客户端

一般涉及资源开辟都使用懒加载方式,涉及多实现都会通过提供对应Provider或者Factory方式进行创建

二 ChannelFactory

// AbstractBootstrap.java

/**
     * Channel创建工厂->内部阈维护了xxxChannel的默认构造器->触发时机->{@link AbstractBootstrap#initAndRegister()}->newInstance()方式创建xxxChannel实例
     *
     * {@link NioServerSocketChannel#NioServerSocketChannel()}->创建服务端Chanel实例
     * {@link NioSocketChannel#NioSocketChannel()}->创建客户端Channel实例
     *
     * 触发时机分别为:
     * 服务端{@link ServerBootstrap#bind()}
     * 客户端{@link Bootstrap#connect()}
     */
    @SuppressWarnings("deprecation")
    private volatile ChannelFactory<? extends C> channelFactory;

/**
     * 创建channelFactory->等到特定时机->创建SocketChannel
     *
     * 形参指向的类是
     * {@link io.netty.channel.socket.nio.NioServerSocketChannel} 无参构造器是{@link NioServerSocketChannel#NioServerSocketChannel()}
     * {@link io.netty.channel.socket.nio.NioSocketChannel} 无参构造器是{@link NioSocketChannel#NioSocketChannel()}
     *
     * 其作用就是提供SocketChannel的无参构造器 生成ChannelFactory
     */
    public B channel(Class<? extends C> channelClass) { // 指定Channel类型->根据Channel特定实现的无参构造方法->反射创建Channel实例
        return this.channelFactory(new ReflectiveChannelFactory<C>(channelClass)); // NioServerSocket的class对象
    }

在AbstractBootstrap维护了channelFactory,将来在恰当时机可以通过Factory创建需要的实例。

1 channelFactory(...)

// AbstractBootstrap.java
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
        return this.channelFactory((ChannelFactory<C>) channelFactory);
    }

@Deprecated
    public B channelFactory(ChannelFactory<? extends C> channelFactory) { // channelFactory的setter
        if (this.channelFactory != null) throw new IllegalStateException("channelFactory set already");
        /**
         * {@link io.netty.channel.ChannelFactory}本质就是一个Channel工厂->阈维护了一个无参构造器->通过newInstance()创建Channel实例
         *
         * 两个无参构造器:
         * {@link NioServerSocketChannel#NioServerSocketChannel()}->创建服务端Channel实例
         * {@link NioSocketChannel#NioSocketChannel()}->创建客户端Channel实例
         */
        this.channelFactory = channelFactory; // 设置channelFactory属性 将ReflectiveChannelFactory实例赋值给该属性
        return self();
    }

该方法的作用就是个setter方法,负责channelFactory赋值

2 new ReflectiveChannelFactory(...)

// ReflectiveChannelFactory.java
public ReflectiveChannelFactory(Class<? extends T> clazz) {
        try {
            this.constructor = clazz.getConstructor(); // NioServerSocket的class对象
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) + " does not have a public non-arg constructor", e);
        }
    }

@Override
    public T newChannel() {
        try {
            return this.constructor.newInstance(); // 反射调用Channel的无参构造方法创建Channel NioSocketChannel充当客户端功能 它的创建时机在connect()的时候 NioServerSocketChannel充当服务端 它的创建时机在bind()的时候
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }

ReflectiveChannelFactory这个类也很简单,只提供了一个构造方法,需要指定Channel实现类对象,将来调用channelFactory的newChannel就可以通过类对象的无参构造方法反射创建Channel实例对象

三 SelectorProvider

关于SelectorProvider的分析,在Java实现Selector中有涉及。

现在我们只需要关心两个方法的实现

  • openServerSocketChannel
  • openSocketChannel

不同平台的SelectorProvider如下

  • Linux EPollSelectorProvider
  • MacOSX KQueueSelectorProvider
  • Windows WindowsSelectorProvider

它们都继承自SelectorProviderImpl

// SelectorProviderImpl.java
public abstract class SelectorProviderImpl
    extends SelectorProvider
{

    public DatagramChannel openDatagramChannel() throws IOException {
        return new DatagramChannelImpl(this);
    }

    public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {
        return new DatagramChannelImpl(this, family);
    }

    public Pipe openPipe() throws IOException {
        return new PipeImpl(this);
    }

    public abstract AbstractSelector openSelector() throws IOException;

    public ServerSocketChannel openServerSocketChannel() throws IOException {
        return new ServerSocketChannelImpl(this);
    }

    public SocketChannel openSocketChannel() throws IOException {
        return new SocketChannelImpl(this);
    }
}

1 openServerSocketChannel()

// ServerSocketChannelImpl.java
ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
        super(sp);
        this.fd =  Net.serverSocket(true);
        this.fdVal = IOUtil.fdVal(fd);
        this.state = ST_INUSE;
    }

// Net.java
static FileDescriptor serverSocket(boolean stream) {
        return IOUtil.newFD(socket0(isIPv6Available(), stream, true, fastLoopback));
    }

2 openSocketChannel()

// SocketChannelImpl
SocketChannelImpl(SelectorProvider sp) throws IOException {
        super(sp);
        this.fd = Net.socket(true);
        this.fdVal = IOUtil.fdVal(fd);
        this.state = ST_UNCONNECTED;
    }

// Net.java
static FileDescriptor socket(ProtocolFamily family, boolean stream)
        throws IOException {
        boolean preferIPv6 = isIPv6Available() &&
            (family != StandardProtocolFamily.INET);
        return IOUtil.newFD(socket0(preferIPv6, stream, false, fastLoopback));
    }

3 创建OS的Socket

// Net.java
private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse,
                                      boolean fastLoopback);
//Net.c

JNIEXPORT jint JNICALL
Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,
                            jboolean stream, jboolean reuse, jboolean ignored)
{
    int fd;
    int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
    int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET;

    fd = socket(domain, type, 0);
    if (fd < 0) {
        return handleSocketError(env, errno);
    }

    /* Disable IPV6_V6ONLY to ensure dual-socket support */
    if (domain == AF_INET6) {
        int arg = 0;
        if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
                       sizeof(int)) < 0) {
            JNU_ThrowByNameWithLastError(env,
                                         JNU_JAVANETPKG "SocketException",
                                         "Unable to set IPV6_V6ONLY");
            close(fd);
            return -1;
        }
    }

    if (reuse) {
        int arg = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,
                       sizeof(arg)) < 0) {
            JNU_ThrowByNameWithLastError(env,
                                         JNU_JAVANETPKG "SocketException",
                                         "Unable to set SO_REUSEADDR");
            close(fd);
            return -1;
        }
    }

#if defined(__linux__)
    if (type == SOCK_DGRAM) {
        int arg = 0;
        int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP;
        if ((setsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) &&
            (errno != ENOPROTOOPT)) {
            JNU_ThrowByNameWithLastError(env,
                                         JNU_JAVANETPKG "SocketException",
                                         "Unable to set IP_MULTICAST_ALL");
            close(fd);
            return -1;
        }
    }

    /* By default, Linux uses the route default */
    if (domain == AF_INET6 && type == SOCK_DGRAM) {
        int arg = 1;
        if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &arg,
                       sizeof(arg)) < 0) {
            JNU_ThrowByNameWithLastError(env,
                                         JNU_JAVANETPKG "SocketException",
                                         "Unable to set IPV6_MULTICAST_HOPS");
            close(fd);
            return -1;
        }
    }
#endif
    return fd;
}

也就是最终通过系统调用socket()创建了Socket对象,Netty根据OS的fd根据调用封装成Java对象ServerSocketChannel和SocketChannel

四 NioServerSocketChannel无参构造方法

// NioServerSocketChannel.java
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER)); // newSocket触发创建jdk底层ServerSocketChannel实例
    }

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 <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
             */
            return provider.openServerSocketChannel(); // Java根据系统调用socket()创建Socket实例 返回的fd封装成ServerSocketChannel
        } catch (IOException e) {
            throw new ChannelException("Failed to open a server socket.", e);
        }
    }

public NioServerSocketChannel(SelectorProvider provider) {
        this(newSocket(provider));
    }

public NioServerSocketChannel(ServerSocketChannel channel) { // 这个channel就是newSocket(...)创建出来jdk的ServerSocketChannel
        super(null, channel, SelectionKey.OP_ACCEPT); // 调用父类构造器 保存属性 设置ServerSocketChannel的非阻塞模式(系统调用fcntl) 服务端关心的是SelectionKey.OP_ACCEPT事件 等待客户端连接
        /**
         * 创建NioServerSocketChannelConfig实例 保存channel配置信息
         * 每一个NioServerSocketChannel都拥有一个config属性 这个属性存放着NioServerSocketChannel的相关配置
         * 这里创建了一个NioServerSocketChannelConfig对象 并将当前channel和channel对应的java底层的socket对象进行了传入
         * NioServerSocketChannelConfig其实是NioServerSocketChannel的内部类
         */
        this.config = new NioServerSocketChannelConfig(this, this.javaChannel().socket());
    }
// AbstractNioMessageChannel.java
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent, ch, readInterestOp);
    }
// AbstractNioChannel.java
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch; // jdk的channel 绑定jdk底层的ServerSocketChannel netty的channel跟jdk的channel关系是组合关系 在netty的channel中有个jdk的channel成员变量 这个成员变量定义在AbstractNioChannel中
        this.readInterestOp = readInterestOp; // Channel关注的IO事件 NioSocketChannel的OP_READ NioServerSocketChannel关注OP_ACCEPT连接事件
        try {
            ch.configureBlocking(false); // 将jdk的channel设置为非阻塞模式(系统调用fcntl)
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
            }
            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
// AbstractChannel.java
protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = this.newId(); // 给每个channel分配一个唯一id
        /**
         * 每个channel内部都需要一个Unsafe实例 执行偏底层的操作
         * {@link io.netty.channel.nio.NioEventLoop}调用的时候会走到其父类的{@link AbstractNioByteChannel#newUnsafe()}方法
         */
        unsafe = this.newUnsafe();
        pipeline = this.newChannelPipeline(); // 每个channel内部都会创建一个pipeline
    }

五 NioSocketChannel无参构造方法

// NioSocketChannel.java
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider(); // 依赖Jdk版本->依赖OS类型

public NioSocketChannel() {
        this(DEFAULT_SELECTOR_PROVIDER);
    }

public NioSocketChannel(SelectorProvider provider) {
        this(newSocket(provider)); // newSocket创建了jdk底层的SocketChannel
    }

private static SocketChannel 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 SocketChannel.open() otherwise.
             *
             *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
             */
            return provider.openSocketChannel(); // Java根据系统调用socket()创建Socket实例 返回的fd封装成SocketChannel
        } catch (IOException e) {
            throw new ChannelException("Failed to open a socket.", e);
        }
    }

public NioSocketChannel(Channel parent, SocketChannel socket) {
        /**
         * 调用父类构造器 设置属性 设置SocketChannel的非阻塞模式
         * socket代表jdk底层的socketChannel
         * parent的含义在于标识SocketChannel的创建场景
         *     - 客户端主动创建Socket跟服务端Socket通信 parent为null
         *     - 服务端Socket监听到有来自客户端的连接后 会copy服务端Socket的创建参数clone一个新的Socket用于跟客户端通信 parent为服务端Socket
         */
        super(parent, socket);
        /**
         * 实例化内部的NioSocketChannelConfig实例 保存channel的配置信息
         */
        config = new NioSocketChannelConfig(this, socket.socket());
    }
// AbstractNioByteChannel.java
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ); // SocketChannel关心的是OP_READ可读事件
    }
// AbstractNioChannel.java
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch; // jdk的channel 绑定jdk底层的ServerSocketChannel netty的channel跟jdk的channel关系是组合关系 在netty的channel中有个jdk的channel成员变量 这个成员变量定义在AbstractNioChannel中
        this.readInterestOp = readInterestOp; // Channel关注的IO事件 NioSocketChannel关注OP_READ可读事件 NioServerSocketChannel关注OP_ACCEPT连接事件
        try {
            ch.configureBlocking(false); // 将jdk的channel设置为非阻塞模式(系统调用fcntl)
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
            }
            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
// AbstractChannel.java
protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = this.newId(); // 给每个channel分配一个唯一id
        /**
         * 每个channel内部都需要一个Unsafe实例 执行偏底层的操作
         * {@link io.netty.channel.nio.NioEventLoop}调用的时候会走到其父类的{@link AbstractNioByteChannel#newUnsafe()}方法
         */
        unsafe = this.newUnsafe();
        pipeline = this.newChannelPipeline(); // 每个channel内部都会创建一个pipeline
    }

标签:Netty,NioServerSocketChannel,07,源码,link,channel,fd,public,Channel
From: https://www.cnblogs.com/miss-u/p/16897736.html

相关文章

  • Netty源码-09-ServerBootstrapAcceptor
    在ServerBootstrapAcceptor启用之前,此刻Reactor状态应该是NioServerSocketChannel在IO多路复用器上关注着Accept(16)事件pipeline中有4个handlerheadbossHandlerSer......
  • Netty源码-08-ChannelInitializer
    一回顾几个时机点pipeline的初始化用户向pipeline添加ChannelInitializer辅助实例Channel注册到复用器之后回调1pipeline的初始化初始化Channel的时候触发了pipel......
  • Netty源码-10-ChannelFuture
    Netty为了提高系统的吞吐,大量使用异步线程模型一DemopublicclassFutureTest00{publicstaticvoidmain(String[]args)throwsInterruptedException,Execut......
  • Netty源码-01-NioEventLoopGroup
    一定义摘自源码JavaDoc/***The{@linkEventExecutorGroup}isresponsibleforprovidingthe{@linkEventExecutor}'stouse*viaits{@link#next()}method......
  • Netty源码-02-FastThreadLocalThread
    一DemopublicclassFastThreadLocalTest00{privatefinalstaticFastThreadLocal<Long>v=newFastThreadLocal<Long>(){@Overrideprotec......
  • apache启动遇到phpinfo只显示源码问题
    在安装php和apache的时候,会遇到只显示源码的问题网上找了好多帖子都是在改php.ini的东西,但是改了半天也不对,发现我安装的wordpress目录也打不开,所以我认为这就是apache服......
  • 【BOI2007】Ranklist Sorting
    【BOI2007】RanklistSortingDescription有一个长为\(n\)的排列\(p\)每次可以选两个位置\(i,j\),然后将\(p_i\)放到\(j\)的位置上,代价为\(i+j\)求将排列变为降序的最小......
  • AGC007
    我现在板刷AGC有两个目的。一个是为了多刷点题增加点练习量,多见点套路和题型。另一个是模拟赛搬AGC的时候不至于保龄。然后今天模拟赛三个套路题切爽了(虽然挂了不少分)。......
  • 2021-07-17 从零开始的一日HTML
    <!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>狗子的工作站</title></head><body><divalign="left"><img......
  • python源码通过词语标记化器tokenize提取注释并正则匹配测试用例作者名
    提取代码如下importtokenizeimportrewithtokenize.open('readcomment.py')asf:list=[]fortoktype,tok,start,end,lineintokenize.generate_t......