1. 程式人生 > >Netty原始碼分析第3章(客戶端接入流程)---->第3節: NioSocketChannel的建立

Netty原始碼分析第3章(客戶端接入流程)---->第3節: NioSocketChannel的建立

 

Netty原始碼分析第三章: 客戶端接入流程

 

第三節: NioSocketChannel的建立

 

回到上一小結的read()方法:

public void read() {
    //必須是NioEventLoop方法呼叫的, 不能通過外部執行緒呼叫
    assert eventLoop().inEventLoop();
    //服務端channel的config
    final ChannelConfig config = config();
    //服務端channel的pipeline
    final ChannelPipeline pipeline = pipeline();
    
//處理服務端接入的速率 final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle(); //設定配置 allocHandle.reset(config); boolean closed = false; Throwable exception = null; try { try { do { //建立jdk底層的channel //readBuf用於臨時承載讀到連結
int localRead = doReadMessages(readBuf); if (localRead == 0) { break; } if (localRead < 0) { closed = true; break; } //分配器將讀到的連結進行計數 allocHandle.incMessagesRead(localRead);
//連線數是否超過最大值 } while (allocHandle.continueReading()); } catch (Throwable t) { exception = t; } int size = readBuf.size(); //遍歷每一條客戶端連線 for (int i = 0; i < size; i ++) { readPending = false; //傳遞事件, 將建立NioSokectChannel進行傳遞 //最終會呼叫ServerBootstrap的內部類ServerBootstrapAcceptor的channelRead()方法 pipeline.fireChannelRead(readBuf.get(i)); } readBuf.clear(); allocHandle.readComplete(); pipeline.fireChannelReadComplete(); //程式碼省略 } finally { //程式碼省略 } }

我們繼續剖析int localRead = doReadMessages(readBuf)這一部分邏輯

我們首先看readBuf:

private final List<Object> readBuf = new ArrayList<Object>();

這裡只是簡單的定義了一個ArrayList, doReadMessages(readBuf)方法就是將讀到的連結放在這個list中, 因為這裡是NioServerSocketChannel所以這走到了NioServerSocketChannel的doReadMessage()方法

跟到doReadMessage()方法中:

protected int doReadMessages(List<Object> buf) throws Exception {
    //根據當前jdk底層的serverSocketChannel拿到jdk底層channel
    SocketChannel ch = javaChannel().accept();
    try {
        if (ch != null) {
            //封裝成一個NioSokectChannel扔到buf中
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    } catch (Throwable t) {
        //程式碼省略
    }
    return 0;
}

這裡終於走到到了jdk底層相關的內容了

首先根據jdk的ServerSocketChannel拿到jdk的Channel, 熟悉Nio的小夥伴應該不會陌生

封裝成一個NioSokectChannel扔到Readbuf中

這裡的NioSocketChannel是對jdk底層的SocketChannel的包裝, 我們看到其構造方法傳入兩個引數, this代表當前NioServerSocketChannel, ch代表jdk的SocketChannel

我們跟到NioSocketChannel的其造方法中:

public NioSocketChannel(Channel parent, SocketChannel socket) {
    super(parent, socket);
    config = new NioSocketChannelConfig(this, socket.socket());
}

這裡看到呼叫了父類構造方法, 傳入兩個引數, parent代表建立自身channel的, NioServerSocketChannel, socket代表jdk底層的socketChannel

跟到父類構造方法中:

protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) { 
    super(parent, ch, SelectionKey.OP_READ);
}

其中SelectionKey.OP_READ代表其監聽事件是讀事件

繼續跟父類的構造方法:

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    try {
        //設定為非阻塞
        ch.configureBlocking(false);
    } catch (IOException e) {
        //程式碼省略
    }
}

這裡初始化了自身成員變數ch, 就是jdk底層的SocketChannel, 並初始化了自身的監聽事件readInterestOp, 也就是讀事件

ch.configureBlocking(false)這一步熟悉nio的小夥伴也不陌生, 就是將jdk的SocketChannel設定為非阻塞

我們繼續跟到父類構造方法中:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

這裡初始化parent, 也就是建立自身的NioServerSocketChannel, 併為自身建立了唯一id

初始化unsafe, 我們跟到newUnsafe()方法中

由於此方法是NioEventLoop呼叫的, 所以會走到其父類AbstractNioByteChannel的newUnsafe()

跟到newUnsafe()中:

protected AbstractNioUnsafe newUnsafe() {
    return new NioByteUnsafe();
}

這裡建立了NioByteUnsafe物件, 所以NioSocketChannel對應的unsafe是NioByteUnsafe

繼續往下跟, 我們看到其初始化了pipeline, 有關pipline的知識, 我們會在下一章節中講到

回到NioSocketChannel中的構造方法:

public NioSocketChannel(Channel parent, SocketChannel socket) {
    super(parent, socket);
    config = new NioSocketChannelConfig(this, socket.socket());
}

同NioServerSocketChannel一樣, 這裡也初始化了一個Config屬性, 傳入兩個引數, 當前NioSocketChannel自身和jdk的底層SocketChannel的socket物件

我們跟進其構造方法:

private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) {
    super(channel, javaSocket);
}

同樣, 這個類是NioSocketChannel的內部類

繼續跟父類構造方法:

public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
    super(channel);
    if (javaSocket == null) {
        throw new NullPointerException("javaSocket");
    }
    //儲存當前javaSocket
    this.javaSocket = javaSocket;
    //是否禁止Nagle演算法
    if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
        try {
            setTcpNoDelay(true);
        } catch (Exception e) {

        }
    }
}

這裡儲存了SocketChannel的socket物件, 並且預設的情況禁止了Nagle演算法, 有關Nagle, 感興趣的同學可以學習下相關知識

繼續跟到父類構造方法中:

public DefaultChannelConfig(Channel channel) {
    this(channel, new AdaptiveRecvByteBufAllocator());
}

又跟到到了我們熟悉的部分了, 也就是說, 無論NioServerSocketChannel和NioSocketChannel, 最後都會初始化DefaultChannelConfig, 並建立可變ByteBuf分配器, 我們之前小節對此做過詳細剖析這裡不再贅述, 這部分忘記的內容可以閱讀之前小節內容進行回顧

這個分配器什麼時候真正分配位元組緩衝的呢?我們會在之後的章節進行詳細剖析

至此我們剖析完成了NioSocketChannel的初始化過程