Netty學(xué)習(xí)筆記二-Netty入門開發(fā)

上一節(jié)中介紹的java NIO的開發(fā)责掏,回顧下NIO開發(fā)的步驟:
1、創(chuàng)建ServerSocketChannel并設(shè)置為非阻塞模式
2小槐、綁定監(jiān)聽端口
3皂股、創(chuàng)建多路服務(wù)器Selector夕凝,將創(chuàng)建的ServerSocketChannel注冊到Selector,監(jiān)聽SelectKey.Accept事件
4努潘、創(chuàng)建IO線程輪詢Selector.select
5呻此、如果輪詢到就緒的Channel如果是OP_ACCEPT狀態(tài)說明是新的客戶端接入則調(diào)用ServerSocketChannel.accept方法接受新的客戶端
6火俄、設(shè)置客戶端鏈路SocketChannel為非阻塞模式棘利,并且將其注冊到Selector上呢撞,監(jiān)聽SelectKey.OP_READ操作位
7蒜茴、如果輪詢到Channel為OP_READ,則說明SocketChannel中有新的就緒的數(shù)據(jù)包需要讀取洋机,則通過ByteBuffer讀取數(shù)據(jù)
由上來看,直接通過Java原生的類進(jìn)行NIO編程非常復(fù)雜繁瑣浇冰,而Netty正好解決了這個(gè)難題
下面通過一個(gè)簡單的Netty程序了解Netty開發(fā)
netty服務(wù)端代碼:

public class TimeServer {
    public void bind(int port) throws Exception {
        //創(chuàng)建兩個(gè)線程組 一個(gè)用于服務(wù)端接收客戶端的連接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //一個(gè)用于網(wǎng)絡(luò)讀寫
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
                .childHandler(new ChildChannelHander());
            ChannelFuture future = b.bind(port).sync();
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private class ChildChannelHander extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new TimeServerHandler());
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        if (args != null && args.length > 0) {
            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {

            }
        }
        try {
            new TimeServer().bind(port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class TimeServerHandler extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{
        ByteBuf byteBuf = (ByteBuf)msg;
        byte[] req = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(req);
        String body = new String(req,"UTF-8");
        System.out.println("the time server receive order:"+body);
        String currentTIme = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(
            System.currentTimeMillis()
        ).toString():"BAD ORDER";
        ByteBuf resp = Unpooled.copiedBuffer(currentTIme.getBytes());
        ctx.write(resp);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

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

}

程序剛開始創(chuàng)建了兩個(gè)EventLoopGroup線程贬媒,一個(gè)專門監(jiān)聽服務(wù)端接收客戶端請求,一個(gè)專門處理業(yè)務(wù)邏輯肘习,之后創(chuàng)建一個(gè)ServerBootstrap類,并將線程組傳遞到ServerBootstrap际乘,設(shè)置的Channel為NioServerSocketChannel并將事件處理類設(shè)置為ChildChannelHander,最后調(diào)用bind方法綁定監(jiān)聽端口漂佩,最后future.channel().closeFuture().sync()會(huì)阻塞直到服務(wù)端斷開連接
客戶端代碼:

public class TimeClient {
    public void connect(int port,String host) throws Exception {
        //創(chuàng)建讀寫io線程組
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY,true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        System.out.println("1");
                        socketChannel.pipeline().addLast(new TimeClientHandler());
                    }
                });
            System.out.println("2");
            ChannelFuture f = b.connect(host,port).sync();
            System.out.println("3");
            f.channel().closeFuture().sync();
            System.out.println("4");
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        if (args != null && args.length >0) {
            try {
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e) {

            }
        }
        try {
            new TimeClient().connect(port,"127.0.0.1");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Netty客戶端代碼更為簡單脖含,第一步創(chuàng)建NioEventLoopGroup線程組,跟服務(wù)端一樣再創(chuàng)建客戶端輔助類Bootstrap并將線程組和渠道作為參數(shù)出傳入投蝉,并且指定handler實(shí)現(xiàn)initChannel方法养葵,其作用就是當(dāng)創(chuàng)建NioSocketChannel之后回調(diào)initChannel方法處理網(wǎng)絡(luò)IO事件
下面是TimeChientHander代碼

public class TimeClientHandler extends ChannelHandlerAdapter {
    private final ByteBuf firstMsg;

    public TimeClientHandler() {
        byte[] req = "QUERY TIME ORDER".getBytes();
        firstMsg = Unpooled.buffer(req.length);
        firstMsg.writeBytes(req);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(firstMsg);

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req,"UTF-8");
        System.out.println("Now is : "+body);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Unexpected exception from downstream :"+cause.getMessage());
        ctx.close();
    }
}

TimeChientHander實(shí)現(xiàn)了channelActive,channelRead,exceptionCaught方法,
channelActive為客戶端或者服務(wù)端TCP鏈路建立連接之后回調(diào)瘩缆,當(dāng)服務(wù)端寫數(shù)據(jù)到客戶端時(shí)channelRead回調(diào)关拒,當(dāng)發(fā)生異常時(shí)會(huì)回調(diào)exceptionCaught方法。

從上面代碼可以看出Netty將IO事件的處理抽象為ChannelHandler接口,開發(fā)者可以繼承ChannelHandler接口來自定義事件處理邏輯庸娱。
ChannelHandler接口主要有以下方法:
channelRegistered(ChannelHandlerContext ctx) 注冊到EventLoop成功回調(diào)
channelActive(ChannelHandlerContext ctx) 當(dāng)建立連接后回調(diào)
channelRead(ChannelHandlerContext ctx, Object msg) 當(dāng)前channel讀到數(shù)據(jù)時(shí)回調(diào)
channelReadComplete(ChannelHandlerContext ctx) 讀取數(shù)據(jù)完成后回調(diào)
exceptionCaught(ChannelHandlerContext ctx, Throwable cause) 發(fā)生異常時(shí)回調(diào)

在實(shí)際Netty開發(fā)中基本都是繼承ChannelHandler實(shí)現(xiàn)業(yè)務(wù)邏輯

在初始化Channel時(shí)需要將自定義的ChannelHandler添加到該Channel上着绊,Netty實(shí)現(xiàn)是ChannelPipeline.addLast
源碼如下

public interface ChannelPipeline extends Iterable<Entry<String, ChannelHandler>> {
……
}

其中繼承Iterable迭代器接口,可以看出其hannelPipeline是一系列ChannelHandler的集合(可以理解為鏈表)自定義的ChannelHandler可以調(diào)用它的addFirst涌韩、addLast添加到IO處理pipeLine的頭或者尾
主要方法有以下:
ChannelPipeline addFirst(String name, ChannelHandler handler) 將handler插入到handler鏈表頭部
ChannelPipeline addLast(ChannelHandler... handlers) 將handler插入到handler鏈表尾部
自動(dòng)以的ChannelHandler一般通過上述方法添加到事件處理鏈表中

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畔柔,一起剝皮案震驚了整個(gè)濱河市氯夷,隨后出現(xiàn)的幾起案子臣樱,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雇毫,死亡現(xiàn)場離奇詭異玄捕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棚放,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門枚粘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人飘蚯,你說我怎么就攤上這事馍迄。” “怎么了局骤?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵攀圈,是天一觀的道長。 經(jīng)常有香客問我峦甩,道長赘来,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任凯傲,我火速辦了婚禮犬辰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冰单。我一直安慰自己幌缝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布诫欠。 她就那樣靜靜地躺著狮腿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呕诉。 梳的紋絲不亂的頭發(fā)上缘厢,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機(jī)與錄音甩挫,去河邊找鬼贴硫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛伊者,可吹牛的內(nèi)容都是我干的英遭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼亦渗,長吁一口氣:“原來是場噩夢啊……” “哼挖诸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起法精,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤多律,失蹤者是張志新(化名)和其女友劉穎痴突,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狼荞,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辽装,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了相味。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拾积。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丰涉,靈堂內(nèi)的尸體忽然破棺而出拓巧,到底是詐尸還是另有隱情,我是刑警寧澤一死,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布玲销,位于F島的核電站,受9級特大地震影響摘符,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逛裤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一瘩绒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧带族,春花似錦锁荔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至择克,卻和暖如春恬总,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肚邢。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工壹堰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骡湖。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓贱纠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親响蕴。 傳聞我的和親對象是個(gè)殘疾皇子谆焊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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