Channel

功能说明

io.netty.channel.Channel 是 Netty 的网络操作抽象类,聚合了一组功能,包括但不限于网络读写、客户端发起连接、主动关闭连接,同时也包含了 Netty 框架相关的一些功能,包括获取 Channel 的 EventLoop,获取缓冲区分配器 ByteBufAllocator 和 pipeline 等。

为了 Netty 不使用 NIO 的原生 Channel,而是要另起炉灶呢?主要原因如下:

  1. JDK 的 SocketChannel 和 ServerSocketChannel 没有统一的 Channel 接口供业务开发者使用。对用户而言,没有统一的操作视图,使用起来不方便
  2. JDK 的 SocketChannel 和 ServerSocketChannel 是 SPI 类接口,通过继承来扩展很不方便,不如开发一个新的。
  3. Netty 的 Channel 需要能跟 Netty 架构融合在一起。
  4. 自定义 Channel 功能实现会更灵活。

基于以上原因,Netty 重新设计了 Channel,其主要设计理念如下:

  • 在 Channel 接口层,采用 Facade 模式统一封装,将网络 I/O 操作、网络 I/O 相关联的其他操作封装起来,统一对外提供。
  • Channel 接口定义尽量大而全,为 SocketChannel 和 ServerSocketChannel 提供统一的视图,由不同子类实现不同的功能,公共功能在抽象父类中实现,最大程度上实现功能和接口的重用。
  • 具体实现采用聚合而非包含的方式,Channel 负责统一分配和调度,更加灵活

Netty 的 Channel 都有哪些功能呢?

  1. 常见的网络 IO 操作:读、写、绑定端口、连接、关闭连接等。
  2. 获取 EventLoop。
  3. 获取 parent Channel,对于服务端 SocketChannel 来说,parent 就是创建它的 ServerSocketChannel。
  4. 唯一标志 id。
  5. 元数据 metadata,获取 TCP 参数配置等。

常用接口

  • eventLoop(),Channel需要注册到EventLoop的多路复用器上,用于处理IO事件,通过eventLoop方法可以获取到Channel注册的EventLoop。EventLoop本质上就是处理网络读写事件的Reactor线程。在Netty中,它不仅仅用来处理网络事件,也可以用来执行定时任务和用户自定义NioTask等任务。
  • metadata(),熟悉TCP协议的同学可能知道,当创建Socket的时候需要指定TCP参数,例如接收和发送的TCP缓冲区大小,TCP的超时时间,是否重用地址等等。在Netty中,每个Channel对应一个物理连接,每个连接都有自己的TCP参数配置。所以,Channel会聚合一个ChannelMetadata用来对TCP参数提供元数据描述信息,通过metadata方法就可以获取当前Channel的TCP参数配置。
  • parent(),对于服务端Channel而言,它的父Channel为空,对于客户端Channel,它的父Channel就是创建它的ServerSocketChannel。

用户获取Channel标识的id,它返回ChannelId对象,ChannelId是Channel的唯一标识,它的可能生成策略如下:

  • 机器的MAC地址(EUI-48或者EUI-64)等可以代表全局唯一的信息。
  • 当前进程的ID。
  • 当前系统时间的毫秒——System.currentTimeMillis
  • 当前系统时间的纳秒——System.nanoTime
  • 32位的随机整型数
  • 32位自增的序列数

Channel 源码分析

继承关系类图

android netty ChannelId 怎么获取端口号_大数据

 NioServerSocketChannel、NioSocketChannel 两者都继承了 Channel、AbstractChannel、AbstractNioChannel。

AbstractChannel

主要成员变量如下所示:

// 父 Channel
private final Channel parent;
// 全局唯一 id。
private final ChannelId id;
// Unsafe 实例
private final Unsafe unsafe;
// 当前 Channel 对应的 DefaultChannelPipeline。
private final DefaultChannelPipeline pipeline;
private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
private final CloseFuture closeFuture = new CloseFuture(this);

private volatile SocketAddress localAddress;
private volatile SocketAddress remoteAddress;
// EventLoop
private volatile EventLoop eventLoop;
private volatile boolean registered;
private boolean closeInitiated;
private Throwable initialCloseCause;

AbstractChannel 中的网络 I/O 操作都是调用 pipeline 中的对应方法,继而由 pipeline 调用 ChannelHandler 进行处理。

@Override
    public ChannelFuture bind(SocketAddress localAddress) {
        return pipeline.bind(localAddress);
    }

    @Override
    public ChannelFuture connect(SocketAddress remoteAddress) {
        return pipeline.connect(remoteAddress);
    }

    @Override
    public ChannelFuture write(Object msg) {
        return pipeline.write(msg);
    }

AbstractNioChannel

  • SelectableChannel:这是一个 Java NIO SocketChannel 和 ServerSocketChannel 的公共父类,放在这里是因为 AbstractNioChannel 也是 NioSocketChannel 和 NioServerSocketChannel 的公共父类。
  • readInterestOp:代表 JDK SelectionKey 的 OP_READ。
  • SelectionKey:Channel 注册到 EventLoop(Selector)时返回的 key,修改它可以改变感兴趣的事件。
  • connectPromise:代表连接操作结果。
  • connectTimeoutFuture:连接超时定时器。
  • requestedRemoteAddress:connect 时的远程地址。

AbstractNioChannel 类里比较重要的方法是 doRegister,该方法负责将 Channel 注册到多路复用器 Selector。

@Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }

在 doRegister 方法中,对 ops 字段设置为 0,也就是对任何事件都不感兴趣。真正的设置读操作位是在 doBeginRead 方法中,那么写操作位在何时设置呢?当然是有数据要写,而缓冲区满(或其他不能立即写)的情况。

@Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;

        final int interestOps = selectionKey.interestOps();
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

AbstractNioByteChannel

AbstractNioByteChannel 是 NioSocketChannel 的父类,只有一个成员变量 flushTask,负责写半包消息。

private Runnable flushTask;

最主要的方法是 doWrite:

@Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        int writeSpinCount = -1;

        boolean setOpWrite = false;
        for (;;) {
            Object msg = in.current();
            // 如果没有要写的数据,就清除写标志位,并返回
            if (msg == null) {
                // Wrote all messages.
                clearOpWrite();
                // Directly return here so incompleteWrite(...) is not called.
                return;
            }

            // 对于 ByteBuf 类型、FileRegion 类型分开处理,其他未知类型抛异常
            if (msg instanceof ByteBuf) {
                ByteBuf buf = (ByteBuf) msg;
                int readableBytes = buf.readableBytes();
                if (readableBytes == 0) {
                    in.remove();
                    continue;
                }

                boolean done = false;
                long flushedAmount = 0;
                if (writeSpinCount == -1) {
                    writeSpinCount = config().getWriteSpinCount();
                }
                // 只循环写 writeSpinCount 次,为了避免写大块儿数据时,阻塞其他线程过长时间
                for (int i = writeSpinCount - 1; i >= 0; i --) {
                    int localFlushedAmount = doWriteBytes(buf);
                    // 返回 0 表示写缓冲区满,setOpWrite 为 true 会设置 SelectionKey 的写标志位,在可写时会得到通知。
                    if (localFlushedAmount == 0) {
                        setOpWrite = true;
                        break;
                    }

                    flushedAmount += localFlushedAmount;
                    if (!buf.isReadable()) {
                        done = true;
                        break;
                    }
                }

                in.progress(flushedAmount);

                if (done) {
                    in.remove();
                } else {
                    // Break the loop and so incompleteWrite(...) is called.
                    break;
                }
            } else if (msg instanceof FileRegion) {
                FileRegion region = (FileRegion) msg;
                boolean done = region.transferred() >= region.count();

                if (!done) {
                    long flushedAmount = 0;
                    if (writeSpinCount == -1) {
                        writeSpinCount = config().getWriteSpinCount();
                    }

                    for (int i = writeSpinCount - 1; i >= 0; i--) {
                        long localFlushedAmount = doWriteFileRegion(region);
                        if (localFlushedAmount == 0) {
                            setOpWrite = true;
                            break;
                        }

                        flushedAmount += localFlushedAmount;
                        if (region.transferred() >= region.count()) {
                            done = true;
                            break;
                        }
                    }

                    in.progress(flushedAmount);
                }

                if (done) {
                    in.remove();
                } else {
                    // Break the loop and so incompleteWrite(...) is called.
                    break;
                }
            } else {
                // Should not reach here.
                throw new Error();
            }
        }
        incompleteWrite(setOpWrite);
    }
    // 走到这里,说明还有数据没有发送完毕,需要进一步处理
    protected final void incompleteWrite(boolean setOpWrite) {
        // setOpWrite 为 true,设置 SelectionKey 写标志位
        if (setOpWrite) {
            setOpWrite();
        } else {
            // 否则,启动 flushTask 继续写半包消息
            Runnable flushTask = this.flushTask;
            if (flushTask == null) {
                flushTask = this.flushTask = new Runnable() {
                    @Override
                    public void run() {
                        flush();
                    }
                };
            }
            eventLoop().execute(flushTask);
        }
    }

AbstractNioMessageChannel

AbstractNioMessageChannel 是 NioServerSocketChannel、NioDatagramChannel 的父类。其主要方法也是 doWrite,功能和 AbstractNioByteChannel 的 doWrite 也类似,区别只是后者只处理 ByteBuf 和 FileRegion,前者无此限制,处理所有 Object。

protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        final SelectionKey key = selectionKey();
        final int interestOps = key.interestOps();

        for (;;) {
            Object msg = in.current();
            if (msg == null) {
                // Wrote all messages.
                if ((interestOps & SelectionKey.OP_WRITE) != 0) {
                    key.interestOps(interestOps & ~SelectionKey.OP_WRITE);
                }
                break;
            }
            try {
                boolean done = false;
                for (int i = config().getWriteSpinCount() - 1; i >= 0; i--) {
                    if (doWriteMessage(msg, in)) {
                        done = true;
                        break;
                    }
                }

                if (done) {
                    in.remove();
                } else {
                    // Did not write all messages.
                    if ((interestOps & SelectionKey.OP_WRITE) == 0) {
                        key.interestOps(interestOps | SelectionKey.OP_WRITE);
                    }
                    break;
                }
            } catch (Exception e) {
                if (continueOnWriteError()) {
                    in.remove(e);
                } else {
                    throw e;
                }
            }
        }
    }
    // 处理 msg,由子类实现
    protected abstract boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception;

doWriteMessage 方法在 NioServerSocketChannel 中实现如下所示,是因为 NioServerSocketChannel 只是用来监听端口,接收客户端请求,不负责传输实际数据。

protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
        throw new UnsupportedOperationException();
    }

doWriteMessage 方法在 NioSctpChannel 中是由具体实现的,从代码中可以看出来,它处理的只是 SctpMessage 类型的数据。

protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
        SctpMessage packet = (SctpMessage) msg;
        ByteBuf data = packet.content();
        int dataLen = data.readableBytes();
        if (dataLen == 0) {
            return true;
        }

        ByteBufAllocator alloc = alloc();
        boolean needsCopy = data.nioBufferCount() != 1;
        if (!needsCopy) {
            if (!data.isDirect() && alloc.isDirectBufferPooled()) {
                needsCopy = true;
            }
        }
        ByteBuffer nioData;
        if (!needsCopy) {
            nioData = data.nioBuffer();
        } else {
            data = alloc.directBuffer(dataLen).writeBytes(data);
            nioData = data.nioBuffer();
        }
        final MessageInfo mi = MessageInfo.createOutgoing(association(), null, packet.streamIdentifier());
        mi.payloadProtocolID(packet.protocolIdentifier());
        mi.streamNumber(packet.streamIdentifier());
        mi.unordered(packet.isUnordered());

        // 写数据
        final int writtenBytes = javaChannel().send(nioData, mi);
        return writtenBytes > 0;
    }

NioServerSocketChannel

NioServerSocketChannel 是服务端 Channel 的实现类,有一个用于配置 TCP 参数的 ServerSocketChannelConfig。

private final ServerSocketChannelConfig config;

作为服务端 Channel,其核心方法是端口绑定 doBind 方法、创建 SocketChannel 的 doReadMessages 方法。

protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

    protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = SocketUtils.accept(javaChannel());

        try {
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            logger.warn("Failed to create a new channel from an accepted socket.", t);

            try {
                ch.close();
            } catch (Throwable t2) {
                logger.warn("Failed to close a socket.", t2);
            }
        }

        return 0;
    }

对于和服务端 Channel 无关的方法,要果断抛出 UnsupportedOperationException 异常。

@Override
    protected void doDisconnect() throws Exception {
        throw new UnsupportedOperationException();
    }

    @Override
    protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
        throw new UnsupportedOperationException();
    }

    @Override
    protected final Object filterOutboundMessage(Object msg) throws Exception {
        throw new UnsupportedOperationException();
    }

NioSocketChannel

NioSocketChannel 是客户端 Channel 的实现类,也是只有一个用于配置参数的变量 SocketChannelConfig。

private final SocketChannelConfig config;

客户端 Channel 的核心方法有连接 doConnect、写半包 doWrite、读操作 doReadBytes,下面我们挨个分析。

连接操作 doConnect 具体实现如下:

  1. 如果 localAddress 为空,则进行绑定操作。
  2. 调用 socketChannel.connect 进行连接。
  3. 如果连接尚未完成,则注册 OP_CONNECT 事件。
  4. 如果连接失败抛出异常,也要调用 doClose 关闭连接。
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
        if (localAddress != null) {
            doBind0(localAddress);
        }

        boolean success = false;
        try {
            // 实际上是调用了 socketChannel.connect 方法。
            boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
            if (!connected) {
                selectionKey().interestOps(SelectionKey.OP_CONNECT);
            }
            success = true;
            return connected;
        } finally {
            if (!success) {
                doClose();
            }
        }
    }

写操作 doWrite 具体实现如下:

  1. 判断待写数据大小,若为 0 则清除写标志位,并返回。
  2. 从 ChannelOutboundBuffer 里获取待写 ByteBuffer 数组,和待写 ByteBuffer 数量 nioBufferCnt。
  3. 针对 nioBufferCnt 的不同大小进行了区别处理。
  4. 如果 nioBufferCnt 为 0,则调用父类的方法处理,以防有除了 ByteBuffer 之外的数据需要写。
  5. nioBufferCnt 为 1 和大于 1 的处理类似,都是循环写 getWriteSpinCount 次,若写完则结束,未写完则设置后续写半包的方式。这一点和父类 AbstractNioByteChannel 中的处理方法类似。
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        for (;;) {
            int size = in.size();
            if (size == 0) {
                // All written so clear OP_WRITE
                clearOpWrite();
                break;
            }
            long writtenBytes = 0;
            boolean done = false;
            boolean setOpWrite = false;

            // Ensure the pending writes are made of ByteBufs only.
            ByteBuffer[] nioBuffers = in.nioBuffers();
            int nioBufferCnt = in.nioBufferCount();
            long expectedWrittenBytes = in.nioBufferSize();
            SocketChannel ch = javaChannel();

            // Always us nioBuffers() to workaround data-corruption.
            switch (nioBufferCnt) {
                case 0:
                    // We have something else beside ByteBuffers to write so fallback to normal writes.
                    super.doWrite(in);
                    return;
                case 1:
                    // 和 default 的区别只是传给 ch.write 的是数组还是单个 ByteBuffer
                    ByteBuffer nioBuffer = nioBuffers[0];
                    for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
                        final int localWrittenBytes = ch.write(nioBuffer);
                        if (localWrittenBytes == 0) {
                            setOpWrite = true;
                            break;
                        }
                        expectedWrittenBytes -= localWrittenBytes;
                        writtenBytes += localWrittenBytes;
                        if (expectedWrittenBytes == 0) {
                            done = true;
                            break;
                        }
                    }
                    break;
                default:
                    for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
                        final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
                        if (localWrittenBytes == 0) {
                            setOpWrite = true;
                            break;
                        }
                        expectedWrittenBytes -= localWrittenBytes;
                        writtenBytes += localWrittenBytes;
                        // expectedWrittenBytes 为 0 表示数据发送完毕
                        if (expectedWrittenBytes == 0) {
                            done = true;
                            break;
                        }
                    }
                    break;
            }

            // Release the fully written buffers, and update the indexes of the partially written buffer.
            in.removeBytes(writtenBytes);

            if (!done) {
                // 设置后续写半包方式
                incompleteWrite(setOpWrite);
                break;
            }
        }
    }

读操作比较简单,主要是通过 ByteBuf 来从 Channel 中读取数据。

protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        allocHandle.attemptedBytesRead(byteBuf.writableBytes());
        return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
    }

总结

Channel 类体系的设计与其实现功能密不可分,父类中实现的是子类共同的功能。在多层次的抽象类中,每一个层次的抽象类负责实现一种功能。

当父类提供大而全的接口时,父类可以根据需要去实现,不需要的可以抛出 UnsupportedOperationException 异常。

Unsafe

功能说明

Unsafe接口实际上是Channel 接口的辅助接口,它不应被用户代码 直接调用,实际的I/O 读写操作者是由Unsafe接口负责完成的。

在netty中一个很核心的组件,封装了java底层的socket操作,作为连接netty和java 底层nio的重要桥梁。

方法名

返回值

功能说明

invoker()

ChannelHandlerInvoker

返回默认使用的ChannelHandlerlnvoker

localAddress()

SocketAddress

返回本地绑定的Socket地址

remoteAddress()

SocketAddress

返回通信对端Socket地址

register(ChannelPromise promise)

void

注册Channel 到多路复用器上,一旦完成,通知channelFuture

bind(SocketAddress localAddress,ChannelPromise promise)

void

绑定指定的本地地址 localAddress到当前的Channel 上,一旦完成,通知channelFuture

connect(SocketAddress remoteAddress,SocketAddress localAddress,ChannelPromise promise)

void

绑定本地的 localAddress之后,连接服务端,一旦完成,通知channelFuture

disconnect(ChannelPromise promise)

void

断天Channel的连接。一旦完成,通知channelFuture

close(ChannelPromise promise)

void

关闭Channel的连接,一旦完成,通知channelFuture

closcForcibly()

void

强制立即关闭连接

beginRead()

void

设置网络操作位为读用于读取信息

write(Object msg,ChannelPromise promise)

void

发送消息,一但完成,通知ChannelFuture

flush()

void

将发送缓冲数组中的消息写入到Channel中

voidPromise()

ChannelPromise

返回一个特殊的可重用和传递的ChannelPromise,它不用于操作成功或者失败的通知器,仅仅作为一容器被使用

outboundBuffer()

ChannelOutboundBuffer

返回消息发送缓冲区

Unsafe继承关系类图:

android netty ChannelId 怎么获取端口号_大数据_02

AbstractUnsafe 源码分析 

  • register方法:主要用于将当前Unsafe对应的Channel注册到EventLoop的多路复用器上,然后调用DefaultChannelPipeline的fireChannelRegisted方法,如果Channel被激活,则调用fireChannelActive方法。
  • bind方法:主要用于绑定指定端口。对于服务端,用于绑定监听端口,并设置backlog参数;对于客户端,用于指定客户端Channel的本地绑定Socket地址
  • disconnect方法: 该方法用于客户端或服务端主动关闭连接
  • close方法:确保在多线程环境下,多次调用close和一次调用的影响一致,并且可以通过promis得到同样的结果。
  • 保证在执行close的过程中,不能向channel写数据。
  • 调用doClose0执行执真正的close操作。
  • 调用deregister对channel做最后的清理工作,并触发channelInactive, channelUnregistered事件。
  • write方法:实际上是将消息添加到环形发送数组上,并不真正的写Channel(真正的写Channel是flush方法)。如果Channel 没有处于激活状态,说明TCP链路还有真正建立成功,当前Channel存在以下两种状态
  • Channel 打开,但是TCP链路尚未建立成功
  • Channel 已经关闭
  • flush方法:前面提到,write方法负责将消息放进发送缓冲区,并没有真正的发送,而flush方法就负责将发送缓冲区中待发送的消息全部写进Channel中并发送。

部分源码如下:

protected abstract class AbstractUnsafe implements Unsafe {

//下面是绑定方法的逻辑 传入的是SocketAddress  
        @Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
// 确认当前channel已经注册
            assertEventLoop();

            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }
 //验证传入的参数
            // See: https://github.com/netty/netty/issues/576
            if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
                localAddress instanceof InetSocketAddress &&
                !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
                !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                // Warn a user about the fact that a non-root user can't receive a
                // broadcast packet on *nix if the socket is bound on non-wildcard address.
                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.");
            }

            boolean wasActive = isActive();
            try {
//具体的绑定操作在doBind方法里执行 这个方法是channel的方法,也就是我们在上面NioServerSocketChannel 里分析的逻辑
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }
//触发 active事件,在pipline链里传播
            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

//往外写数据的操作(这里只往缓存里写数据)
        @Override
        public final void write(Object msg, ChannelPromise promise) {
//验证是否已经注册并且react线程是否已经准备好
            assertEventLoop();
//ChannelOutboundBuffer  表示要往外写数据的缓存
            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null) {
                // If the outboundBuffer is null we know the channel was closed and so
                // need to fail the future right away. If it is not null the handling of the rest
                // will be done in flush0()
                // See https://github.com/netty/netty/issues/2362
                safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
                // release message now to prevent resource-leak
                ReferenceCountUtil.release(msg);
                return;
            }

            int size;
            try {
//对需要写的数据进行过滤
                msg = filterOutboundMessage(msg);
//对需要写的数据进行大小的预估
                size = pipeline.estimatorHandle().size(msg);
                if (size < 0) {
                    size = 0;
                }
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                ReferenceCountUtil.release(msg);
                return;
            }
//将数据增加到缓存中
            outboundBuffer.addMessage(msg, size, promise);
        }

// flush方法用于将数据写入到网络中
        @Override
        public final void flush() {
            assertEventLoop();
//往外发送的缓存对象不能为空
            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null) {
                return;
            }

            outboundBuffer.addFlush();
            flush0();
        }

//最终会调用到这个方法
        @SuppressWarnings("deprecation")
        protected void flush0() {
            if (inFlush0) {
                // Avoid re-entrance
                return;
            }

            final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null || outboundBuffer.isEmpty()) {
                return;
            }

            inFlush0 = true;
//对channel的状态进行验证
            // Mark all pending write requests as failure if the channel is inactive.
            if (!isActive()) {
                try {
                    if (isOpen()) {
                        outboundBuffer.failFlushed(FLUSH0_NOT_YET_CONNECTED_EXCEPTION, true);
                    } else {
                        // Do not trigger channelWritabilityChanged because the channel is closed already.
                        outboundBuffer.failFlushed(FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                    }
                } finally {
                    inFlush0 = false;
                }
                return;
            }

            try {
//会调用到Channel的doWrite方法,具体实现的源码可以看NioSocketChannel
                doWrite(outboundBuffer);
            } catch (Throwable t) {
                if (t instanceof IOException && config().isAutoClose()) {
                    /**
                     * Just call {@link #close(ChannelPromise, Throwable, boolean)} here which will take care of
                     * failing all flushed messages and also ensure the actual close of the underlying transport
                     * will happen before the promises are notified.
                     *
                     * This is needed as otherwise {@link #isActive()} , {@link #isOpen()} and {@link #isWritable()}
                     * may still return {@code true} even if the channel should be closed as result of the exception.
                     */
                    close(voidPromise(), t, FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                } else {
                    try {
                        shutdownOutput(voidPromise(), t);
                    } catch (Throwable t2) {
                        close(voidPromise(), t2, FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                    }
                }
            } finally {
                inFlush0 = false;
            }
        }

//开始读方法的逻辑
        @Override
        public final void beginRead() {
            assertEventLoop();

            if (!isActive()) {
                return;
            }

            try {
//会调用channel的doBeginRead方法 可以看上面AbstractNioChannel方法里的注释 
                doBeginRead();
            } catch (final Exception e) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireExceptionCaught(e);
                    }
                });
                close(voidPromise());
            }
        }

//注册方法,channel会往selector里注册关注的事件
        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
//验证数据
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }

            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) {
//调用下面的方法
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }

        private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = neverRegistered;
//会调用到channel里的方法 可以参考上面的AbstractNioChannel类
                doRegister();
                neverRegistered = false;
                registered = true;

                // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
                // user may already fire events through the pipeline in the ChannelFutureListener.
                pipeline.invokeHandlerAddedIfNeeded();

                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        //
                        // See https://github.com/netty/netty/issues/4805
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

}

Netty服务端接收客户端数据的调用流程

读数据的调用流程

  NioEventLoop的run方法会监控SelectionKey对象当有读事件时,会调用unsafe对象的read()方法,在read方法的逻辑里会触发pipeline对象链的调用,最终调用到设置的各种ChannelHandler

写数据的调用流程

      通过Channel的writeAndFlush会调用到pipeline的writeAndFlush方法里,在pipeline的调用链里会调用链中的各种ChannelHandler(各以对需要写入的数据进行格式转换)最终通过HeadContext的write方法调用到unsafe里的write逻辑。这里只是把数据写入到ByteBuffer里。通过调用unsafe的flash方法才能最终将数据写入到网络中,也就是上面的分析过程。