Netty源码分析第一章:Netty启动步骤html
第五节:绑定端口java
上一小节咱们学习了channel注册在selector的步骤, 仅仅作了注册但并无监听事件, 事件是如何监听的呢?promise
咱们继续跟第一小节的最初的doBind()方法:socket
private ChannelFuture doBind(final SocketAddress localAddress) { //初始化并注册(1)
final ChannelFuture regFuture = initAndRegister(); //得到channel(2)
final Channel channel = regFuture.channel(); if (regFuture.cause() != null) { return regFuture; } if (regFuture.isDone()) { ChannelPromise promise = channel.newPromise(); //绑定(3)
doBind0(regFuture, channel, localAddress, promise); return promise; } else { //去除非关键代码
return promise; } }
上一小节跟完了initAndRegister()方法, 咱们继续往下走:ide
第二步, 得到channel:oop
final Channel channel = regFuture.channel();
经过ChannelFuture的channel()方法得到了咱们刚刚注册的NioServerSocketChannel, 拿到这个channel咱们跟到第三步, 绑定源码分析
跟进方法doBind0(regFuture, channel, localAddress, promise):学习
private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { //绑定端口
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); }
最终会走到channel.bind(localAddress, promise)这个方法当中this
继续跟, 会走到AbstractChannel的bind()方法中:spa
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { //经过pipeline绑定端口
return pipeline.bind(localAddress, promise); }
这里的pipeline就是channel初始化建立的pipline, pipline是事件传输通道, 这里咱们暂不跟传输过程, 咱们只需知道最后这个方法走到了AbstractChannel的bind()方法
跟到AbstractChannel的bind()方法:
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) { //代码省略 //端口绑定以前不是active, 返回false
boolean wasActive = isActive(); try { //作jdk底层的绑定
doBind(localAddress); } catch (Throwable t) { //省略
return; } //端口绑定以前不是active, 端口绑定以后变成active了
if (!wasActive && isActive()) { invokeLater(new Runnable() { @Override public void run() { pipeline.fireChannelActive(); } }); } safeSetSuccess(promise); }
重点关注下doBind(localAddress)方法
跟到NioSeverSocketChannel的doBind()方法:
protected void doBind(SocketAddress localAddress) throws Exception { //jdk版本的判断
if (PlatformDependent.javaVersion() >= 7) { javaChannel().bind(localAddress, config.getBacklog()); } else { javaChannel().socket().bind(localAddress, config.getBacklog()); } }
开始是一个jdk版本的判断, 咱们以jdk7以上为例, 看到这条语句:
javaChannel().bind(localAddress, config.getBacklog());
终于找到了和jdk底层相关的绑定逻辑了, javaChannel()返回的是当前channel绑定的jdk底层的channel, 而bind()方法, 就是jdk底层的channel绑定端口的逻辑
回到bind(final SocketAddress localAddress, final ChannelPromise promise)方法:
首先看if判断: if (!wasActive && isActive())
这里意思是若是以前不是active, 绑定以后是active的话, 执行if块, 显然这里符合条件, 继续往里走
最终会走到这一步, pipeline.fireChannelActive()
这也是传输active事件, 目前咱们只需知道, 事件完成以后, 会调用AbstractChannel内部类AbstractUnsafe的beginRead()方法
跟到AbstractUnsafe的beginRead()方法中:
public final void beginRead() { assertEventLoop(); if (!isActive()) { return; } try { doBeginRead(); } catch (final Exception e) { //代码省略
} }
咱们关注doBeginRead()方法:
protected void doBeginRead() throws Exception { //拿到selectionKey
final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; //得到感兴趣的事件
final int interestOps = selectionKey.interestOps(); //判断是否是对任何事件都不监听
if ((interestOps & readInterestOp) == 0) { //此条件成立 //将以前的accept事件注册, readInterest表明能够读取一个新链接的意思
selectionKey.interestOps(interestOps | readInterestOp); } }
这里到了jdk底层的调用逻辑, 经过注释不难看出其中的逻辑, 咱们拿到和channel绑定的jdk底层的selectionKey, 获取其监听事件, 一上节咱们知道, channel注册的时候没有注册任何事件, 因此咱们这里if ((interestOps & readInterestOp) == 0) 返回true, 以后, 将accept事件注册到channel中, 也就是 selectionKey.interestOps(interestOps | readInterestOp) 这步执行的
注册完accept事件以后, 就能够轮询selector, 监听是否有新链接接入了
第一章总结
经过了这一章的学习, 咱们了解了server启动的大概流程, 这里重点掌握整个启动脉络, 知道关键步骤在哪一个类执行, 后面的章节会分析每个模块的含义