netty源码分析(四):新连接接入

103 阅读2分钟

概述

本章主要讲解netty启动后的新连接的接入,当服务端的NioEventLoop启动后,selector会一直调用select方法,当监测到新的连接时,会创建客户端的NioSocketChanne,并从workGroup中分配出一个NioEventLoop,并注册到对应的selector上

一.新连接监测

在上篇文章# netty源码分析(三):NioEventLoop中的4.1章节中已经讲解eventLoop启动并监测IO事件

这里我们可以使用telnet 模拟一个客户端的连接

telnet 127.0.0.1 8888

最终会调用

NioEventLoop类
unsafe.close(unsafe.voidPromise());

读取channel中的数据是unsafe类,服务端的unsafe类是NioMessageUnsafe

public void read() {
             //...省略边缘代码
            do {
               // 1. 这里读取连接,创建NioSocketChannel
                int localRead = doReadMessages(readBuf);
                if (localRead == 0) {
                    break;
                }
                if (localRead < 0) {
                    closed = true;
                    break;
                }
                allocHandle.incMessagesRead(localRead);
            } while (allocHandle.continueReading());
       

        int size = readBuf.size();
        for (int i = 0; i < size; i ++) {
            readPending = false;
            //2.触发channel中channelRead方法,就是分配NioEventLoop和注册Selector
            pipeline.fireChannelRead(readBuf.get(i));
        }
        readBuf.clear();
        allocHandle.readComplete();
        pipeline.fireChannelReadComplete();

        if (exception != null) {
            closed = closeOnReadError(exception);
            pipeline.fireExceptionCaught(exception);
        }

        if (closed) {
            inputShutdown = true;
            if (isOpen()) {
                close(voidPromise());
            }
        }
    } finally {
        if (!readPending && !config.isAutoRead()) {
            removeReadOp();
        }
    }
}

1.1.NioSocketChannel创建

通过上面代码,

//在这里,创建
int localRead = doReadMessages(readBuf);

接着进入,在这里调用jdk底层,创建 NioSocketChannel image.png

new NioSocketChannel(this, ch)

在new这个对象时候,第一个参数是NioServerSocketChannel作为父chanle,第二个参数是jdk的SocketChannel,代码如下

image.png

会一层一层的调用父类构造,最后创建一个NioSocketChannel,这里和创建NioServerSocketChannel代码逻辑是一样的,因为他们有共同的父类,不同的是,这里关注的事件是OP_READ事件

1.2.新链接的NioEventLoop的分配和selector注册

新的连接创建成功后,会把新的连接放入readBuf集合中,然后遍历该集合,依次触发方法

pipeline.fireChannelRead(readBuf.get(i))

fireChannelRead方法,会在这个channel中,从head开始,依次触发每个handler的read方法,会依次进入 Head --> MyServerHandler -->ServerBootstrapAcceptor image.pngServerBootstrapAcceptor中,会对这个客户端连接,进行NioEventLoop的分配和selector注册

image.png 进入第五步,注册

childGroup.register(child)

继续进入

public ChannelFuture register(Channel channel) {
    return next().register(channel);
}
1. next()方法是从选择器中选择一个对象,这个前面讲过了
2. 选好之后,调用jdk底层channel进行注册,这个和服务端的channel注册逻辑是一样的,前面文章已经讲过了
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}

注册到select后,现在pipline中的hadler情况如下 HeadHandler --> MyTcpServerInitializer 启动类自己添加的 ---> TailHandler

但是我们知道 MyTcpServerInitializer 启动类自己添加的 里面有个 initChannel(socketchannel)方法,在这个方法里面,我们才真正添加处理业务的handler,那么MyTcpServerInitializer的initChannel(socketchannel)方法是什么时候执行的呢? 答案就是把这个channle注册到select后,如下所示

private void register0(ChannelPromise promise) {
    try {
        if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
            return;
        }

        boolean firstRegistration = this.neverRegistered;
        AbstractChannel.this.doRegister();
        this.neverRegistered = false;
        AbstractChannel.this.registered = true;
        AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded(); // todo 这里会触发handleradd,最终触发initChannel方法
        this.safeSetSuccess(promise);
        AbstractChannel.this.pipeline.fireChannelRegistered();
        if (AbstractChannel.this.isActive()) {
            if (firstRegistration) {
                AbstractChannel.this.pipeline.fireChannelActive();
            } else if (AbstractChannel.this.config().isAutoRead()) {
                this.beginRead();
            }
        }
    } catch (Throwable var3) {
        this.closeForcibly();
        AbstractChannel.this.closeFuture.setClosed();
        this.safeSetFailure(promise, var3);
    }

}

最终会调用

image.png

二.NioSocketChannel读写事件的注册

后面在写,心累了

三.channle分类

image.png