先来看下Channel相关类图:
为了便于理解,上面的类图对层次关系做了一定的简化。
Channel接口定义了Netty中网络IO最顶层的框架。AbstractChannel是Channel接口的骨架实现,这个类中定义了channel的几个重要成员,id(ChannelId),unsafe(Unsafe),pipeline(DefaultChannelPipeline),eventLoop(EventLoop)。服务端channel(NioServerSocketChannel)和客户端channel(NioSocketChannel)都会逐层的调用父类构造函数,从而创建创建或绑定上述几个成员变量。AbstractNioChannel主要作用是负责Nio相关的部分,使用selector的方式监听读写事件。AbstractNioChannel有成员变量SelectionKey,成员变量SelectableChannel(保存底层jdk的channel),成员变量readInterestOp(OP_READ或OP_ACCEPT事件)。
AbstractNioChannel的构造函数:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch; // 保存底层jdk的channel
this.readInterestOp = readInterestOp; // 保存感兴趣的事件
try {
ch.configureBlocking(false); // 设置jdk的底层channel为非阻塞模式
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
NioSocketChannel和NioServerSocketChannel注册事件区别
接下来就是两大阵营服务端channel(AbstractNioMessageChannel,NioServerSocketChannel)和客户端channel(AbstractNioByteChannel,NioServerChannel)。它们都继承了AbstractNioChannel,说明它们都是通过selector轮询IO事件的,它们之间最大的区别是它们向selector注册的IO事件不同。
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT); // 服务端channel注册OP_ACCEPT事件
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket); // 调用 AbstractNioByteChannel 的构造函数
config = new NioSocketChannelConfig(this, socket.socket());
}
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ); // 客户端channel注册OP_READ事件,关心数据的读写
}
NioSocketChannel和NioServerSocketChannel抽象读事件的区别
服务端channel和客户端channel的另一个区别是底层的Unsafe不同。Unsafe负责具体实现是客户端channel还是客户端channel的协议。服务端channel对应的是NioMessageUnsafe,客户端channel对应的是NioByteUnsafe(NioSocketChannelUnsafe继承自它)。
从源码中来分析客户端unsafe的创建:
protected AbstractNioUnsafe newUnsafe() {
return new NioSocketChannelUnsafe(); // 客户端channel直接创建unsafe
}
// NioSocketChannelUnsafe 只有一个准备关闭的方法,大部分功能还是来自于 NioByteUnsafe
private final class NioSocketChannelUnsafe extends NioByteUnsafe {
@Override
protected Executor prepareToClose() {
try {
if (javaChannel().isOpen() && config().getSoLinger() > 0) {
// We need to cancel this key of the channel so we may not end up in a eventloop spin
// because we try to read or write until the actual close happens which may be later due
// SO_LINGER handling.
// See https://github.com/netty/netty/issues/4449
doDeregister();
return GlobalEventExecutor.INSTANCE;
}
} catch (Throwable ignore) {
// Ignore the error as the underlying channel may be closed in the meantime and so
// getSoLinger() may produce an exception. In this case we just return null.
// See https://github.com/netty/netty/issues/4449
}
return null;
}
}
服务端channel的unsafe,在AbstractNioMessageChannel中可以看到:
protected AbstractNioUnsafe newUnsafe() {
return new NioMessageUnsafe(); // 服务端channel创建unsafe
}
服务端channel和客户端channel的第三个不同:读取的内容不同。服务端channel的读是读取一条新的连接;客户端channel的读是读取IO数据。
我们来看服务端channel读事件相关的源码,NioMessageUnsafe的read方法:
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List
来看客户端channel的读事件,NioByteUnsafe的read方法:
@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);
// 从这里看出,客户端channel是读字节
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);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
NioSocketChannel和NioServerSocketChannel绑定channelConfig的区别
最后一点,服务端channel和客户端channel绑定的channeConfig不同。