前文传送门:netty客户端处理接入事件handle

niosocketchannel的创建

回到上一小节的read()方法

public void read() {
    //必须是nioeventloop方法调用的, 不能通过外部线程调用
    assert eventloop().ineventloop();
    //服务端channel的config
    final channelconfig config = config();
    //服务端channel的pipeline
    final channelpipeline pipeline = pipeline();
    //处理服务端接入的速率
    final recvbytebufallocator.handle allochandle = unsafe().recvbufallochandle();
    //设置配置
    allochandle.reset(config);
    boolean closed = false;
    throwable exception = null;
    try {
        try {
            do {
                //创建jdk底层的channel
                //readbuf用于临时承载读到链接
                int localread = doreadmessages(readbuf);
                if (localread == 0) {
                    break;
                }
                if (localread < 0) {
                    closed = true;
                    break;
                }
                //分配器将读到的链接进行计数
                allochandle.incmessagesread(localread);
                //连接数是否超过最大值
            } while (allochandle.continuereading());
        } catch (throwable t) {
            exception = t;
        }
        int size = readbuf.size();
        //遍历每一条客户端连接
        for (int i = 0; i < size; i ++) {
            readpending = false;
            //传递事件, 将创建niosokectchannel进行传递
            //最终会调用serverbootstrap的内部类serverbootstrapacceptor的channelread()方法
            pipeline.firechannelread(readbuf.get(i));
        }
        readbuf.clear();
        allochandle.readcomplete();
        pipeline.firechannelreadcomplete();
        //代码省略
    } finally {
        //代码省略
    }
}

我们继续剖析int localread = doreadmessages(readbuf)这一部分逻辑

我们首先看readbuf

private final list<object> readbuf = new arraylist<object>();

这里只是简单的定义了一个arraylist, doreadmessages(readbuf)方法就是将读到的链接放在这个list中, 因为这里是nioserversocketchannel所以这走到了nioserversocketchannel的doreadmessage()方法

跟到doreadmessage()方法中:

protected int doreadmessages(list<object> buf) throws exception {
    //根据当前jdk底层的serversocketchannel拿到jdk底层channel
    socketchannel ch = javachannel().accept();
    try {
        if (ch != null) {
            //封装成一个niosokectchannel扔到buf中
            buf.add(new niosocketchannel(this, ch));
            return 1;
        }
    } catch (throwable t) {
        //代码省略
    }
    return 0;
}

jdk底层相关的内容

首先根据jdk的serversocketchannel拿到jdk的channel, 熟悉nio的小伙伴应该不会陌生

封装成一个niosokectchannel扔到readbuf中

这里的niosocketchannel是对jdk底层的socketchannel的包装, 我们看到其构造方法传入两个参数, this代表当前nioserversocketchannel, ch代表jdk的socketchannel

我们跟到niosocketchannel的构造方法中:

public niosocketchannel(channel parent, socketchannel socket) {
    super(parent, socket);
    config = new niosocketchannelconfig(this, socket.socket());
}

这里看到调用了父类构造方法, 传入两个参数, parent代表创建自身channel的, nioserversocketchannel, socket代表jdk底层的socketchannel

跟到父类构造方法中

protected abstractniobytechannel(channel parent, selectablechannel ch) { 
    super(parent, ch, selectionkey.op_read);
}

其中selectionkey.op_read代表其监听事件是读事件

继续跟父类的构造方法:

protected abstractniochannel(channel parent, selectablechannel ch, int readinterestop) {
    super(parent);
    this.ch = ch;
    this.readinterestop = readinterestop;
    try {
        //设置为非阻塞
        ch.configureblocking(false);
    } catch (ioexception e) {
        //代码省略
    }
}

这里初始化了自身成员变量ch, 就是jdk底层的socketchannel, 并初始化了自身的监听事件readinterestop, 也就是读事件

ch.configureblocking(false)这一步熟悉nio的小伙伴也不陌生, 就是将jdk的socketchannel设置为非阻塞

我们继续跟到父类构造方法中:

protected abstractchannel(channel parent) {
    this.parent = parent;
    id = newid();
    unsafe = newunsafe();
    pipeline = newchannelpipeline();
}

这里初始化parent, 也就是创建自身的nioserversocketchannel, 并为自身创建了唯一id

初始化unsafe, 我们跟到newunsafe()方法中

由于此方法是nioeventloop调用的, 所以会走到其父类abstractniobytechannel的newunsafe()

跟到newunsafe()中:

protected abstractniounsafe newunsafe() {
    return new niobyteunsafe();
}

这里创建了niobyteunsafe对象, 所以niosocketchannel对应的unsafe是niobyteunsafe

继续往下跟, 我们看到其初始化了pipeline, 有关pipline的知识, 我们会在下一章节中讲到

回到niosocketchannel中的构造方法:

public niosocketchannel(channel parent, socketchannel socket) {
    super(parent, socket);
    config = new niosocketchannelconfig(this, socket.socket());
}

同nioserversocketchannel一样, 这里也初始化了一个config属性, 传入两个参数, 当前niosocketchannel自身和jdk的底层socketchannel的socket对象

我们跟进其构造方法

private niosocketchannelconfig(niosocketchannel channel, socket javasocket) {
    super(channel, javasocket);
}

同样, 这个类是niosocketchannel的内部类

继续跟父类构造方法:

public defaultsocketchannelconfig(socketchannel channel, socket javasocket) {
    super(channel);
    if (javasocket == null) {
        throw new nullpointerexception("javasocket");
    }
    //保存当前javasocket
    this.javasocket = javasocket;
    //是否禁止nagle算法
    if (platformdependent.canenabletcpnodelaybydefault()) {
        try {
            settcpnodelay(true);
        } catch (exception e) {
        }
    }
}

这里保存了socketchannel的socket对象, 并且默认的情况禁止了nagle算法, 有关nagle, 感兴趣的同学可以学习下相关知识

继续跟到父类构造方法中:

public defaultchannelconfig(channel channel) {
    this(channel, new adaptiverecvbytebufallocator());
}

又跟到到了我们熟悉的部分了, 也就是说, 无论nioserversocketchannel和niosocketchannel, 最后都会初始化defaultchannelconfig, 并创建可变bytebuf分配器, 我们之前小节对此做过详细剖析这里不再赘述, 这部分忘记的内容可以阅读之前小节内容进行回顾

这个分配器什么时候真正分配字节缓冲的呢?我们会在之后的章节进行详细剖析

至此我们剖析完成了niosocketchannel的初始化过程

以上就是netty客户端接入流程niosocketchannel创建源码解析的详细内容,更多关于netty客户端接入流程niosocketchannel的资料请关注其它相关文章!