6. Netty解析:服務(wù)端如何接受連接并后續(xù)處理讀寫(xiě)事件

前言

??之前的文章分析到了服務(wù)端NioServerSocketChannel的創(chuàng)建注冊(cè)及注冊(cè)accept事件奈懒。到現(xiàn)在為止宪巨,關(guān)于服務(wù)端,我們還有多個(gè)疑問(wèn)未解開(kāi):例如當(dāng)有客戶端連接過(guò)來(lái)時(shí)捏卓,服務(wù)端要怎么處理,以及后續(xù)的讀寫(xiě)如何進(jìn)行的怠晴?之前的分析都是在服務(wù)端的父事件循環(huán)組中,那么子事件循環(huán)組又是怎么起作用的稿械?之前提到了子處理器childHandler被封裝在了ServerBootStrapAcceptor中添加到了流水線冲粤,那么子處理器又是怎么工作的,有沒(méi)有被添加到pipeline中梯捕?本文將對(duì)這些問(wèn)題進(jìn)行解答。

服務(wù)端接收到連接后做了什么

??我們不得不再次回到下面這個(gè)方法傀顾,當(dāng)有read或者accept事件到來(lái)時(shí),執(zhí)行unsaf.read()(對(duì)于連接事件和read事件寒砖,因?yàn)槠渌鶎?duì)應(yīng)的通道一個(gè)是NioServerSocketChannel一個(gè)是NioSocketChannel,兩者的unsafe是不同的入撒,對(duì)于連接事件,unsafe.read位于AbstractNioUnsafe中)茅逮。

    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            final EventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable ignored) {
             
                return;
            }
            if (eventLoop != this || eventLoop == null) {
                return;
            }
            unsafe.close(unsafe.voidPromise());
            return;
        }

        try {
            int readyOps = k.readyOps();
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }

            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }
           if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }
    @Override
    public void read() {
        /*省略代碼*/
        try {
            try {
                for (;;) {
                    int localRead = doReadMessages(readBuf);
                    if (localRead == 0) {
                        break;
                    }
                    if (localRead < 0) {
                        closed = true;
                        break;
                    }

                    // stop reading and remove op
                    if (!config.isAutoRead()) {
                        break;
                    }

                    if (readBuf.size() >= maxMessagesPerRead) {
                        break;
                    }
                }
            } catch (Throwable t) {
                exception = t;
            }
            setReadPending(false);
            int size = readBuf.size();
            for (int i = 0; i < size; i ++) {
                pipeline.fireChannelRead(readBuf.get(i));
            }
            readBuf.clear();
            pipeline.fireChannelReadComplete();
            /*省略代碼*/
        } finally {
            /*省略代碼*/
        }
    }


    /*進(jìn)行連接的接收*/
    @Override
    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;
    }

??在doReadMessages方法中碉考,會(huì)通過(guò)NioServerSocketChannel內(nèi)部的ServerSocketChannel來(lái)完成連接的接受挺身,如果此時(shí)有連接進(jìn)來(lái)侯谁,那么會(huì)生成SocketChannel實(shí)例章钾,netty將其封裝為自身的NioSocketChannel實(shí)例加入到緩沖buf中。隨后會(huì)將緩沖區(qū)中內(nèi)容(新接收的連接)通過(guò)fireChannelRead進(jìn)行流水線傳輸惨撇。可以發(fā)現(xiàn)魁衙,NioServerSocketChannel中的ServerSocketChannel在接收到連接后會(huì)通過(guò)NioServerSocketChannel中的流水線的channelRead方法進(jìn)行傳輸株搔。
??隨后流水線channelRead執(zhí)行到ServerBootstrapAcceptor。

    @Override
    @SuppressWarnings("unchecked")
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        final Channel child = (Channel) msg;
        child.pipeline().addLast(childHandler);
        setChannelOptions(child, childOptions, logger);
        for (Entry<AttributeKey<?>, Object> e: childAttrs) {
            child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
        }
        try {
            childGroup.register(child).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                        forceClose(child, future.cause());
                    }
                }
            });
        } catch (Throwable t) {
            forceClose(child, t);
        }
    }

??由于我們已經(jīng)知道傳入的msg是一個(gè)netty已接收連接的通道(實(shí)際封裝了SocketChannel的NioSocketChannel)纵隔,獲取到它的pipeline,并將childHandler添加到它的流水線中炮姨。如果我們給其配置的childHandler是ChannelInitializer的話捌刮,那么隨后還會(huì)將其他的處理器添加到流水線中。同樣的剑令,新接收的連接還沒(méi)有注冊(cè)糊啡,所以也需要將其注冊(cè)拄查,只不過(guò)在server端吁津,新接受的連接注冊(cè)到childGroup,也就是子事件循環(huán)組中,最終它會(huì)被注冊(cè)到子事件循環(huán)組中的某個(gè)NioEventLoop上碍脏,它之后的讀寫(xiě)的事件操作就與之前分析客戶端時(shí)相似梭依。
?? 在netty中NioServerSocketChannel負(fù)責(zé)接收連接,它注冊(cè)在父NioEventLoopGroup中役拴,而且他的通道的handler最后是一個(gè)ServerBootStrapAcceptor處理器。在接收到新連接時(shí)钾埂,出發(fā)fireChannelRead回調(diào)方法的執(zhí)行河闰,當(dāng)執(zhí)行到ServerBootStrapAcceptor時(shí),ServerBootStrapAcceptor將子處理器加入到新連接的通道的流水線中褥紫。那么以Echo Server這個(gè)例子來(lái)看姜性,兩種通道的流水線分別是:

1 NioServerSocketChannel的流水線(在前文已經(jīng)見(jiàn)到過(guò))


2 新連接到來(lái)后NioSocketChannel的流水線


??

*鏈接

1. Netty解析:第一個(gè)demo——Echo Server
2. Netty解析:NioEventLoopGroup事件循環(huán)組
3. Netty解析:NioSocketChannel、NioServerSocketChannel的創(chuàng)建及注冊(cè)
4. Netty解析:Handler髓考、Pipeline大動(dòng)脈及其在注冊(cè)過(guò)程中體現(xiàn)
5. Netty解析:connect/bind方法背后
6. Netty解析:服務(wù)端如何接受連接并后續(xù)處理讀寫(xiě)事件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末部念,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子氨菇,更是在濱河造成了極大的恐慌儡炼,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件查蓉,死亡現(xiàn)場(chǎng)離奇詭異乌询,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)奶是,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)楣责,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人聂沙,你說(shuō)我怎么就攤上這事秆麸。” “怎么了及汉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵沮趣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我坷随,道長(zhǎng)房铭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任温眉,我火速辦了婚禮缸匪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘类溢。我一直安慰自己凌蔬,他們只是感情好露懒,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著砂心,像睡著了一般懈词。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辩诞,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天坎弯,我揣著相機(jī)與錄音,去河邊找鬼译暂。 笑死抠忘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的外永。 我是一名探鬼主播褐桌,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼象迎!你這毒婦竟也來(lái)了荧嵌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤砾淌,失蹤者是張志新(化名)和其女友劉穎啦撮,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體汪厨,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赃春,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了劫乱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片织中。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖衷戈,靈堂內(nèi)的尸體忽然破棺而出狭吼,到底是詐尸還是另有隱情,我是刑警寧澤殖妇,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布刁笙,位于F島的核電站,受9級(jí)特大地震影響谦趣,放射性物質(zhì)發(fā)生泄漏疲吸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一前鹅、第九天 我趴在偏房一處隱蔽的房頂上張望摘悴。 院中可真熱鬧,春花似錦舰绘、人聲如沸蹂喻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叉橱。三九已至,卻和暖如春者蠕,著一層夾襖步出監(jiān)牢的瞬間窃祝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工踱侣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粪小,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓抡句,卻偏偏與公主長(zhǎng)得像探膊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子待榔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容