网络编程与Netty(四) Netty源码-(Ⅴ)

Unsafe系列源码

​ Unsafe 是 Channel 的内部接口,聚合在 Channel 中协助进行网络读写相关的操作,因为它的设计初衷就是 Channel 的内部辅助类,不应该被 Netty 框架的上层使用者调用,所以被命名为 Unsafe。不能将它与JDK的Unsafe弄混,并不是同一个类。Unsafe 用于处理 Channel 对应网络 IO 的底层操作,相关的网络操作基本上最终也会交给Unsafe完成,所以基本上一个 Channel 的实现中就有一个对应的 Unsafe 类的实现。下面来看看NioMessageUnsafe和NioSocketChannelUnsafe两个类的继承关系:

NioMessageUnsafe

在这里插入图片描述

NioSocketChannelUnsafe

在这里插入图片描述

Unsafe和NioUnsafe

在这里插入图片描述

Unsafe 接口中定义了 socket 相关操作,包括 SocketAddress 获取、selector 注册、网卡 端口绑定、socket 建连与断连、socket 写数据。这些操作都和 jdk 底层 socket 相关。

NioUnsafe 接口主要增加了一些和 nio 操作联系比较紧密的操作。

AbstractUnsafe

​ 在这个类中方法比较多,这里找几个比较重要的来讲解,这些方法可能是之前讲解的方法中调用到的,也可能是下一节内容中会调用的方法,如果有不明白或写错的欢迎在评论区指出,笔者定期会看博客消息。

方法register
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            } else if (AbstractChannel.this.isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
            } else if (!AbstractChannel.this.isCompatible(eventLoop)) {
                promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
            } else {
              //前面一堆的逻辑都是在做校验,如果校验通过了就会来到这个代码块
              //这里首先将当前的Channel与参数中的EventLoop建立关系
                AbstractChannel.this.eventLoop = eventLoop;
            //这个首先进行线程的本地性校验,也就是前面说过得,校验现在执行的线程是不是与Channel关联
            //的EventLoop中的线程,如果是则直接执行register0方法,如果不是则调用EventLoop.execute来执行
            //最终不管走哪里都是执行register0方法,所以接下来进入register0方法
                if (eventLoop.inEventLoop()) {
                    this.register0(promise);
                } else {
                    try {
                        eventLoop.execute(new Runnable() {
                            public void run() {
                                AbstractUnsafe.this.register0(promise);
                            }
                        });
                    } catch (Throwable var4) {
                        AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
                        this.closeForcibly();
                        AbstractChannel.this.closeFuture.setClosed();
                        this.safeSetFailure(promise, var4);
                    }
                }
            }
        }
private void register0(ChannelPromise promise) {
            try {
                if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = this.neverRegistered;
                //调用doRegister方法,这个方法说了很多次了吧,大概就是将Channel注册到EventLoop中的
                //Selector选择器中,但是此时还未将关注的事件注册进去。
                AbstractChannel.this.doRegister();
                this.neverRegistered = false;
                AbstractChannel.this.registered = true;
             //执行ChannelInitializer.initChannel,以保证handler被添加到pipeline上。
                AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
                this.safeSetSuccess(promise);
             //触发了ChannelRegistered事件,跟ChannelActive作用一样都是在某个特定时间点执行
                AbstractChannel.this.pipeline.fireChannelRegistered();
                if (AbstractChannel.this.isActive()) {
                    if (firstRegistration) {
                //如果Channel处于活动状态且是第一次注册,则触发ChannelActive事件。
                //为什么要判断第一次呢?因为可能是之前register然后deregister了,现在有register的
                //防止多次触发ChannelActive事件
                        AbstractChannel.this.pipeline.fireChannelActive();
                    } else if (AbstractChannel.this.config().isAutoRead()) {
               //如果Channel处于活动状态且不是第一次注册,并设置了AutoRead,则调用beginRead
            //beginRead最重要的就是doBeginRead方法,这个之前讲过,就是将Channel关注的事件注册到Selector
                        this.beginRead();
                    }
                }
            } catch (Throwable var3) {
                this.closeForcibly();
                AbstractChannel.this.closeFuture.setClosed();
                this.safeSetFailure(promise, var3);
            }
        }
方法bind

​ bind 方法主要用于绑定指定端口。对于服务端,用于绑定监听端口,并设置 backlog 参数;对于客户端,用于指定客户端 Channel 的本地绑定 Socket 地址。

public final void bind(SocketAddress localAddress, ChannelPromise promise) {
            this.assertEventLoop();
            if (promise.setUncancellable() && this.ensureOpen(promise)) {
                if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                    AbstractChannel.logger.warn("A non-root user can't receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
                }
           //先获取绑定前的Channel状态是否为active激活状态
                boolean wasActive = AbstractChannel.this.isActive();
                try {
           //doBind方法是一个抽象方法,在NioSocketChannel和NioServerSocketChannel中有不同的实现
                    AbstractChannel.this.doBind(localAddress);
                } catch (Throwable var5) {
                    this.safeSetFailure(promise, var5);
                    this.closeIfClosed();
                    return;
                }
           //这里表示如果绑定前为未激活,绑定后为激活,说明Channel是在此次绑定过程中激活的
           //那么就将fireChannelActive交给EventLoop中的线程执行
                if (!wasActive && AbstractChannel.this.isActive()) {
                    this.invokeLater(new Runnable() {
                        public void run() {
                            AbstractChannel.this.pipeline.fireChannelActive();
                        }
                    });
                }
                this.safeSetSuccess(promise);
            }
        }

​ 先来看看两种doBind方法的实现,然后在看看invokeLater方法。

NioServerSocketChannelprotected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
       //可以设置backlog参数
            this.javaChannel().bind(localAddress, this.config.getBacklog());
        } else {
            this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
        }
    }
NioSocketChannelprivate void doBind0(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            SocketUtils.bind(this.javaChannel(), localAddress);
        } else {
            SocketUtils.bind(this.javaChannel().socket(), localAddress);
        }
    }

​ 接下来看invokeLater,主要是将fireChannelActive打包成Runnable交给EventLoop执行

private void invokeLater(Runnable task) {
            try {
                AbstractChannel.this.eventLoop().execute(task);
            } catch (RejectedExecutionException var3) {
                AbstractChannel.logger.warn("Can't invoke task later as EventLoop rejected it", var3);
            }
        }
方法disconnect

​ disconnect主要是用于断开连接,其中最重要的方法是doDisconnect方法。

public final void disconnect(ChannelPromise promise) {
            this.assertEventLoop();
            if (promise.setUncancellable()) {
            //这里的思路跟bind方法一样,记录下disconnect前通道是否激活
                boolean wasActive = AbstractChannel.this.isActive();

                try {
                //最重要的就是这个方法
                //doDisconnect是抽象方法,在NioServerSocketChannel和NioSocketChannel中有不同实现
                    AbstractChannel.this.doDisconnect();
                } catch (Throwable var4) {
                    this.safeSetFailure(promise, var4);
                    this.closeIfClosed();
                    return;
                }
			//表示如果原来是激活的,而现在是未激活状态
                if (wasActive && !AbstractChannel.this.isActive()) {
                 //invokeLaster方法上面讲解bind方法时已经讲过了,就是将fireChannelInactive打包成
                 //Runnable交给EventLoop去执行。
                    this.invokeLater(new Runnable() {
                        public void run() {
                            AbstractChannel.this.pipeline.fireChannelInactive();
                        }
                    });
                }

                this.safeSetSuccess(promise);
                this.closeIfClosed();
            }
        }

​ 接下来看看doDisconnect两种实现

protected void doDisconnect() throws Exception {
   //在NioServerSocketChannel中的实现直接抛出异常
        throw new UnsupportedOperationException();
    }
protected void doDisconnect() throws Exception {
   //接着进入doClose
        this.doClose();
    }
protected void doClose() throws Exception {
        super.doClose();
    //最后调用了Channel的close方法来关闭连接
        this.javaChannel().close();
    }

​ 可以发现在NioServerSocketChannel 是无所谓断开连接一说的,NioSocketChanne 则调用了SocketChannel 关闭连接,然后调用 fireChannelInactive 方法。

方法write、Flush

​ write 方法实际上将消息添加到发送缓存区 ChannelOutboundBuffer 中,并不是真正的写Channel。当调用 channel 的 write 方法写数据时,这个数据被一系列 ChannelOutboundHandler处理之后,它被放进这个缓冲区中,并没有真正把数据写到socketChannel中。然后再调用channel的flush方法,flush 会把 outboundBuffer中数据真正写到socketChannel。

public final void write(Object msg, ChannelPromise promise) {
            this.assertEventLoop();
   		//获取到发送缓冲区ChannelOutboundBuffer,并对其进行校验
            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null) {
                this.safeSetFailure(promise, AbstractChannel.WRITE_CLOSED_CHANNEL_EXCEPTION);
                ReferenceCountUtil.release(msg);
            } else {
              //outboundBuffer校验通过
                int size;
                try {
                   //调用filterOutboundMessage对msg进行过滤
                    msg = AbstractChannel.this.filterOutboundMessage(msg);
                   //计算msg的长度
                    size = AbstractChannel.this.pipeline.estimatorHandle().size(msg);
                    if (size < 0) {
                        size = 0;
                    }
                } catch (Throwable var6) {
                    this.safeSetFailure(promise, var6);
                    ReferenceCountUtil.release(msg);
                    return;
                }
               //将处理好的msg放入到outboundBuffer中
                outboundBuffer.addMessage(msg, size, promise);
            }
        }

​ 将msg放入到outboundBuffer后,此时并未写到socketChannel中,当调用flush方法时才全部写入到Channel中。

public final void flush() {
            this.assertEventLoop();
            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer != null) {
                outboundBuffer.addFlush();
              //进入flush0看看
                this.flush0();
            }
        }
protected void flush0() {
            if (!this.inFlush0) {
                ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
                if (outboundBuffer != null && !outboundBuffer.isEmpty()) {
                    this.inFlush0 = true;
                    if (!AbstractChannel.this.isActive()) {
                        try {
                            if (AbstractChannel.this.isOpen()) {
                                outboundBuffer.failFlushed(AbstractChannel.FLUSH0_NOT_YET_CONNECTED_EXCEPTION, true);
                            } else {
                                outboundBuffer.failFlushed(AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                            }
                        } finally {
                            this.inFlush0 = false;
                        }
                    } else {
                        try {
                         //整个方法最重要的一步就是调用Channel的doWrite方法
                         //doWrite方法其实就是在NioSocketChannel中讲到的doWrite方法,这里不在赘述。
                         //doWrite方法就是具体的将数据从outboundBuffer写到Channel的地方
                            AbstractChannel.this.doWrite(outboundBuffer);
                        } catch (Throwable var15) {
                            Throwable t = var15;
                            if (var15 instanceof IOException && AbstractChannel.this.config().isAutoClose()) {
                                this.close(this.voidPromise(), var15, AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                            } else {
                                try {
                                    this.shutdownOutput(this.voidPromise(), t);
                                } catch (Throwable var14) {
                                    this.close(this.voidPromise(), var14, AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                                }
                            }
                        } finally {
                            this.inFlush0 = false;
                        }
                    }
                }
            }
        }
AbstractNioUnsafe

​ AbstractNioUnsafe是 AbstractUnsafe类的NIO实现,它主要实现了 connect ,finishConnect等方法。

方法connect
public final void connect(final SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
            if (promise.setUncancellable() && this.ensureOpen(promise)) {
                try {
                    if (AbstractNioChannel.this.connectPromise != null) {
                        throw new ConnectionPendingException();
                    }
                    boolean wasActive = AbstractNioChannel.this.isActive();
                  //这里调用doConnect,执行连接操作,这个方法主要有三种情况
                  //1.连接成功返回true
                  //2.连接正在进行,服务端还没有返回ACK应答,结果不确定,返回false
                  //3.连接失败,抛出异常
                    if (AbstractNioChannel.this.doConnect(remoteAddress, localAddress)) {
                       //第一种,连接成功则执行fulfillConnectPromise方法。
                        this.fulfillConnectPromise(promise, wasActive);
                    } else {
                       //第二种,连接正在进行,结果还无法确定成功失败
                        AbstractNioChannel.this.connectPromise = promise;
                        AbstractNioChannel.this.requestedRemoteAddress = remoteAddress;
                     //获取连接超时时长设置
                        int connectTimeoutMillis = AbstractNioChannel.this.config().getConnectTimeoutMillis();
                     //设置定时任务,如果超时时间到了,还没有完成连接,则关闭连接,释放资源
                        if (connectTimeoutMillis > 0) {
                            AbstractNioChannel.this.connectTimeoutFuture = AbstractNioChannel.this.eventLoop().schedule(new Runnable() {
                                public void run() {
                                    ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
                                    ConnectTimeoutException cause = new ConnectTimeoutException("connection timed out: " + remoteAddress);
                                    if (connectPromise != null && connectPromise.tryFailure(cause)) {
                                        AbstractNioUnsafe.this.close(AbstractNioUnsafe.this.voidPromise());
                                    }
                                }
                            }, (long)connectTimeoutMillis, TimeUnit.MILLISECONDS);
                        }
                      //设置监听器,接收连接完成通知,判断连接是否被取消,如果被取消则关闭连接,释放资源
                        promise.addListener(new ChannelFutureListener() {
                            public void operationComplete(ChannelFuture future) throws Exception {
                                if (future.isCancelled()) {
                                    if (AbstractNioChannel.this.connectTimeoutFuture != null) {
                                        AbstractNioChannel.this.connectTimeoutFuture.cancel(false);
                                    }
                                    AbstractNioChannel.this.connectPromise = null;
                                    AbstractNioUnsafe.this.close(AbstractNioUnsafe.this.voidPromise());
                                }
                            }
                        });
                    }
                } catch (Throwable var6) {
                    promise.tryFailure(this.annotateConnectException(var6, remoteAddress));
                    this.closeIfClosed();
                }
            }
        }

​ 接下来看看fulfillConnectPromise方法:

private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
            if (promise != null) {
                boolean active = AbstractNioChannel.this.isActive();
                boolean promiseSet = promise.trySuccess();
                if (!wasActive && active) {
                  //最重要的是这里,调用pipeline的fireChannelActive方法
                    AbstractNioChannel.this.pipeline().fireChannelActive();
                }
                if (!promiseSet) {
                    this.close(this.voidPromise());
                }
            }
        }

​ channelActive事件是一个入站事件,那么首先就会调用到HeadContext.channelActive方法:

public void channelActive(ChannelHandlerContext ctx) throws Exception {
		//向后传递
            ctx.fireChannelActive();
        //重点来看看这个方法
            this.readIfIsAutoRead();
        }
private void readIfIsAutoRead() {
            if (DefaultChannelPipeline.this.channel.config().isAutoRead()) {
                //这里调用了channel.read方法
                DefaultChannelPipeline.this.channel.read();
            }
        }
public void read(ChannelHandlerContext ctx) {
   		//可以看到这里调用了beginRead方法,这个方法熟悉吧?前面介绍过很多次了。
            this.unsafe.beginRead();
        }
public final void beginRead() {
            this.assertEventLoop();
            if (AbstractChannel.this.isActive()) {
                try {
                  //重点在doBeginRead方法
                    AbstractChannel.this.doBeginRead();
                } catch (final Exception var2) {
                    this.invokeLater(new Runnable() {
                        public void run() {
                            AbstractChannel.this.pipeline.fireExceptionCaught(var2);
                        }
                    });
                    this.close(this.voidPromise());
                }
            }
        }
protected void doBeginRead() throws Exception {
        SelectionKey selectionKey = this.selectionKey;
        if (selectionKey.isValid()) {
            this.readPending = true;
            int interestOps = selectionKey.interestOps();
            if ((interestOps & this.readInterestOp) == 0) {
             //这里是真正的将channel关注的事件注册到EventLoop的selector选择器中
                selectionKey.interestOps(interestOps | this.readInterestOp);
            }
        }
    }
方法finishConnect
public final void finishConnect() {
            assert AbstractNioChannel.this.eventLoop().inEventLoop();
            try {
                boolean wasActive = AbstractNioChannel.this.isActive();
              //doFinishConnect方法主要就是判断channel是否完成connect操作,如果没完成则抛出异常
                AbstractNioChannel.this.doFinishConnect();
              //fulfillConnectPromise方法上面刚讲过,这里不赘述
                this.fulfillConnectPromise(AbstractNioChannel.this.connectPromise, wasActive);
            } catch (Throwable var5) {
                this.fulfillConnectPromise(AbstractNioChannel.this.connectPromise, this.annotateConnectException(var5, AbstractNioChannel.this.requestedRemoteAddress));
            } finally {
                if (AbstractNioChannel.this.connectTimeoutFuture != null) {
                    AbstractNioChannel.this.connectTimeoutFuture.cancel(false);
                }
                AbstractNioChannel.this.connectPromise = null;
            }
        }
NioMessageUnsafe

​ 这个是专门来处理客户端连接的 unsafe,里面的方法很少,只有一个 read 方法。

public void read() {
            assert AbstractNioMessageChannel.this.eventLoop().inEventLoop();
            ChannelConfig config = AbstractNioMessageChannel.this.config();
            ChannelPipeline pipeline = AbstractNioMessageChannel.this.pipeline();
            Handle allocHandle = AbstractNioMessageChannel.this.unsafe().recvBufAllocHandle();
            allocHandle.reset(config);
            boolean closed = false;
            Throwable exception = null;
            try {
                int localRead;
                try {
                    do {
                      //整个read方法最重要的也就是这个doReadMessages方法,
                      //执行完后生成的子channel也就加入到readBuf中了
                        localRead = AbstractNioMessageChannel.this.doReadMessages(this.readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }
                        allocHandle.incMessagesRead(localRead);
                    } while(allocHandle.continueReading());
                } catch (Throwable var11) {
                    exception = var11;
                }
                localRead = this.readBuf.size();
             //这里遍历生成的子channel加入到,触发ChannelRead事件,
             //ChannelRead事件入站事件,所以进入到ServerBootStrapAccept的channelRead方法看看,
             //因为在HeadContext和TailContext中的ChannelRead没有做处理
                for(int i = 0; i < localRead; ++i) {
                    AbstractNioMessageChannel.this.readPending = false;
                    pipeline.fireChannelRead(this.readBuf.get(i));
                }
                this.readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();
                if (exception != null) {
                    closed = AbstractNioMessageChannel.this.closeOnReadError(exception);
                    pipeline.fireExceptionCaught(exception);
                }
                if (closed) {
                    AbstractNioMessageChannel.this.inputShutdown = true;
                    if (AbstractNioMessageChannel.this.isOpen()) {
                        this.close(this.voidPromise());
                    }
                }
            } finally {
                if (!AbstractNioMessageChannel.this.readPending && !config.isAutoRead()) {
                    this.removeReadOp();
                }
            }
        }

​ 先进入doReadMessages方法看看,然后在看看ServerBootStrapAccept的channelRead方法(ServerBootStrapAccept这个类后面会提到)

protected int doReadMessages(List<Object> buf) throws Exception {
    //借用SocketUtils.accept方法接受连接,生成一个子channel
        SocketChannel ch = SocketUtils.accept(this.javaChannel());
        try {
            if (ch != null) {
             //将这个子Channel包装成一个NioSocketChannel,然后加入到buf中
             //需要注意的是,在new NioSocketChannel()时,会创建出channel中的pipeline
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable var6) {
            logger.warn("Failed to create a new channel from an accepted socket.", var6);
            try {
                ch.close();
            } catch (Throwable var5) {
                logger.warn("Failed to close a socket.", var5);
            }
        }
        return 0;
    }
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    //这个msg是什么?它是一个子channel,而且包装成NioSocketChannel。
            final Channel child = (Channel)msg;
    	//将提前设置好的handler加入到pipeline中,还有一些其他的ops参数等等
    	//也就是channelRead这个方法完成了channel的一些配置
            child.pipeline().addLast(new ChannelHandler[]{this.childHandler});
            AbstractBootstrap.setChannelOptions(child, this.childOptions, ServerBootstrap.logger);
            Entry[] var4 = this.childAttrs;
            int var5 = var4.length;
            for(int var6 = 0; var6 < var5; ++var6) {
                Entry<AttributeKey<?>, Object> e = var4[var6];
                child.attr((AttributeKey)e.getKey()).set(e.getValue());
            }
            try {
                this.childGroup.register(child).addListener(new ChannelFutureListener() {
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            ServerBootstrap.ServerBootstrapAcceptor.forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable var8) {
                forceClose(child, var8);
            }
        }
NioByteUnsafe

​ NioByteUnsafe 负责具体的网络数据的读取,里面的方法不多,closeOnRead 处理读关闭(半关闭),handleReadException 处理读异常,最关键的是 read 方法。

方法read
public final void read() {
    //首先,获取 NioSocketChannel 的 SocketChannelConfig,它主要用于设置客户端连接的 TCP 参数
            ChannelConfig config = AbstractNioByteChannel.this.config();
            if (AbstractNioByteChannel.this.shouldBreakReadReady(config)) {
                AbstractNioByteChannel.this.clearReadPending();
            } else {
                ChannelPipeline pipeline = AbstractNioByteChannel.this.pipeline();
                ByteBufAllocator allocator = config.getAllocator();
                Handle allocHandle = this.recvBufAllocHandle();
                allocHandle.reset(config);
                ByteBuf byteBuf = null;
                boolean close = false;
                try {
                    do {
                  //Nio是面向缓冲区的,所以读数据,肯定需要先分配个缓冲区
                        byteBuf = allocHandle.allocate(allocator);
                  //读取Channel中的数据到bytebuf中
                       allocHandle.lastBytesRead(AbstractNioByteChannel.this.doReadBytes(byteBuf));
                  //如果读取字节数<=0则清空释放缓冲区,终止读取
                        if (allocHandle.lastBytesRead() <= 0) {
                            byteBuf.release();
                            byteBuf = null;
                            close = allocHandle.lastBytesRead() < 0;
                            if (close) {
                                AbstractNioByteChannel.this.readPending = false;
                            }
                            break;
                        }
                        allocHandle.incMessagesRead(1);
                        AbstractNioByteChannel.this.readPending = false;
                    //读取数据冲个,则触发ChannelRead事件,交给Handler处理这些数据
                        pipeline.fireChannelRead(byteBuf);
                        byteBuf = null;
                    } while(allocHandle.continueReading());

                    allocHandle.readComplete();
                    pipeline.fireChannelReadComplete();
                    if (close) {
                        this.closeOnRead(pipeline);
                    }
                } catch (Throwable var11) {
                    this.handleReadException(pipeline, byteBuf, var11, close, allocHandle);
                } finally {
                    if (!AbstractNioByteChannel.this.readPending && !config.isAutoRead()) {
                        this.removeReadOp();
                    }
                }
            }
        }

​ 看到这里,基本对Netty里面的组件源码有所认识了,但是仅仅是对单一的组件有所了解,放在整个Netty里,估计还是无法联系起来,再讲完下一个小节EventLoop系列源码后就是对Netty整体流程开始讲解,会将前面所学的东西串联起来。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值