AbstractNioByteChannel源碼解析
@Override protected void doWrite(ChannelOutboundBuffer in) throws Exception { int writeSpinCount = -1; for (;;) { // 從當前的消息發送環形數組中彈出一條消息 Object msg = in.current(); // 消息爲null,說明所有消息已經發送完成,清除寫操作位 if (msg == null) { // Wrote all messages. clearOpWrite(); break; } // 如果msg是ByteBuf類型 if (msg instanceof ByteBuf) { ByteBuf buf = (ByteBuf) msg; int readableBytes = buf.readableBytes(); // 如果消息沒有可讀字節,則丟棄消息,進行下一次循環 if (readableBytes == 0) { in.remove(); continue; } // 設置寫半包標識 boolean setOpWrite = false; // 設置消息是否全部發送標誌 boolean done = false; // 設置發送的消息總字節數 long flushedAmount = 0; // 獲取循環發送次數:是爲了當一次發送沒有完成時,繼續循環發送的次數,設置寫半包的最大循環次數的原因是循環發送的時候, // IO線程會一直進行寫操作, 此時IO線程無法處理其他的IO操作,例如讀取新的消息或者執行定時任務等;如果網絡IO阻塞或者對方接受消息太慢, // 可能導致線程假死 if (writeSpinCount == -1) { writeSpinCount = config().getWriteSpinCount(); } for (int i = writeSpinCount - 1; i >= 0; i --) { int localFlushedAmount = doWriteBytes(buf); if (localFlushedAmount == 0) { // 如果本次發送的字節數爲0,說明TCP發送緩衝區已滿,發生了ZERO_WINDOW, 此時再次發送可能出現寫0字節,空循環會佔用CPU資源 // 所以將寫半包標識設置爲true,退出循環。釋放IO線程。 setOpWrite = true; break; } flushedAmount += localFlushedAmount; // 消息沒有可讀字節,說明消息已經發送成功,退出本次循環; if (!buf.isReadable()) { done = true; break; } } in.progress(flushedAmount); // 如果消息發送彎沉,將消息從消息發送循環數組中移除 if (done) { in.remove(); } else { // 設置寫半包標識,啓動刷新線程繼續發送之前沒有發送完成的半包消息--寫半包 incompleteWrite(setOpWrite); break; } // 分支二 文件傳輸 } else if (msg instanceof FileRegion) { FileRegion region = (FileRegion) msg; boolean done = region.transfered() >= region.count(); boolean setOpWrite = false; if (!done) { long flushedAmount = 0; if (writeSpinCount == -1) { writeSpinCount = config().getWriteSpinCount(); } for (int i = writeSpinCount - 1; i >= 0; i--) { long localFlushedAmount = doWriteFileRegion(region); if (localFlushedAmount == 0) { setOpWrite = true; break; } flushedAmount += localFlushedAmount; if (region.transfered() >= region.count()) { done = true; break; } } in.progress(flushedAmount); } if (done) { in.remove(); } else { incompleteWrite(setOpWrite); break; } } else { // Should not reach here. throw new Error(); } } }protected final void incompleteWrite(boolean setOpWrite) {
// Did not write completely.
// 判斷並設置寫半包
if (setOpWrite) {
setOpWrite();
} else {
// Schedule flush again later so other tasks can be picked up in the meantime
// 啓動刷新線程繼續寫半包
Runnable flushTask = this.flushTask;
if (flushTask == null) {
flushTask = this.flushTask = new Runnable() {
@Override
public void run() {
flush();
}
};
}
eventLoop().execute(flushTask);
}
}
// AbstractNioMessageChannel 與AbstractNioByteChannel的不同是:
// 前者發送的是POJO對象,後者發送的是ByteBuf或者FileRegion
@Override
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
final SelectionKey key = selectionKey();
final int interestOps = key.interestOps();
for (;;) {
Object msg = in.current();
if (msg == null) {
// Wrote all messages.
if ((interestOps & SelectionKey.OP_WRITE) != 0) {
key.interestOps(interestOps & ~SelectionKey.OP_WRITE);
}
break;
}
try {
boolean done = false;
for (int i = config().getWriteSpinCount() - 1; i >= 0; i--) {
if (doWriteMessage(msg, in)) {
done = true;
break;
}
}
if (done) {
in.remove();
} else {
// Did not write all messages.
if ((interestOps & SelectionKey.OP_WRITE) == 0) {
key.interestOps(interestOps | SelectionKey.OP_WRITE);
}
break;
}
} catch (IOException e) {
if (continueOnWriteError()) {
in.remove(e);
} else {
throw e;
}
}
}
}
NioServerSocketChannel:
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
//
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
// 打開新的ServerSocketChannel通道
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
/**
* Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
* {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
*
* See <a href="See https://github.com/netty/netty/issues/2308">#2308</a>.
*/
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
// 配置Channel的TCP參數
private final ServerSocketChannelConfig config;
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
try {
if (ch != null) {
// 讀取操作就是:接受客戶端鏈接,創建NioSocketChannel對象
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
NioSocketChannel:
// 鏈接的三種狀態:
// 1.鏈接成功,返回true
// 2.暫時沒有鏈接上,服務端沒有返回ACK應答,鏈接結果不確定,返回false;
// 3.鏈接失敗,拋出異常
@Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null) {
// 綁定本地端口
javaChannel().socket().bind(localAddress);
}
boolean success = false;
try {
// 獲取鏈接狀態
boolean connected = javaChannel().connect(remoteAddress);
// 如果未鏈接上,設置selectionKey未OP_CONNECT,監聽鏈接網絡操作位;
if (!connected) {
selectionKey().interestOps(SelectionKey.OP_CONNECT);
}
success = true;
return connected;
} finally {
if (!success) {
// 如果拋出異常,則關閉客戶端鏈接;
doClose();
}
}
}
寫半包:
@Override
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
for (;;) {
// 獲取待發送的ByteBuf個數;
int size = in.size();
if (size == 0) {
// All written so clear OP_WRITE
// clear OP_WRITE
clearOpWrite();
break;
}
// 已經寫出去的字節數
long writtenBytes = 0;
// 消息發送完成標識
boolean done = false;
// 是否設置寫半包標識
boolean setOpWrite = false;
// Ensure the pending writes are made of ByteBufs only.
ByteBuffer[] nioBuffers = in.nioBuffers();
// 要發送的ByteBufer的數量
int nioBufferCnt = in.nioBufferCount();
// 需要發送的總字節數
long expectedWrittenBytes = in.nioBufferSize();
SocketChannel ch = javaChannel();
// Always us nioBuffers() to workaround data-corruption.
// See https://github.com/netty/netty/issues/2761
switch (nioBufferCnt) {
case 0:
// We have something else beside ByteBuffers to write so fallback to normal writes.
super.doWrite(in);
return;
case 1:
// Only one ByteBuf so use non-gathering write
ByteBuffer nioBuffer = nioBuffers[0];
for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
final int localWrittenBytes = ch.write(nioBuffer);
if (localWrittenBytes == 0) {
setOpWrite = true;
break;
}
expectedWrittenBytes -= localWrittenBytes;
writtenBytes += localWrittenBytes;
if (expectedWrittenBytes == 0) {
done = true;
break;
}
}
break;
default:
// 寫操作次數上限進行控制,因爲如果發送緩衝區滿,TCP處於KEEP-ALIVE狀態,消息無法發送出去,線程無法執行其他任務
for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
// 寫入SocketChannel的字節數
final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
// 緩衝區滿,設置寫半包標識未true,用於向多路複用器註冊OP_WRITE
if (localWrittenBytes == 0) {
setOpWrite = true;
break;
}
// 需要發送的字節數
expectedWrittenBytes -= localWrittenBytes;
// 寫出去的字節數
writtenBytes += localWrittenBytes;
// 發送完成
if (expectedWrittenBytes == 0) {
done = true;
break;
}
}
break;
}
// Release the fully written buffers, and update the indexes of the partially written buffer.
// 移除寫出去的字節數
in.removeBytes(writtenBytes);
// 寫半包
if (!done) {
// Did not write all buffers completely.
incompleteWrite(setOpWrite);
break;
}
}
}
protected final void incompleteWrite(boolean setOpWrite) {
// Did not write completely.
if (setOpWrite) {
setOpWrite();
} else {
// Schedule flush again later so other tasks can be picked up in the meantime
Runnable flushTask = this.flushTask;
if (flushTask == null) {
flushTask = this.flushTask = new Runnable() {
@Override
public void run() {
flush();
}
};
}
eventLoop().execute(flushTask);
}
}
讀寫操作:實際上就是從SocketChannel中讀取L個字節到ByteBuf中,L爲ByteBuf可寫的字節數。
@Override
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());
}
// AbstractByteBuf
@Override
public int writeBytes(ScatteringByteChannel in, int length) throws IOException {
ensureAccessible();
ensureWritable(length);
int writtenBytes = setBytes(writerIndex, in, length);
if (writtenBytes > 0) {
writerIndex += writtenBytes;
}
return writtenBytes;
}
@Override
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
ensureAccessible();
try {
// 從SocketChannel中讀取字節數組到緩衝區ByteBuffer中, 它的其實position爲writeIndex, limit爲writeIndex + length;
return in.read((ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length));
} catch (ClosedChannelException ignored) {
return -1;
}
}