“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”

InitAndRegister (); initAndRegister (); initAndRegister (); We need to get the SocketChaennel, get the Selector, register the channel with the Selector, bind the port, handle the event! So, the same Netty is based on NIO development, also indispensable several steps, so far, we have learned, Selector creation, SocketChannel creation, Selector registration, today we are going to learn is channel binding port!

One, source code entry

We went back to: io.net ty. The bootstrap. AbstractBootstrap# doBind method:

private ChannelFuture doBind(final SocketAddress localAddress) {
    finalChannelFuture regFuture = initAndRegister(); . Ignore...if(regFuture.isDone()) { ........................ Ignore...// Registers the data binding channel and triggers the event
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else{... Ignore...returnpromise; }}Copy the code

InitAndRegister is used to create, initialize, and register a Channel.

Without much ado, let’s go straight into the doBind0 method!

private static void doBind0( final ChannelFuture regFuture, final Channel channel, 
                            final SocketAddress localAddress, final ChannelPromise promise) {

        // Call this method before channelRegistered () is triggered. Give the user handler a chance to set up
        // Pipes in its channelRegistered () implementation.
        channel.eventLoop().execute(() -> {
            if (regFuture.isSuccess()) {
                channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            } else{ promise.setFailure(regFuture.cause()); }}); }Copy the code

Needless to say, we focus on:

channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
Copy the code

Follow up to the bind method:

@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    //this.bind head
    return pipeline.bind(localAddress, promise);
}
Copy the code

Here we see a strange line of code that seems to invoke a channel propagation, so we continue:

@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    return tail.bind(localAddress, promise);
}
Copy the code

As we all know,tail node is the tail node in the channel. From the analysis of last class, we can know that the current pipeline structure is as follows:

Bind is ChannelOutboundInvoker. Bind is ChannelOutboundInvoker. Bind is ChannelOutboundInvoker. TailContext and ServerBootstrapAcceptor do not bind, so we end up in HeadContext code: (Note, there is no need to know, hey pipeline is how to spread, the following chapter is to add, find, register a complete source analysis of pipeline, here for a better understanding of the students, first do not involve so much!)

We enter: io.net ty. Channel. DefaultChannelPipeline. HeadContext# bind

@Override
public void bind( ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
    unsafe.bind(localAddress, promise);
}
Copy the code

Unsafe is of type NioMessageUnsafe, and its parent class is AbstractNioUnsafe, so we’ll look at the source code for AbstractNioUnsafe:

Two, source code analysis

@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {... Ignore...//false
    boolean wasActive = isActive();
    try {
        // The underlying binding port of the JDK NioServerSocketChannel
        doBind(localAddress);
    } catch(Throwable t) { ............................... Ignore...return;
    }
    //isActive true
    if(! wasActive && isActive()) {// Triggers the Active event
        invokeLater(new Runnable() {
            @Override
            public void run(a) { pipeline.fireChannelActive(); }}); } safeSetSuccess(promise); }Copy the code

Let’s ignore some of the branch code and look at our mainline code, which first checks whether the channel is active:

boolean wasActive = isActive();
Copy the code

At this point, the channel is not bound to a port number, so false is returned.

doBind(localAddress);
Copy the code

We start calling the underlying JDK logic to bind the channel, and we go to the doBind method, and you have to remember, we’re initializing the server, and we’re giving NioServerSocketChannel type

@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    //Netty calls the underlying BIND () method of the JDK, depending on the JDK version.
    // I'm using JDK8, so I call the BIND () method of the JDK's native Channel.
    // After doBind(), the server JDK's native Channel is actually port bound.
    if (PlatformDependent.javaVersion() >= 7) {
        // The underlying bindings of the JDK
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        // The underlying bindings of the JDKjavaChannel().socket().bind(localAddress, config.getBacklog()); }}Copy the code

Netty will register slightly differently depending on the JDK version. For example, in JDK8, the if branch will execute:

javaChannel().bind(localAddress, config.getBacklog());
Copy the code

This line of code, I believe you are also very familiar with, this is the JDK NIO binding port code, we recall JDK NIO how to bind the port:

The above JDK NIO registration method, both code is the same! With the binding complete, we return to the main line code:

if(! wasActive && isActive()) {// Triggers the Active event
    invokeLater(new Runnable() {
        @Override
        public void run(a) { pipeline.fireChannelActive(); }}); }Copy the code

The wasActive attribute is false because it was not activated before. If the wasActive attribute is set to true, then the channel has been bound successfully. Call isActive() again and return true.

According to the previous analysis, the logic of this judgment is that it is not activated before binding, but activated after binding. Only two conditions are met at the same time will this branch be used, which can guarantee that the logic in this judgment logic will not be called repeatedly, but will only be called once after binding is successful!

We go to the logical branch, and the method is also asynchronous, but that’s ok, we’re still going to analyze it as synchronous. I’ll do a full analysis of asynchrony in the next class. All asynchrony in Netty has the same execution mode!

pipeline.fireChannelActive();
Copy the code

Again, by definition, event propagation is a pipeline. Let’s go in and see where it starts:

@Override
public final ChannelPipeline fireChannelActive(a) {
    AbstractChannelHandlerContext.invokeChannelActive(head);
    return this;
}
Copy the code

We can see that it’s propagated from the Head node,

static void invokeChannelActive(final AbstractChannelHandlerContext next) {
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeChannelActive();
    } else {
        executor.execute(new Runnable() {
            @Override
            public void run(a) { next.invokeChannelActive(); }}); }}Copy the code

Either synchronous or asynchronous, next. InvokeChannelActive () is called; Let’s go to the source logic:

private void invokeChannelActive(a) {
    if (invokeHandler()) {
        try {
            ((ChannelInboundHandler) handler()).channelActive(this);
        } catch(Throwable t) { .............................. Ignore... }}... Ignore... }Copy the code

Since our handler is of type Head:

@Override
public void channelActive(ChannelHandlerContext ctx) {
    // Broadcast events
    ctx.fireChannelActive();
    // Set it to read listening
    readIfIsAutoRead();
}
Copy the code

The first line propagates the event and looks down from the top node to propagate the ChannelActive method:

ctx.fireChannelActive();
Copy the code

The channelActive method is called to propagate an event.

ChannelActive is a ChannelInboundHandler method. Channelboundhandler is propagated from the Head node to the Tail node:

// Set it to read listening
readIfIsAutoRead();
Copy the code

Remember, when we registered NioServerSocketChannel again, we focused on 0, that is, we did not pay attention to any events, forget the students can go to the last class registered source code parsing view:

However, on the basis of our JDK NIO, our new server should focus on an OP_ACCEPT event, so we need to make a change to it to focus on new connection events. We will enter the source code of readIfIsAutoRead:

private void readIfIsAutoRead(a) {
    if(channel.config().isAutoRead()) { channel.read(); }}Copy the code

The logical branch of the judgment defaults to true

We’ve talked several times about why we chose this, but without making a statement here, we’ll jump right into the read source code:

@Override
public Channel read(a) {
    pipeline.read();
    return this;
}
Copy the code

Obviously, another event spread, we continue to follow:

@Override
public final ChannelPipeline read(a) {
    tail.read();
    return this;
}
Copy the code

Read is ChannelOutboundInvoker. This code looks up from the tail node. The default implementation is HeadContext. Let’s go to HeadContext:

Attention, students about how to spread the event will be very confused, don’t worry, according to my logic first, after studying pipeline, you will have a clear understanding of how to spread, according to my logic first!

io.netty.channel.DefaultChannelPipeline.HeadContext#read

@Override
public void read(ChannelHandlerContext ctx) {
    unsafe.beginRead();
}
Copy the code

We go to unsafe.beginread ();

@Override
public final void beginRead(a) {... Ignore...try {
        doBeginRead();
    } catch (finalException e) { .................... Ignore... }}Copy the code

We go to doBeginRead(); Method, pay attention to our is the default server is Unsafe AbstractNioMessageChannel type:

@Override
protected void doBeginRead(a) throws Exception {
    if (inputShutdown) {
        return;
    }
    super.doBeginRead();
}
Copy the code

Call the parent class’s doBeginRead method:

io.netty.channel.nio.AbstractNioChannel#doBeginRead

@Override
protected void doBeginRead(a) throws Exception {... Ignore...final int interestOps = selectionKey.interestOps();
    // If the current read event is 0 and the preset event is not 0, the logic is entered
    if ((interestOps & readInterestOp) == 0) { selectionKey.interestOps(interestOps | readInterestOp); }}Copy the code

Remember what readInterestOp we saved when we created NioServerSocketChannel? Let me take a screenshot to help you remember:

So if branch logic is equivalent to:

selectionKey.interestOps(0 | SelectionKey.OP_ACCEPT);
Copy the code

NioServerSokcetChannel’s selector is then bound to focus on connection events!

At this point, the server started successfully!!

Third, summary

  1. Call JDK native methods and bind a port to channel!
  2. Propagate the channelActive event for method callbacks!
  3. Change the NioServerSocketChannel selector’s default focus from 0 to selectionkey. OP_ACCEPT.
  4. Server started successfully!