Netty 中的读和写

目录

Demo中的读和写

Netty 中的读和写

读操作

写操作


Demo中的读和写

        在简单的手写 Netty demo项目中,Netty 读和写数据都是对 Channel 的读和写,也就是对NioSocketChannel 中的 SelectableChannel 的读和写。底层上也就是 Java NIO 中对SelectableChannel  读写 ByteBuffer。因为一个 NIOEventLoop 持有一个IO多路复用选择器 Selector,一个 Selector 又管理多个Channel, 所以所有的读和写都是发生在 NIOEventLoop 这个工作线程里的。

public class NioSocketChannel extends AbstractNioByteChannel {    
    @Override
    protected SocketChannel javaChannel() {
        return (SocketChannel) super.javaChannel();
    }
    public int doWriteBytes(ByteBuffer buf) throws Exception {
        if (javaChannel().isOpen()&&javaChannel().isConnected()){
            return javaChannel().write(buf);
        } 
        return 0;
    }
    @Override
    protected int doReadBytes(ByteBuffer buf) {
        try {
            return javaChannel().read(buf);
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }
}

Netty 中的读和写

        Netty 对这个读写过程进行了封装,它分别使用 ChannelInboundHandler 和 ChannelOutboundHandler 来封装读写,便于使用的同时更为复杂。ChannelInboundHandler 和 ChannelOutboundHandler 就是一个双向链表,ChannelInboundHandler 对入栈读数据进行处理,ChannelOutboundHandler 对出栈写数据进行处理。ChannelInboundHandler 使用了责任链模式,每个 ChannelInboundHandler 就相当于一个拦截器,对它感兴趣的数据进行处理,比如编解码,解析http,websocket协议等。

读操作

在 NioEventLoop 处理任务时它会通过 Unsafe 内部操作读取数据。在最底层,它会调用

byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());

去读取Channel 传过来的数据,然后通过下面的代码去触发后续 pipeline 数据操作。

pipeline.fireChannelRead(byteBuf); 

protected class NioByteUnsafe extends AbstractNioUnsafe {
    @Override
        public final void read() {
            final ChannelConfig config = config();
            if (shouldBreakReadReady(config)) {
                clearReadPending();
                return;
            }
            final ChannelPipeline pipeline = pipeline();
            final ByteBufAllocator allocator = config.getAllocator();
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    byteBuf = allocHandle.allocate(allocator);

                    # 开始读取数据 doReadBytes(byteBuf)
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        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);
                }
            } 
            ...
}


public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
    # 实际读取数据
    @Override
    protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        allocHandle.attemptedBytesRead(byteBuf.writableBytes());
        return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
    }
}

Netty 中的读和写_第1张图片

写操作

ChannelOutboundHandler 对数据进行写操作,每个ContextHandle 对应一个ChannelOutboundBuffer ,它底层是一个Entry 单向链表,当 Channel 可写的时候,它会进行 flush() 操作,把 ByteBuf 转为 NIO 的 ByteBuff 写到 Channel 里。

public abstract class AbstractNioByteChannel extends AbstractNioChannel {
    @Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        int writeSpinCount = config().getWriteSpinCount();
        do {
            Object msg = in.current();
            if (msg == null) {
                // Wrote all messages.
                clearOpWrite();
                // Directly return here so incompleteWrite(...) is not called.
                return;
            }
            # 开始写
            writeSpinCount -= doWriteInternal(in, msg);
        } while (writeSpinCount > 0);

        incompleteWrite(writeSpinCount < 0);
    }
private int doWriteInternal(ChannelOutboundBuffer in, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {
            ByteBuf buf = (ByteBuf) msg;
            if (!buf.isReadable()) {
                in.remove();
                return 0;
            }
            # 开始写
            final int localFlushedAmount = doWriteBytes(buf);
            if (localFlushedAmount > 0) {
                in.progress(localFlushedAmount);
                if (!buf.isReadable()) {
                    in.remove();
                }
                return 1;
            }
        }  
        ...
        return WRITE_STATUS_SNDBUF_FULL;
    }
}
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
     @Override
    # 实际写 ByteBuf 
    protected int doWriteBytes(ByteBuf buf) throws Exception {
        final int expectedWrittenBytes = buf.readableBytes();
        return buf.readBytes(javaChannel(), expectedWrittenBytes);
    }
    # 实际写 ChannelOutboundBuffer 
    @Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        SocketChannel ch = javaChannel();
        int writeSpinCount = config().getWriteSpinCount();
        do {
            if (in.isEmpty()) {
                // All written so clear OP_WRITE
                clearOpWrite();
                // Directly return here so incompleteWrite(...) is not called.
                return;
            }
 
            int maxBytesPerGatheringWrite = ((NioSocketChannelConfig) config).getMaxBytesPerGatheringWrite();
            ByteBuffer[] nioBuffers = in.nioBuffers(1024, maxBytesPerGatheringWrite);
            int nioBufferCnt = in.nioBufferCount(); 
            switch (nioBufferCnt) {
                case 0:
                    // We have something else beside ByteBuffers to write so fallback to normal writes.
                    writeSpinCount -= doWrite0(in);
                    break;
                case 1: { 
                    ByteBuffer buffer = nioBuffers[0];
                    int attemptedBytes = buffer.remaining();
                    
                    #实际写 ChannelOutboundBuffer 
                    final int localWrittenBytes = ch.write(buffer);
                    if (localWrittenBytes <= 0) {
                        incompleteWrite(true);
                        return;
                    }
                    adjustMaxBytesPerGatheringWrite(attemptedBytes, localWrittenBytes, maxBytesPerGatheringWrite);
                    in.removeBytes(localWrittenBytes);
                    --writeSpinCount;
                    break;
                }
                default: { 
                    long attemptedBytes = in.nioBufferSize();
                    final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
                    if (localWrittenBytes <= 0) {
                        incompleteWrite(true);
                        return;
                    } 
                    adjustMaxBytesPerGatheringWrite((int) attemptedBytes, (int) localWrittenBytes, maxBytesPerGatheringWrite);
                    in.removeBytes(localWrittenBytes);
                    --writeSpinCount;
                    break;
                }
            }
        } while (writeSpinCount > 0); 
        incompleteWrite(writeSpinCount < 0);
    }
}

 写 ChannelOutboundBuffer 数据时调用

ch.write(buffer);

写 ByteBuf 数据时调用

buf.readBytes(javaChannel(), expectedWrittenBytes)


你可能感兴趣的:(java,nio)