天天看點

netty 實作 伺服器 用戶端通信 用戶端——伺服器連接配接 用戶端————伺服器傳遞對象

先啰嗦兩句,如果你還不知道netty是做什麼的能做什麼。那可以先簡單的搜尋了解一下。我隻能說netty是一個nio的架構,可以用于開發分布式的java程式。具體能做什麼,各位可以盡量發揮想象。技術,是服務于人而不是局限住人的。

如果你已經萬事具備,那麼我們先從一段代碼開始。程式員們習慣的上手第一步,自然是"hello world",不過netty官網的例子卻偏偏抛棄了"hello world"。那我們就自己寫一個最簡單的"hello world"的例子,作為上手。

/** 

 * netty 服務端代碼 

 *  

 * @author lihzh 

 * @alia onecoder 

 * @blog http://www.coderli.com 

 */  

public class helloserver {  

    public static void main(string args[]) {  

        // server服務啟動器  

        serverbootstrap bootstrap = new serverbootstrap(  

                new nioserversocketchannelfactory(  

                        executors.newcachedthreadpool(),  

                        executors.newcachedthreadpool()));  

        // 設定一個處理用戶端消息和各種消息事件的類(handler)  

        bootstrap  

                .setpipelinefactory(new channelpipelinefactory() {  

                    @override  

                    public channelpipeline getpipeline()  

                            throws exception {  

                        return channels  

                                .pipeline(new helloserverhandler());  

                    }  

                });  

        // 開放8000端口供用戶端通路。  

        bootstrap.bind(new inetsocketaddress(8000));  

    }  

    private static class helloserverhandler extends  

            simplechannelhandler {  

        /** 

         * 當有用戶端綁定到服務端的時候觸發,列印"hello world, i'm server." 

         *  

         * @alia onecoder 

         * @author lihzh 

         */  

        @override  

        public void channelconnected(  

                channelhandlercontext ctx,  

                channelstateevent e) {  

            system.out.println("hello world, i'm server.");  

        }  

}  

 * netty 用戶端代碼 

public class helloclient {  

        // client服務啟動器  

        clientbootstrap bootstrap = new clientbootstrap(  

                new nioclientsocketchannelfactory(  

        // 設定一個處理服務端消息和各種消息事件的類(handler)  

        bootstrap.setpipelinefactory(new channelpipelinefactory() {  

            @override  

            public channelpipeline getpipeline() throws exception {  

                return channels.pipeline(new helloclienthandler());  

            }  

        });  

        // 連接配接到本地的8000端口的服務端  

        bootstrap.connect(new inetsocketaddress(  

                "127.0.0.1", 8000));  

    private static class helloclienthandler extends simplechannelhandler {  

         * 當綁定到服務端的時候觸發,列印"hello world, i'm client." 

        public void channelconnected(channelhandlercontext ctx,  

            system.out.println("hello world, i'm client.");  

既然是分布式的,自然要分多個服務。netty中,需要區分server和client服務。所有的client都是綁定在server上的,他們之間是不能通過netty直接通信的。(自己采用的其他手段,不包括在内。)。白話一下這個通信過程,server端開放端口,供client連接配接,client發起請求,連接配接到server指定的端口,完成綁定。随後便可自由通信。其實就是普通socket連接配接通信的過程。

netty架構是基于事件機制的,簡單說,就是發生什麼事,就找相關處理方法。就跟着火了找119,搶劫了找110一個道理。是以,這裡,我們處理的是當用戶端和服務端完成連接配接以後的這個事件。什麼時候完成的連接配接,netty知道,他告訴我了,我就負責處理。這就是架構的作用。netty,提供的事件還有很多,以後會慢慢的接觸和介紹。

說了這麼多廢話,才提到對象的傳輸,不知道您是不是已經不耐煩了。一個系統内部的消息傳遞,沒有對象傳遞是不太現實的。下面就來說說,怎麼傳遞對象。

如果,您看過前面的介紹,如果您善于專注本質,勤于思考。您應該也會想到,我們說過,netty的消息傳遞都是基于流,通過channelbuffer傳遞的,那麼自然,object也需要轉換成channelbuffer來傳遞。好在netty本身已經給我們寫好了這樣的轉換工具。 objectencoder和objectdecoder。

工具怎麼用?我們之前也說過,netty給我們處理自己業務的空間是在靈活的可自定義的handler上的,也就是說,如果我們自己去做這個轉換工作,那麼也應該在handler裡去做。而netty,提供給我們的objectencoder和decoder也恰恰是一組 handler。于是,修改server和client的啟動代碼:

netty 實作 伺服器 用戶端通信 用戶端——伺服器連接配接 用戶端————伺服器傳遞對象

// 服務端設定一個處理用戶端消息和各種消息事件的類(handler)  

bootstrap.setpipelinefactory(new channelpipelinefactory() {  

    @override  

    public channelpipeline getpipeline() throws exception {  

        return channels.pipeline(  

                new objectdecoder(classresolvers.cachedisabled(this.getclass().getclassloader())),  

                new objectserverhandler());  

});  

netty 實作 伺服器 用戶端通信 用戶端——伺服器連接配接 用戶端————伺服器傳遞對象

// 用戶端設定一個處理服務端消息和各種消息事件的類(handler)  

        return channels.pipeline(new objectencoder(), new objectclienthandler());  

要傳遞對象,自然要有一個被傳遞模型,一個簡單的pojo,當然,實作序列化接口是必須的。

netty 實作 伺服器 用戶端通信 用戶端——伺服器連接配接 用戶端————伺服器傳遞對象

public class command implements serializable {  

    private static final long serialversionuid = 7590999461767050471l;  

    private string actionname;  

    public string getactionname() {  

        return actionname;  

    public void setactionname(string actionname) {  

        this.actionname = actionname;  

服務端和用戶端裡,我們自定義的handler實作如下:

netty 實作 伺服器 用戶端通信 用戶端——伺服器連接配接 用戶端————伺服器傳遞對象

 * 對象傳遞服務端代碼 

 * 

public class objectserverhandler extends simplechannelhandler {  

    /** 

     * 當接受到消息的時候觸發 

     */  

    public void messagereceived(channelhandlercontext ctx, messageevent e)  

            throws exception {  

        command command = (command) e.getmessage();  

        // 列印看看是不是我們剛才傳過來的那個  

        system.out.println(command.getactionname());  

netty 實作 伺服器 用戶端通信 用戶端——伺服器連接配接 用戶端————伺服器傳遞對象

 * 對象傳遞,用戶端代碼 

public class objectclienthandler extends simplechannelhandler {  

     * 當綁定到服務端的時候觸發,給服務端發消息。 

    public void channelconnected(channelhandlercontext ctx, channelstateevent e) {  

        // 向服務端發送object資訊  

        sendobject(e.getchannel());  

     * 發送object 

     * 

    private void sendobject(channel channel) {  

        command command = new command();  

        command.setactionname("hello action.");  

        channel.write(command);  

啟動後,服務端正常列印結果:hello action.

簡單梳理一下思路:

通過netty傳遞,都需要基于流,以channelbuffer的形式傳遞。是以,object -> channelbuffer.

netty提供了轉換工具,需要我們配置到handler。

樣例從用戶端 -> 服務端,單向發消息,是以在用戶端配置了編碼,服務端解碼。如果雙向收發,則需要全部配置encoder和decoder。

這裡需要注意,注冊到server的handler是有順序的,如果你颠倒一下注冊順序:

netty 實作 伺服器 用戶端通信 用戶端——伺服器連接配接 用戶端————伺服器傳遞對象

                new objectserverhandler(),  

                new objectdecoder(classresolvers.cachedisabled(this.getclass().getclassloader())));  

結果就是,會先進入我們自己的業務,再進行解碼。這自然是不行的,會強轉失敗。至此,你應該會用netty傳遞對象了吧。