【Netty】服务端启动Netty是都做了哪些事情?(二):初始化服务端Channel

【Netty】服务端启动Netty是都做了哪些事情?(二):初始化服务端Channel

Scroll Down

回顾

启动的四个阶段


Netty version:4.1.6

为了方便,再把上一节的Server.java再贴一次:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;

/**
 * @author cwj
 */
public final class Server {![init函数断点2.png](https://www.wenjie.store//blog/img//init%E5%87%BD%E6%95%B0%E6%96%AD%E7%82%B92_1579869044165.png)

    public static void main(String[] args) throws Exception {
        // 两大线程
        // 对应Socket中Server的启动监听线程
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 对应Socket中Client中主函数的线程
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 辅助类
            ServerBootstrap server = new ServerBootstrap();
            // 装配线程
            server.group(bossGroup, workerGroup)
                    // 设置Channel的类型
                    .channel(NioServerSocketChannel.class)
                    // 给每个客户端的连接设置一些tcp的基本属性
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.TCP_NODELAY, true)
                    // 每次创建客户端时,绑定一些基本的属性
                    .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    .attr(AttributeKey.newInstance("Attr"), "AttrValue")
                    // 服务端启动时做的逻辑
                    .handler(new ServerHandler())
                    // 给pipeline配置handler,Channel发生某种变化时对应的处理逻辑
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            //ch.pipeline().addLast(new AuthHandler());
                        }
                    });

            // 辅助类,绑定端口,这里使用了同步
            ChannelFuture future = server.bind(8888).sync();
            // 关闭
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

注意上面代码多了.option函数和.attr函数,这节验证某些东西时用到。


这次的起点其实就在上一节的initAndRegister函数中,整段代码是这样的:

    /**
     * 位置:io.netty.bootstrap.AbstractBootstrap#initAndRegister
     **/
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel();
            // 本篇入口
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly();
            }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        return regFuture;
    }

上面的init函数就是本篇的入口。

如果不记得是怎么进入initAndRegister函数的,就看看上一节吧,这里就不再赘述了。


初始化服务端Channel

视角移至init函数:
init函数.png

进入init函数实现,实现类选ServerBootstrap:
实现类选ServerBootstrap.png

进入该函数后,我们就可以看到配置属性的代码了:
init函数内部部分代码 .png

这里我们可以验证一下代码到底是不是如我们想象般工作,我们在Server.java代码中已经设置了attr和option,这里直接打断点看看即可:

attr和option

init函数断点1.png

childAttr和childOption

init函数断点2.png

handler

init函数断点3.png

从断点看到的结果来看,这段代码确实就是负责装配attr和option、handler。

补充:attr()函数设置的是服务端Channel的属性,而childAttr()函数设置的是客户端Channel的属性,之后的博客跟到还会补充。

服务端channel是指AbstractNioMessageChannel,客户端channel是指AbstractNioByteChannel。


添加连接器

最后,视角还是在上面的init函数里面,我们来看看最后一部分代码:

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;

        ...(中间略)

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                // 配置handler
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });

上面代码除了配置我们自定义的handler外,还有就是添加连接器,即ServerBootstrapAcceptor,关于连接器我现在知道的不多,等之后再补全。

不过除了连接器,我们还需要注意上面代码的两参数:currentChildGroup, currentChildHandler,我们不妨再看看断点:

init函数断点4.png

可以发现currentChildGroup其实就是我们在Server.java中的.group()设置的,包括currentChildHandler也一样,只不过我在这里没有设置而已。


到此,初始化服务端Channel就完成了。