NOTE
2.79 1. Instantiate the Channel
Instantiate NioServerSocketChannel through reflection, configure it as non-blocking, and create channelId, Unsafe, and pipeline.
This is a historical learning note and may contain outdated or incomplete understanding.
1. Code to Analyze
// Instantiate the channel.
channel = channelFactory.newChannel();
2. Invoke the Channel.class Constructor Through Reflection to Create an Instance
public T newChannel() {
try {
// Invoke the NioServerSocketChannel constructor through reflection.
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
The constructor of NioServerSocketChannel is invoked through reflection.
3. NioServerSocketChannel
3.1. Class Hierarchy

NioServerSocketChannel->ServerSocketChannel->ServerChannel->Channel
3.2. Constructor
public NioServerSocketChannel() {
// newSocket creates ServerSocketChannel -- the underlying JDK NIO class.
// provider.openServerSocketChannel()
// Then invoke the constructor.
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
3.2.1. Create the Underlying JDK NIO ServerSocketChannel
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
provider.openServerSocketChannel() ultimately creates a ServerSocketChannelImpl. ServerSocketChannelImpl extends ServerSocketChannel, and it is the JDK’s underlying channel implementation.
3.2.2. Listen for the OP_ACCEPT Event
Continue tracing the constructor of NioServerSocketChannel.
public NioServerSocketChannel(ServerSocketChannel channel) {
// AbstractNioMessageChannel->AbstractNioChannel[√]->AbstractChannel[√]
// The listen event passed in is SelectionKey.OP_ACCEPT.
super(null, channel, SelectionKey.OP_ACCEPT);
// Use a config class to store the NioServerSocketChannel just created
// and the ServerSocket corresponding to the channel.
// ServerSocketChannelConfig is specifically used for configuration.
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
It invokes the parent constructor and passes SelectionKey.OP_ACCEPT as the event to listen for.
A ServerSocketChannelConfig is created to configure options, attrs, and other properties.
Continue tracing the parent-class constructor, AbstractNioChannel.
3.2.3. Configure the Channel as Non-Blocking
- AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
// AbstractChannel constructor.
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
// Configure non-blocking mode.
ch.configureBlocking(false);
} 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);
}
}
This configures the selector channel as non-blocking and continues by invoking the parent constructor.
3.2.4. Create channelId, Unsafe, and pipeline
- AbstractChannel
protected AbstractChannel(Channel parent) {
this.parent = parent;
// Create channelId.
id = newId();
// Create Unsafe.
unsafe = newUnsafe();
// Create pipeline.
pipeline = newChannelPipeline();
}
3.2.4.1. Create channelId
We know that every channel has a unique channelId. As shown below, it is determined by machineId, processId, sequence, timestamp, and random.
private DefaultChannelId() {
data = new byte[MACHINE_ID.length + PROCESS_ID_LEN + SEQUENCE_LEN + TIMESTAMP_LEN + RANDOM_LEN];
int i = 0;
// machineId
System.arraycopy(MACHINE_ID, 0, data, i, MACHINE_ID.length);
i += MACHINE_ID.length;
// processId
i = writeInt(i, PROCESS_ID);
// sequence
i = writeInt(i, nextSequence.getAndIncrement());
// timestamp (kind of)
i = writeLong(i, Long.reverse(System.nanoTime()) ^ System.currentTimeMillis());
// random
int random = PlatformDependent.threadLocalRandom().nextInt();
i = writeInt(i, random);
assert i == data.length;
hashCode = Arrays.hashCode(data);
}
3.2.4.2. Create Unsafe
This Unsafe actually encapsulates operations from the underlying JDK NIO package; Netty calls it Unsafe.
3.2.4.3. newChannelPipeline
protected DefaultChannelPipeline newChannelPipeline() {
// Creates DefaultChannelPipeline.
return new DefaultChannelPipeline(this);
}
3.2.4.3.1. ChannelPipeline Class Hierarchy

ChannelOutboundInvoker defines which Handler operations belong to outbound operations in the pipeline, while ChannelInboundInvoker defines inbound operations.
ChannelOutboundInvoker
bind
connect
connect
disconnect
close
deregister
bind
connect
connect
disconnect
close
deregister
read
write
write
flush
writeAndFlush
writeAndFlush
newPromise
newProgressivePromise
newSucceededFuture
newFailedFuture
voidPromise
ChannelInboundInvoker
fireChannelRegistered
fireChannelUnregistered
fireChannelActive
fireChannelInactive
fireExceptionCaught
fireUserEventTriggered
fireChannelRead
fireChannelReadComplete
fireChannelWritabilityChanged
- Constructor
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
// The pipeline is a linked list. Each element is an AbstractChannelHandlerContext
// that wraps a Handler.
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
The TailContext above is a ChannelInboundHandler. HeadContext is both a ChannelOutboundHandler and a ChannelInboundHandler. Both extend AbstractChannelHandlerContext, forming a doubly linked-list structure.

Discussion
Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub