1. 程式人生 > >netty 實現 伺服器 客戶端通訊

netty 實現 伺服器 客戶端通訊

客戶端——伺服器連線

先囉嗦兩句,如果你還不知道Netty是做什麼的能做什麼。那可以先簡單的搜尋瞭解一下。我只能說Netty是一個NIO的框架,可以用於開發分散式的Java程式。具體能做什麼,各位可以儘量發揮想象。技術,是服務於人而不是侷限住人的。

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

  1. /** 
  2.  * Netty 服務端程式碼 
  3.  *  
  4.  * @author lihzh 
  5.  * @alia OneCoder
     
  6.  * @blog http://www.coderli.com 
  7.  */
  8. publicclass HelloServer {  
  9.     publicstaticvoid main(String args[]) {  
  10.         // Server服務啟動器
  11.         ServerBootstrap bootstrap = new ServerBootstrap(  
  12.                 new NioServerSocketChannelFactory(  
  13.                         Executors.newCachedThreadPool(),  
  14.                         Executors.newCachedThreadPool()));  
  15.         // 設定一個處理客戶端訊息和各種訊息事件的類(Handler)
  16.         bootstrap  
  17.                 .setPipelineFactory(new ChannelPipelineFactory() {  
  18.                     @Override
  19.                     public ChannelPipeline getPipeline()  
  20.                             throws
     Exception {  
  21.                         return Channels  
  22.                                 .pipeline(new HelloServerHandler());  
  23.                     }  
  24.                 });  
  25.         // 開放8000埠供客戶端訪問。
  26.         bootstrap.bind(new InetSocketAddress(8000));  
  27.     }  
  28.     privatestaticclass HelloServerHandler extends
  29.             SimpleChannelHandler {  
  30.         /** 
  31.          * 當有客戶端繫結到服務端的時候觸發,列印"Hello world, I'm server." 
  32.          *  
  33.          * @alia OneCoder 
  34.          * @author lihzh 
  35.          */
  36.         @Override
  37.         publicvoid channelConnected(  
  38.                 ChannelHandlerContext ctx,  
  39.                 ChannelStateEvent e) {  
  40.             System.out.println("Hello world, I'm server.");  
  41.         }  
  42.     }  
  43. }  
  1. /** 
  2.  * Netty 客戶端程式碼 
  3.  *  
  4.  * @author lihzh 
  5.  * @alia OneCoder 
  6.  * @blog http://www.coderli.com 
  7.  */
  8. publicclass HelloClient {  
  9.     publicstaticvoid main(String args[]) {  
  10.         // Client服務啟動器
  11.         ClientBootstrap bootstrap = new ClientBootstrap(  
  12.                 new NioClientSocketChannelFactory(  
  13.                         Executors.newCachedThreadPool(),  
  14.                         Executors.newCachedThreadPool()));  
  15.         // 設定一個處理服務端訊息和各種訊息事件的類(Handler)
  16.         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {  
  17.             @Override
  18.             public ChannelPipeline getPipeline() throws Exception {  
  19.                 return Channels.pipeline(new HelloClientHandler());  
  20.             }  
  21.         });  
  22.         // 連線到本地的8000埠的服務端
  23.         bootstrap.connect(new InetSocketAddress(  
  24.                 "127.0.0.1"8000));  
  25.     }  
  26.     privatestaticclass HelloClientHandler extends SimpleChannelHandler {  
  27.         /** 
  28.          * 當繫結到服務端的時候觸發,列印"Hello world, I'm client." 
  29.          *  
  30.          * @alia OneCoder 
  31.          * @author lihzh 
  32.          */
  33.         @Override
  34.         publicvoid channelConnected(ChannelHandlerContext ctx,  
  35.                 ChannelStateEvent e) {  
  36.             System.out.println("Hello world, I'm client.");  
  37.         }  
  38.     }  
  39. }  

既然是分散式的,自然要分多個服務。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的啟動程式碼:

  1. // 服務端設定一個處理客戶端訊息和各種訊息事件的類(Handler)
  2. bootstrap.setPipelineFactory(new ChannelPipelineFactory() {  
  3.     @Override
  4.     public ChannelPipeline getPipeline() throws Exception {  
  5.         return Channels.pipeline(  
  6.                 new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())),  
  7.                 new ObjectServerHandler());  
  8.     }  
  9. });  

  1. // 客戶端設定一個處理服務端訊息和各種訊息事件的類(Handler)
  2. bootstrap.setPipelineFactory(new ChannelPipelineFactory() {  
  3.     @Override
  4.     public ChannelPipeline getPipeline() throws Exception {  
  5.         return Channels.pipeline(new ObjectEncoder(), new ObjectClientHandler());  
  6.     }  
  7. });  

要傳遞物件,自然要有一個被傳遞模型,一個簡單的Pojo,當然,實現序列化介面是必須的。
  1. publicclass Command implements Serializable {  
  2.     privatestaticfinallong serialVersionUID = 7590999461767050471L;  
  3.     private String actionName;  
  4.     public String getActionName() {  
  5.         return actionName;  
  6.     }  
  7.     publicvoid setActionName(String actionName) {  
  8.         this.actionName = actionName;  
  9.     }  
  10. }  

服務端和客戶端裡,我們自定義的Handler實現如下:
  1. /** 
  2.  * 物件傳遞服務端程式碼 
  3.  * 
  4.  */
  5. publicclass ObjectServerHandler extends SimpleChannelHandler {  
  6.     /** 
  7.      * 當接受到訊息的時候觸發 
  8.      */
  9.     @Override
  10.     publicvoid messageReceived(ChannelHandlerContext ctx, MessageEvent e)  
  11.             throws Exception {  
  12.         Command command = (Command) e.getMessage();  
  13.         // 列印看看是不是我們剛才傳過來的那個
  14.         System.out.println(command.getActionName());  
  15.     }  
  16. }  

  1. /** 
  2.  * 物件傳遞,客戶端程式碼 
  3.  * 
  4.  */
  5. publicclass ObjectClientHandler extends SimpleChannelHandler {  
  6.     /** 
  7.      * 當繫結到服務端的時候觸發,給服務端發訊息。 
  8.      */
  9.     @Override
  10.     publicvoid channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {  
  11.         // 向服務端傳送Object資訊
  12.         sendObject(e.getChannel());  
  13.     }  
  14.     /** 
  15.      * 傳送Object 
  16.      * 
  17.      */
  18.     privatevoid sendObject(Channel channel) {