Take a small Netty demo (source version 4.1.50.final) as an example

From b.b ind (PORT) to follow up

Finally, the AbstractBootstrap#doBind method is called

private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if(regFuture.cause() ! =null) {
        return regFuture;
 }   if (regFuture.isDone()) {  // At this point we know that the registration was complete and successful.  ChannelPromise promise = channel.newPromise();  doBind0(regFuture, channel, localAddress, promise);  return promise;  } else {  // Registration future is almost always fulfilled already, but just in case it's not.  final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);  regFuture.addListener(new ChannelFutureListener() {  @Override  public void operationComplete(ChannelFuture future) throws Exception {  Throwable cause = future.cause();  if(cause ! =null) {  // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an  // IllegalStateException once we try to access the EventLoop of the Channel.  promise.setFailure(cause);  } else {  // Registration was successful, so set the correct executor to use.  // See https://github.com/netty/netty/issues/2586  promise.registered();   doBind0(regFuture, channel, localAddress, promise);  }  }  });  return promise;  } } Copy the code

Here is the main code created on the server side

Create a Channel

The main summary is as follows

1. Create a channel by reflecting the NioServerSocketChannel class passed in demo

2. In the NioServerSocketChannel constructor

  • Create a ServerSocketChannel using the Java underlying SelectorProvider
  • Call the superclass constructor to set blocking mode, create ID, unsafe, pipeline, and so on
  • Create NioServerSocketChannelConfig, facilitate to configuration parameters

The initAndRegister method in the first line of the method body creates and initializes a channel

final ChannelFuture initAndRegister(a) {
    Channel channel = null;
    try {
        channel = channelFactory.newChannel();   // Create channel
        init(channel);
 } catch (Throwable t) {  / / to omit  }   ChannelFuture regFuture = config().group().register(channel);  if(regFuture.cause() ! =null) {  if (channel.isRegistered()) {  channel.close();  } else {  channel.unsafe().closeForcibly();  }  }  return regFuture; } Copy the code

Create a channel in the channelFactory. NewChannel () in this line of code, there will be calls to ReflectiveChannelFactory# newChannel this method

public T newChannel(a) {
    try {
        return constructor.newInstance();
    } catch (Throwable t) {
        / / to omit
 } } Copy the code

As you can see, a channel is created using reflection, i.e., a channel is created using reflection from the Constructor of channelFactory, which is initialized when the channel method is called in the demo, as shown below

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class) // It is initialized herechannelFactory
/ / to omitCopy the code

In the AbstractBootstrap#channel method

public B channel(Class<? extends C> channelClass) {
    return channelFactory(new ReflectiveChannelFactory<C>(
        ObjectUtil.checkNotNull(channelClass, "channelClass")
    ));
}
Copy the code

The AbstractBootstrap#channelFactory method is called

public B channelFactory(ChannelFactory<? extends C> channelFactory) {
    ObjectUtil.checkNotNull(channelFactory, "channelFactory");
    if (this.channelFactory ! =null) {
        throw new IllegalStateException("channelFactory set already");
    }
  this.channelFactory = channelFactory;  return self(); } Copy the code

Now that a NioServerSocketChannel is created by reflection, look at the constructor of the NioServerSocketChannel class

public NioServerSocketChannel(a) {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));  
}
Copy the code

Take a look at the NioServerSocketChannel#newSocket method

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        return provider.openServerSocketChannel(); . / / the provider is DEFAULT_SELECTOR_PROVIDER for SelectorProvider provider ()
    } catch (IOException e) {
        / / to omit
 } } Copy the code

The above constructor calls another constructor

public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
Copy the code

The super method calls the parent class to do some simple initialization, mainly in AbstractNioChannel and AbstractChannel classes

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    try {
 ch.configureBlocking(false);  } catch (IOException e) {  / / to omit  } } Copy the code
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
} Copy the code

NioServerSocketChannelConfig is introduced to the Java at the bottom of the ServerSocketChannel convenient to do some configuration in the future

Initialize the channel

To summarize, we have done these things

  • Set user-specified options and attrs to channel config
  • Add the handler passed in by the user to the channel pipeline
  • Create a special handler called ServerBootstrapAcceptor (to handle new connections) and add it to the pipeline

The next step is to examine the process of initializing a channel, namely the line init(channel)

This calls the ServerBootstrap#init method

void init(Channel channel) {
    // The newOptionsArray method takes the user-set options and sets them into the config of the channel
    setChannelOptions(channel, newOptionsArray(), logger);
    //attrs0 this method takes the user's attrs setting to a channel
    setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
  ChannelPipeline p = channel.pipeline();   final EventLoopGroup currentChildGroup = childGroup;  final ChannelHandler currentChildHandler = childHandler;  finalEntry<ChannelOption<? >, Object>[] currentChildOptions; // Get the childOptions used to configure childHandler  synchronized (childOptions) {  currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);  }  // Get the childAttrs used to configure childHandler  finalEntry<AttributeKey<? >, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);  p.addLast(new ChannelInitializer<Channel>() {  @Override  public void initChannel(final Channel ch) {  final ChannelPipeline pipeline = ch.pipeline();  // Get the handler that was added when the childHandler method was called in demo  ChannelHandler handler = config.handler();  if(handler ! =null) {  pipeline.addLast(handler);  }   ch.eventLoop().execute(new Runnable() {  @Override  public void run(a) {  / / ServerBootstrapAcceptor is a special handler, to handle the new connection access  pipeline.addLast(new ServerBootstrapAcceptor(  ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));  }  });  }  }); } Copy the code

Netty server startup (2) — Register selector and port binding