Netty源码(十二):断开连接

在这里插入图片描述

1.正常关闭的逻辑

关闭连接会进入read状态
在这里插入图片描述
为了直接观察到关闭操作,我们注释上面的代码
在这里插入图片描述
关闭最后也会进入到read事件里面
1.allocHandle.lastBytesRead() <= 0这一行会发现数据小于零,释放byteBuf
2.进入到closeOnRead(pipeline);完成关闭事件

		@Override
        public final void read() {
            final ChannelConfig config = config();
            if (shouldBreakReadReady(config)) {
                clearReadPending();
                return;
            }
            final ChannelPipeline pipeline = pipeline();
            // 内存分配器
            final ByteBufAllocator allocator = config.getAllocator();
            // 接收数据测handler
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    // 分配内存是自适应的
                    byteBuf = allocHandle.allocate(allocator);
                    // 开始读取数据
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    if (allocHandle.lastBytesRead() <= 0) {// 读取为-1,表示断开连接
                        // nothing was read. release the buffer.
                        byteBuf.release();//释放byteBuf的空间
                        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();
                }
            }
        }
    }

不断跟进源码:进入到了AbstractChannelclose方法

private void close(final ChannelPromise promise, final Throwable cause,
                           final ClosedChannelException closeCause, final boolean notify) {
            if (!promise.setUncancellable()) {
                return;
            }

            if (closeInitiated) {
                if (closeFuture.isDone()) {
                    // Closed already.
                    safeSetSuccess(promise);
                } else if (!(promise instanceof VoidChannelPromise)) { // Only needed if no VoidChannelPromise.
                    // This means close() was called before so we just register a listener and return
                    closeFuture.addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(ChannelFuture future) throws Exception {
                            promise.setSuccess();
                        }
                    });
                }
                return;
            }

            closeInitiated = true;

            final boolean wasActive = isActive();
            final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            // outboundBuffer置空
            this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer.
            Executor closeExecutor = prepareToClose();// 关闭之前将数据发送完成之前的一些操作
            if (closeExecutor != null) {
                closeExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            // Execute the close.
                            doClose0(promise);
                        } finally {
                            // Call invokeLater so closeAndDeregister is executed in the EventLoop again!
                            invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    if (outboundBuffer != null) {
                                        // Fail all the queued messages
                                        outboundBuffer.failFlushed(cause, notify);
                                        outboundBuffer.close(closeCause);
                                    }
                                    fireChannelInactiveAndDeregister(wasActive);
                                }
                            });
                        }
                    }
                });
            } else {
                try {
                    // Close the channel and fail the queued messages in all cases.
                    doClose0(promise);// 执行close操作
                } finally {
                    // 强行关闭outboundBuffer
                    if (outboundBuffer != null) {
                        // Fail all the queued messages.
                        outboundBuffer.failFlushed(cause, notify);
                        outboundBuffer.close(closeCause);
                    }
                }
                if (inFlush0) {
                    invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            fireChannelInactiveAndDeregister(wasActive);
                        }
                    });
                } else {
                    // 将channel不活跃的事件传播出去
                    fireChannelInactiveAndDeregister(wasActive);
                }
            }
        }

1.1 跟进prepareToClose()

跟进进入下面源码,
1.首先判断是否需要阻塞,先将数据发送完成
2.doDeregister()使得所有的selectorKey失效,防止关闭的时候注册事件到selector上
3.返回一个GlobalEventExecutor.INSTANCE线程池执行任务
在这里插入图片描述

1.2 跟进doClose0(promise);

最后调用jdk的close方法关闭channel
在这里插入图片描述
继续跟进源码,jdk的close也就是取消所有selectorKey
在这里插入图片描述

1.3 跟进fireChannelInactiveAndDeregister(wasActive);

1.cancel所有的selectKey
2.pipeline.fireChannelInactive()将close事件传播出去
3.pipeline也取消注册

		private void deregister(final ChannelPromise promise, final boolean fireChannelInactive) {
            if (!promise.setUncancellable()) {
                return;
            }

            if (!registered) {
                safeSetSuccess(promise);
                return;
            }

            // As a user may call deregister() from within any method while doing processing in the ChannelPipeline,
            // we need to ensure we do the actual deregister operation later. This is needed as for example,
            // we may be in the ByteToMessageDecoder.callDecode(...) method and so still try to do processing in
            // the old EventLoop while the user already registered the Channel to a new EventLoop. Without delay,
            // the deregister operation this could lead to have a handler invoked by different EventLoop and so
            // threads.
            //
            // See:
            // https://github.com/netty/netty/issues/4435
            invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        doDeregister();// cancel所有的selectKey
                    } catch (Throwable t) {
                        logger.warn("Unexpected exception occurred while deregistering a channel.", t);
                    } finally {
                        if (fireChannelInactive) {
                            // 最后将这个关闭事件传播出去
                            pipeline.fireChannelInactive();
                        }
                        // Some transports like local and AIO does not allow the deregistration of
                        // an open channel.  Their doDeregister() calls close(). Consequently,
                        // close() calls deregister() again - no need to fire channelUnregistered, so check
                        // if it was registered.
                        if (registered) {
                            registered = false;
                            // pipeline也取消注册
                            pipeline.fireChannelUnregistered();
                        }
                        safeSetSuccess(promise);
                    }
                }
            });
        }

1.3.1 跟进doDeregister()

它本质上就是取消了selector上的所有的selectorKey,但是做出了一些优化
在这里插入图片描述

2.异常关闭的逻辑

进入AbstractNioByteChannel,其中的read方法为例,假设正在读取数据的时候突然抛出异常,会被handleReadException(pipeline, byteBuf, t, close, allocHandle);处理,并关闭channel。

		@Override
        public final void read() {
            final ChannelConfig config = config();
            if (shouldBreakReadReady(config)) {
                clearReadPending();
                return;
            }
            final ChannelPipeline pipeline = pipeline();
            // 内存分配器
            final ByteBufAllocator allocator = config.getAllocator();
            // 接收数据测handler
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    // 分配内存是自适应的
                    byteBuf = allocHandle.allocate(allocator);
                    // 开始读取数据
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    if (allocHandle.lastBytesRead() <= 0) {// 读取为-1,表示断开连接
                        // nothing was read. release the buffer.
                        byteBuf.release();//释放byteBuf的空间
                        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();
                }
            }
        }

handleReadException(pipeline, byteBuf, t, close, allocHandle);会最终交给closeOnRead()方法处理,上面正常时的源码中也调用了该方法,此处不赘述。

private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause, boolean close,
                RecvByteBufAllocator.Handle allocHandle) {
            if (byteBuf != null) {
                if (byteBuf.isReadable()) {
                    readPending = false;
                    pipeline.fireChannelRead(byteBuf);
                } else {
                    byteBuf.release();
                }
            }
            allocHandle.readComplete();
            pipeline.fireChannelReadComplete();
            pipeline.fireExceptionCaught(cause);
            // 如果读写时异常,就交给closeOnRead处理
            if (close || cause instanceof IOException) {
                closeOnRead(pipeline);
            }
        }

总结:
在这里插入图片描述

发布了275 篇原创文章 · 获赞 42 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_35688140/article/details/105539383