鍍金池/ 教程/ Java/ 寫個時間服務(wù)器
Netty 實現(xiàn) WebSocket 聊天功能
總結(jié)
寫個時間客戶端
寫個丟棄服務(wù)器
問題
開始之前
關(guān)閉你的應(yīng)用
開始
用POJO代替ByteBuf
總結(jié)
架構(gòu)總覽
豐富的緩沖實現(xiàn)
解決
寫個應(yīng)答服務(wù)器
I/O API 統(tǒng)一的異步 I/O API
適用快速開發(fā)的高級組件
處理一個基于流的傳輸
Netty 實現(xiàn)聊天功能
基于攔截鏈模式的事件模型
寫個時間服務(wù)器
查看收到的數(shù)據(jù)

寫個時間服務(wù)器

在這個部分被實現(xiàn)的協(xié)議是 TIME 協(xié)議。和之前的例子不同的是在不接受任何請求時他會發(fā)送一個含32位的整數(shù)的消息,并且一旦消息發(fā)送就會立即關(guān)閉連接。在這個例子中,你會學(xué)習(xí)到如何構(gòu)建和發(fā)送一個消息,然后在完成時關(guān)閉連接。

因為我們將會忽略任何接收到的數(shù)據(jù),而只是在連接被創(chuàng)建發(fā)送一個消息,所以這次我們不能使用 channelRead() 方法了,代替他的是,我們需要覆蓋 channelActive() 方法,下面的就是實現(xiàn)的內(nèi)容:

    public class TimeServerHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(final ChannelHandlerContext ctx) { // (1)
            final ByteBuf time = ctx.alloc().buffer(4); // (2)
            time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));

            final ChannelFuture f = ctx.writeAndFlush(time); // (3)
            f.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) {
                    assert f == future;
                    ctx.close();
                }
            }); // (4)
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }

1.channelActive() 方法將會在連接被建立并且準備進行通信時被調(diào)用。因此讓我們在這個方法里完成一個代表當前時間的32位整數(shù)消息的構(gòu)建工作。

2.為了發(fā)送一個新的消息,我們需要分配一個包含這個消息的新的緩沖。因為我們需要寫入一個32位的整數(shù),因此我們需要一個至少有4個字節(jié)的 ByteBuf。通過 ChannelHandlerContext.alloc() 得到一個當前的ByteBufAllocator,然后分配一個新的緩沖。

3.和往常一樣我們需要編寫一個構(gòu)建好的消息。但是等一等,flip 在哪?難道我們使用 NIO 發(fā)送消息時不是調(diào)用 java.nio.ByteBuffer.flip() 嗎?ByteBuf 之所以沒有這個方法因為有兩個指針,一個對應(yīng)讀操作一個對應(yīng)寫操作。當你向 ByteBuf 里寫入數(shù)據(jù)的時候?qū)懼羔樀乃饕蜁黾?,同時讀指針的索引沒有變化。讀指針索引和寫指針索引分別代表了消息的開始和結(jié)束。

比較起來,NIO 緩沖并沒有提供一種簡潔的方式來計算出消息內(nèi)容的開始和結(jié)尾,除非你調(diào)用 flip 方法。當你忘記調(diào)用 flip 方法而引起沒有數(shù)據(jù)或者錯誤數(shù)據(jù)被發(fā)送時,你會陷入困境。這樣的一個錯誤不會發(fā)生在 Netty 上,因為我們對于不同的操作類型有不同的指針。你會發(fā)現(xiàn)這樣的使用方法會讓你過程變得更加的容易,因為你已經(jīng)習(xí)慣一種沒有使用 flip 的方式。

另外一個點需要注意的是 ChannelHandlerContext.write() (和 writeAndFlush() )方法會返回一個 ChannelFuture 對象,一個 ChannelFuture 代表了一個還沒有發(fā)生的 I/O 操作。這意味著任何一個請求操作都不會馬上被執(zhí)行,因為在 Netty 里所有的操作都是異步的。舉個例子下面的代碼中在消息被發(fā)送之前可能會先關(guān)閉連接。


    Channel ch = ...;
    ch.writeAndFlush(message);
    ch.close();

因此你需要在 write() 方法返回的 ChannelFuture 完成后調(diào)用 close() 方法,然后當他的寫操作已經(jīng)完成他會通知他的監(jiān)聽者。請注意,close() 方法也可能不會立馬關(guān)閉,他也會返回一個ChannelFuture。

4.當一個寫請求已經(jīng)完成是如何通知到我們?這個只需要簡單地在返回的 ChannelFuture 上增加一個ChannelFutureListener。這里我們構(gòu)建了一個匿名的 ChannelFutureListener 類用來在操作完成時關(guān)閉 Channel。

或者,你可以使用簡單的預(yù)定義監(jiān)聽器代碼:

    f.addListener(ChannelFutureListener.CLOSE);

為了測試我們的time服務(wù)如我們期望的一樣工作,你可以使用 UNIX 的 rdate 命令

    $ rdate -o <port> -p <host>

Port 是你在main()函數(shù)中指定的端口,host 使用 locahost 就可以了。

上一篇:開始下一篇:總結(jié)