前文传送门: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的资料请关注其它相关文章!