Netty Review - NioServerSocketChannel源码分析

文章目录

  • 概述
  • 入口
  • NioServerSocketChannel 类继承关系
  • 实例化过程中做的事情
  • 小结

在这里插入图片描述

在这里插入图片描述


概述

在这里插入图片描述


入口

ChannelFuture channelFuture = serverBootstrap.bind(9000).sync();

我们进入bind方法

/**
 * Create a new {@link Channel} and bind it.
 */
public ChannelFuture bind(int inetPort) {
    // 调用bind方法,并传入一个InetSocketAddress对象,其中端口号由参数inetPort指定
    return bind(new InetSocketAddress(inetPort));
}

这个方法的作用是创建一个新的Channel并绑定到指定的端口。它首先创建一个InetSocketAddress对象,该对象包含了要绑定的端口号,然后调用bind方法将新创建的Channel绑定到该InetSocketAddress。

这个方法的参数inetPort表示要绑定的端口号。通过调用bind(int inetPort)方法,可以方便地将新创建的Channel绑定到指定的端口。


final ChannelFuture regFuture = initAndRegister();

继续跟进去会进到 io.netty.bootstrap.AbstractBootstrap#doBind

    private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
 		......
 		......      
}

我们重点关注 initAndRegister

这段代码是NioServerSocketChannel类中的一个私有方法,用于初始化并注册一个新的Channel。让我们逐步解释它:

```java
final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        // 使用ChannelFactory创建一个新的Channel实例
        channel = channelFactory.newChannel();
        // 初始化Channel,包括设置Channel的配置、分配Channel的ID等
        init(channel);
    } catch (Throwable t) {
        if (channel != null) {
            // 如果Channel已经创建但是初始化过程中发生了异常,则强制关闭Channel
            channel.unsafe().closeForcibly();
            // 创建一个新的ChannelPromise并设置异常
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        // 如果Channel还未创建就发生了异常,则创建一个FailedChannel实例,并设置异常
        return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
    }

    // 将Channel注册到EventLoopGroup中,并获取注册结果
    ChannelFuture regFuture = config().group().register(channel);
    // 如果注册过程中出现了异常
    if (regFuture.cause() != null) {
        // 如果Channel已经注册成功,直接关闭Channel
        if (channel.isRegistered()) {
            channel.close();
        } else {
            // 如果Channel尚未注册成功,强制关闭Channel
            channel.unsafe().closeForcibly();
        }
    }

    // 返回注册结果
    return regFuture;
}

这个方法的作用是初始化并注册一个新的Channel。它首先使用ChannelFactory创建一个新的Channel实例,然后调用init方法对其进行初始化。如果在初始化过程中发生了异常,则会强制关闭Channel,并返回一个带有异常的ChannelPromise。

接着,它将新创建的Channel注册到指定的EventLoopGroup中,并获取注册结果。如果注册过程中出现了异常,则会根据Channel是否已经注册成功来决定是直接关闭Channel还是强制关闭Channel。最后,返回注册结果。

这个方法的设计使得在初始化和注册Channel的过程中能够处理各种异常情况,并保证Channel的状态正确。


我们重点关注 channelFactory.newChannel();Netty Review - NioServerSocketChannel源码分析_第1张图片

Netty Review - NioServerSocketChannel源码分析_第2张图片

来会看一下

Netty Review - NioServerSocketChannel源码分析_第3张图片


那就继续 NioServerSocketChannel的构造函数 ,这里就是 NioServerSocketChannel初始化的地方 。


NioServerSocketChannel 类继承关系

Netty Review - NioServerSocketChannel源码分析_第4张图片


实例化过程中做的事情

    @Override
    public T newChannel() {
        try {
            return constructor.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }

从上面的源码中,我们可以知道必然是无参构造函数 。

无参构造函数

/**
 * Create a new instance
 */
public NioServerSocketChannel() {
    // 调用另一个构造函数,传入一个新创建的Java NIO ServerSocketChannel实例
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

这段代码是NioServerSocketChannel类的构造函数,它调用了另一个私有构造函数来创建一个新的NioServerSocketChannel实例。

这个构造函数的作用是创建一个新的NioServerSocketChannel实例。它通过调用另一个私有构造函数来完成实例的创建过程。在调用私有构造函数时,传入了一个新创建的Java NIO ServerSocketChannel实例作为参数。

这样,NioServerSocketChannel实例就会持有这个Java NIO ServerSocketChannel实例,并在需要时对其进行操作。


`newSocket

我们继续看下 newSocket

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        // 使用指定的SelectorProvider打开一个新的ServerSocketChannel  (熟悉的NIO代码)
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        // 如果发生异常,则抛出ChannelException异常
        throw new ChannelException("Failed to open a server socket.", e);
    }
}
 private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

这段代码是NioServerSocketChannel类中的一个私有静态方法,用于使用指定的SelectorProvider创建一个新的ServerSocketChannel。

这个方法的作用是使用指定的SelectorProvider打开一个新的ServerSocketChannel。在Java NIO中,SelectorProvider用于提供新的SelectableChannel实例。方法首先调用指定的SelectorProvider的openServerSocketChannel()方法来创建一个新的ServerSocketChannel实例。如果创建过程中发生了IO异常,则会捕获并抛出ChannelException异常。

provider.openServerSocketChannel() 熟悉的NIO代码


this(newSocket(DEFAULT_SELECTOR_PROVIDER));

/**
 * Create a new instance using the given {@link ServerSocketChannel}.
 */
public NioServerSocketChannel(ServerSocketChannel channel) {
    // 调用父类构造函数,传入null作为EventLoopGroup参数(因为NioServerSocketChannel没有父类EventLoopGroup),传入给定的ServerSocketChannel实例以及OP_ACCEPT作为感兴趣的事件
    super(null, channel, SelectionKey.OP_ACCEPT);
    // 创建一个新的NioServerSocketChannelConfig实例,用于配置NioServerSocketChannel的参数
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

这段代码是NioServerSocketChannel类的另一个构造函数,它接受一个ServerSocketChannel实例作为参数,并调用父类构造函数来初始化NioServerSocketChannel实例。

这个构造函数的作用是使用给定的ServerSocketChannel实例来创建一个新的NioServerSocketChannel实例。

在构造函数中,首先调用了父类AbstractNioMessageChannel的构造函数,传入了null作为EventLoopGroup参数(因为NioServerSocketChannel没有父类EventLoopGroup)、给定的ServerSocketChannel实例以及OP_ACCEPT作为感兴趣的事件。

然后,创建一个新的NioServerSocketChannelConfig实例,用于配置NioServerSocketChannel的参数。

Netty Review - NioServerSocketChannel源码分析_第5张图片

SelectionKey.OP_ACCEPT是Java NIO中SelectableChannel所支持的一种操作兴趣标志。在Netty中,它通常用于NioServerSocketChannel,用于指示对新连接请求的接受操作感兴趣。

具体来说,SelectionKey.OP_ACCEPT表示SelectableChannel对新连接请求的接受操作感兴趣。当有新的连接请求到达时,Selector会将该事件通知给对应的SelectableChannel,并在之后的事件循环中处理该连接请求。在Netty中,NioServerSocketChannel通常会注册SelectionKey.OP_ACCEPT事件,以便及时响应新的连接请求。

通过使用这个操作兴趣标志,Netty能够利用Java NIO的非阻塞IO特性,实现高效的TCP服务器端编程,能够处理大量并发的连接请求,提高系统的性能和并发能力。

在这里插入图片描述


super(null, channel, SelectionKey.OP_ACCEPT)

这段代码是AbstractNioChannel类的受保护构造函数,用于创建一个新的AbstractNioChannel实例。让我们逐步解释它:

/**
 * 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) {
    // 调用父类构造函数,传入父Channel实例
    super(parent);
    // 初始化成员变量,保存SelectableChannel和读取操作的兴趣操作
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    try {
        // 将SelectableChannel设置为非阻塞模式  (熟悉的NIO方法)
        ch.configureBlocking(false);
    } catch (IOException e) {
        try {
            // 发生异常时关闭SelectableChannel
            ch.close();
        } catch (IOException e2) {
            if (logger.isWarnEnabled()) {
                logger.warn("Failed to close a partially initialized socket.", e2);
            }
        }
        // 抛出ChannelException异常
        throw new ChannelException("Failed to enter non-blocking mode.", e);
    }
}

这个构造函数用于创建一个新的AbstractNioChannel实例。

它接受三个参数:

  • 父Channel、
  • 底层的SelectableChannel
  • 读取数据时的操作兴趣标志。

在构造函数内部,首先调用了父类构造函数,将父Channel传入以便建立关系。然后,初始化了成员变量,保存了SelectableChannel和读取操作的兴趣标志。

接着,尝试将SelectableChannel设置为非阻塞模式。如果设置过程中发生IO异常,会关闭SelectableChannel并抛出ChannelException异常。


super(parent);

这段代码是AbstractChannel类的受保护构造函数,用于创建一个新的AbstractChannel实例。让我们逐步解释它:

/**
 * Creates a new instance.
 *
 * @param parent
 *        the parent of this channel. {@code null} if there's no parent.
 */
protected AbstractChannel(Channel parent) {
    // 将父Channel赋值给成员变量parent
    this.parent = parent;
    // 生成唯一的通道ID
    id = newId();
    // 创建一个新的Unsafe实例,用于执行底层操作
    unsafe = newUnsafe();
    // 创建一个新的ChannelPipeline实例,用于存储和处理ChannelHandler
    pipeline = newChannelPipeline();
}

这个构造函数用于创建一个新的AbstractChannel实例。它接受一个参数parent,用于指定这个Channel的父Channel。

在构造函数内部,首先将父Channel赋值给成员变量parent,然后生成一个唯一的通道ID,接着创建一个新的Unsafe实例用于执行底层操作,最后创建一个新的ChannelPipeline实例用于存储和处理ChannelHandler。

newChannelPipeline();创建一个新的ChannelPipeline实例,用于存储和处理ChannelHandler

/**
 * Returns a new {@link DefaultChannelPipeline} instance.
 */
protected DefaultChannelPipeline newChannelPipeline() {
    // 创建一个新的DefaultChannelPipeline实例,传入当前的Channel作为参数
    return new DefaultChannelPipeline(this);
}

这段代码是AbstractChannel类中的一个受保护方法,用于创建一个新的DefaultChannelPipeline实例。

这个方法用于创建一个新的DefaultChannelPipeline实例,并将当前的Channel作为参数传入。DefaultChannelPipeline是Netty中用于管理ChannelHandler链的默认实现。每个Channel都有自己的ChannelPipeline,用于存储和处理ChannelHandler。调用这个方法会创建一个新的ChannelPipeline实例,并将当前的Channel作为其所属的Channel。

protected DefaultChannelPipeline(Channel channel) {
    // 将传入的Channel赋值给成员变量channel
    this.channel = ObjectUtil.checkNotNull(channel, "channel");
    // 创建一个新的SucceededChannelFuture实例,表示成功的Future
    succeededFuture = new SucceededChannelFuture(channel, null);
    // 创建一个新的VoidChannelPromise实例,表示空的Promise
    voidPromise =  new VoidChannelPromise(channel, true);

    // 创建一个新的TailContext实例,表示管道中的尾部节点
    tail = new TailContext(this);
    // 创建一个新的HeadContext实例,表示管道中的头部节点
    head = new HeadContext(this);

    // 设置头部节点的下一个节点为尾部节点,尾部节点的上一个节点为头部节点
    head.next = tail;
    tail.prev = head;
}

这段代码是DefaultChannelPipeline类的受保护构造函数,用于创建一个新的DefaultChannelPipeline实例。

这个构造函数用于创建一个新的DefaultChannelPipeline实例。在构造函数内部,首先将传入的Channel赋值给成员变量channel,并创建了一个成功的Future实例(succeededFuture)和一个空的Promise实例(voidPromise)。

接着创建了头部节点(HeadContext)和尾部节点(TailContext),并设置头部节点的下一个节点为尾部节点,尾部节点的上一个节点为头部节点。

这样就构成了一个空的ChannelPipeline链表结构。


ch.configureBlocking(false)

ch.configureBlocking(false);熟悉的NIO代码

ch.configureBlocking(false)是将一个Java NIO的SelectableChannel配置为非阻塞模式。

在Java NIO中,SelectableChannel是一个可以注册到Selector上并监听IO事件的通道,比如SocketChannelServerSocketChannel等。在默认情况下,这些通道都是阻塞模式的,意味着当没有数据可读或无法写入时,读取和写入操作会一直阻塞当前线程,直到有数据可用或者通道关闭。

通过调用configureBlocking(false)方法,可以将这些通道配置为非阻塞模式。在非阻塞模式下,当没有数据可读或无法写入时,读取和写入操作会立即返回而不会阻塞当前线程,这样就可以在单个线程上处理多个通道的IO操作,提高了系统的并发处理能力。

在Netty中,通常会将SelectableChannel配置为非阻塞模式,以利用Java NIO的非阻塞IO特性,实现高效的事件驱动的网络编程。

Netty Review - NioServerSocketChannel源码分析_第6张图片


小结

NioServerSocketChannel是Netty中用于处理TCP服务器端Socket的通道实现之一。它继承自AbstractNioMessageChannel,是基于Java NIO的ServerSocketChannel的封装。以下是关于NioServerSocketChannel的总结:

  1. 构造函数

    • 有两个构造函数:一个接受ServerSocketChannel实例作为参数,另一个不接受任何参数。
    • 构造函数负责初始化NioServerSocketChannel实例,其中包括调用父类构造函数、配置SelectableChannel为非阻塞模式等操作。
  2. 初始化和注册

    • 使用initAndRegister方法初始化和注册NioServerSocketChannel。
    • 在初始化过程中,会调用channelFactory.newChannel()方法创建一个新的NioServerSocketChannel实例,并初始化该实例。
    • 如果初始化过程中出现异常,会尝试关闭部分初始化的SocketChannel,并返回一个包含异常信息的DefaultChannelPromise实例。
  3. 功能和特性

    • 用于接受TCP连接请求,并创建对应的NioSocketChannel实例来处理连接。
    • 可以设置Channel的各种参数,如TCP参数、选项等。
    • 通过继承自AbstractNioMessageChannel,具有处理读写事件的能力。
  4. 底层实现

    • 使用了Java NIO的ServerSocketChannel来处理TCP连接请求。
    • 通过配置SelectableChannel为非阻塞模式,实现了高性能的非阻塞IO操作。

综上所述,NioServerSocketChannel是Netty中用于处理TCP服务器端Socket的通道实现,它基于Java NIO,提供了高性能的非阻塞IO操作,并具有初始化、注册、配置参数等功能。

Netty Review - NioServerSocketChannel源码分析_第7张图片

你可能感兴趣的:(【Netty】,netty,serversocket)