目录
Demo中的读和写
在简单的手写 Netty demo项目中,Netty 读和写数据都是对 Channel 的读和写,也就是对NioSocketChannel 中的 SelectableChannel 的读和写。底层上也就是 Java NIO 中对SelectableChannel 读写 ByteBuffer。因为一个 NIOEventLoop 持有一个IO多路复用选择器 Selector,一个 Selector 又管理多个Channel, 所以所有的读和写都是发生在 NIOEventLoop 这个工作线程里的。
public class NioSocketChannel extends AbstractNioByteChannel {
@Override
protected SocketChannel javaChannel() {
return (SocketChannel) super.javaChannel();
}
public int doWriteBytes(ByteBuffer buf) throws Exception {
if (javaChannel().isOpen()&&javaChannel().isConnected()){
return javaChannel().write(buf);
}
return 0;
}
@Override
protected int doReadBytes(ByteBuffer buf) {
try {
return javaChannel().read(buf);
} catch (IOException e) {
throw new RuntimeException();
}
}
}
Netty 中的读和写
Netty 对这个读写过程进行了封装,它分别使用 ChannelInboundHandler 和 ChannelOutboundHandler 来封装读写,便于使用的同时更为复杂。ChannelInboundHandler 和 ChannelOutboundHandler 就是一个双向链表,ChannelInboundHandler 对入栈读数据进行处理,ChannelOutboundHandler 对出栈写数据进行处理。ChannelInboundHandler 使用了责任链模式,每个 ChannelInboundHandler 就相当于一个拦截器,对它感兴趣的数据进行处理,比如编解码,解析http,websocket协议等。
读操作
在 NioEventLoop 处理任务时它会通过 Unsafe 内部操作读取数据。在最底层,它会调用
byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
去读取Channel 传过来的数据,然后通过下面的代码去触发后续 pipeline 数据操作。
pipeline.fireChannelRead(byteBuf);
protected class NioByteUnsafe extends AbstractNioUnsafe {
@Override
public final void read() {
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
final ChannelPipeline pipeline = pipeline();
final ByteBufAllocator allocator = config.getAllocator();
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
byteBuf = allocHandle.allocate(allocator);
# 开始读取数据 doReadBytes(byteBuf)
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
if (close) {
// There is nothing left to read as we received an EOF.
readPending = false;
}
break;
}
allocHandle.incMessagesRead(1);
readPending = false;
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (close) {
closeOnRead(pipeline);
}
}
...
}
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
# 实际读取数据
@Override
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.attemptedBytesRead(byteBuf.writableBytes());
return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
}
}
写操作
ChannelOutboundHandler 对数据进行写操作,每个ContextHandle 对应一个ChannelOutboundBuffer ,它底层是一个Entry 单向链表,当 Channel 可写的时候,它会进行 flush() 操作,把 ByteBuf 转为 NIO 的 ByteBuff 写到 Channel 里。
public abstract class AbstractNioByteChannel extends AbstractNioChannel {
@Override
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
int writeSpinCount = config().getWriteSpinCount();
do {
Object msg = in.current();
if (msg == null) {
// Wrote all messages.
clearOpWrite();
// Directly return here so incompleteWrite(...) is not called.
return;
}
# 开始写
writeSpinCount -= doWriteInternal(in, msg);
} while (writeSpinCount > 0);
incompleteWrite(writeSpinCount < 0);
}
private int doWriteInternal(ChannelOutboundBuffer in, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
if (!buf.isReadable()) {
in.remove();
return 0;
}
# 开始写
final int localFlushedAmount = doWriteBytes(buf);
if (localFlushedAmount > 0) {
in.progress(localFlushedAmount);
if (!buf.isReadable()) {
in.remove();
}
return 1;
}
}
...
return WRITE_STATUS_SNDBUF_FULL;
}
}
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
@Override
# 实际写 ByteBuf
protected int doWriteBytes(ByteBuf buf) throws Exception {
final int expectedWrittenBytes = buf.readableBytes();
return buf.readBytes(javaChannel(), expectedWrittenBytes);
}
# 实际写 ChannelOutboundBuffer
@Override
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
SocketChannel ch = javaChannel();
int writeSpinCount = config().getWriteSpinCount();
do {
if (in.isEmpty()) {
// All written so clear OP_WRITE
clearOpWrite();
// Directly return here so incompleteWrite(...) is not called.
return;
}
int maxBytesPerGatheringWrite = ((NioSocketChannelConfig) config).getMaxBytesPerGatheringWrite();
ByteBuffer[] nioBuffers = in.nioBuffers(1024, maxBytesPerGatheringWrite);
int nioBufferCnt = in.nioBufferCount();
switch (nioBufferCnt) {
case 0:
// We have something else beside ByteBuffers to write so fallback to normal writes.
writeSpinCount -= doWrite0(in);
break;
case 1: {
ByteBuffer buffer = nioBuffers[0];
int attemptedBytes = buffer.remaining();
#实际写 ChannelOutboundBuffer
final int localWrittenBytes = ch.write(buffer);
if (localWrittenBytes <= 0) {
incompleteWrite(true);
return;
}
adjustMaxBytesPerGatheringWrite(attemptedBytes, localWrittenBytes, maxBytesPerGatheringWrite);
in.removeBytes(localWrittenBytes);
--writeSpinCount;
break;
}
default: {
long attemptedBytes = in.nioBufferSize();
final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
if (localWrittenBytes <= 0) {
incompleteWrite(true);
return;
}
adjustMaxBytesPerGatheringWrite((int) attemptedBytes, (int) localWrittenBytes, maxBytesPerGatheringWrite);
in.removeBytes(localWrittenBytes);
--writeSpinCount;
break;
}
}
} while (writeSpinCount > 0);
incompleteWrite(writeSpinCount < 0);
}
}
写 ChannelOutboundBuffer 数据时调用
ch.write(buffer);
写 ByteBuf 数据时调用
buf.readBytes(javaChannel(), expectedWrittenBytes)