Netty啟動步驟綁定端口示例方法源碼分析
前文傳送門:Netty啟動流程注冊多路復用源碼解析
綁定端口
上一小節(jié)我們學習了channel注冊在selector的步驟, 僅僅做了注冊但并沒有監(jiān)聽事件, 事件是如何監(jiān)聽的呢?
我們繼續(xù)跟第一小節(jié)的最初的doBind()方法
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 {
//去除非關(guān)鍵代碼
return promise;
}
}上一小節(jié)跟完了initAndRegister()方法, 我們繼續(xù)往下走:
第二步, 獲得channel
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)這個方法當中
繼續(xù)跟, 會走到AbstractChannel的bind()方法中:
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
//通過pipeline綁定端口
return pipeline.bind(localAddress, promise);
}這里的pipeline就是channel初始化創(chuàng)建的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);
}重點關(guān)注下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底層相關(guān)的綁定邏輯了, javaChannel()返回的是當前channel綁定的jdk底層的channel, 而bind()方法, 就是jdk底層的channel綁定端口的邏輯
回到bind(final SocketAddress localAddress, final ChannelPromise promise)方法:
首先看if判斷: if (!wasActive && isActive())
這里意思是如果之前不是active, 綁定之后是active的話, 執(zhí)行if塊, 顯然這里符合條件, 繼續(xù)往里走
最終會走到這一步, pipeline.fireChannelActive()
這也是傳輸active事件, 目前我們只需知道, 事件完成之后, 會調(diào)用AbstractChannel內(nèi)部類AbstractUnsafe的beginRead()方法
跟到AbstractUnsafe的beginRead()方法中:
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
//代碼省略
}
}我們關(guān)注doBeginRead()方法:
protected void doBeginRead() throws Exception {
//拿到selectionKey
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
//獲得感興趣的事件
final int interestOps = selectionKey.interestOps();
//判斷是不是對任何事件都不監(jiān)聽
if ((interestOps & readInterestOp) == 0) {
//此條件成立
//將之前的accept事件注冊, readInterest代表可以讀取一個新連接的意思
selectionKey.interestOps(interestOps | readInterestOp);
}
}這里到了jdk底層的調(diào)用邏輯, 通過注釋不難看出其中的邏輯, 我們拿到和channel綁定的jdk底層的selectionKey, 獲取其監(jiān)聽事件, 一上節(jié)我們知道, channel注冊的時候沒有注冊任何事件, 所以我們這里if ((interestOps & readInterestOp) == 0) 返回true, 之后, 將accept事件注冊到channel中, 也就是 selectionKey.interestOps(interestOps | readInterestOp) 這步執(zhí)行的
注冊完accept事件之后, 就可以輪詢selector, 監(jiān)聽是否有新連接接入了
章節(jié)總結(jié)
通過了這一章的學習, 我們了解了server啟動的大概流程, 這里重點掌握整個啟動脈絡(luò), 知道關(guān)鍵步驟在哪個類執(zhí)行, 后面的章節(jié)會分析每一個模塊的含義
以上就是Netty啟動步驟綁定端口源碼分析的詳細內(nèi)容,更多關(guān)于Netty啟動的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Sping?Security前后端分離兩種實戰(zhàn)方案
這篇文章主要介紹了Sping?Security前后端分離兩種方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03
Kotlin Coroutines執(zhí)行異步加載示例詳解
這篇文章主要給大家介紹了關(guān)于Kotlin Coroutines執(zhí)行異步加載的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-01-01
Java中static修飾的靜態(tài)變量、方法及代碼塊的特性與使用
這篇文章主要介紹了Java中static修飾的靜態(tài)變量、方法及代碼塊的特性與使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04

