netty 實現 伺服器 客戶端通訊
客戶端——伺服器連線
先囉嗦兩句,如果你還不知道Netty是做什麼的能做什麼。那可以先簡單的搜尋瞭解一下。我只能說Netty是一個NIO的框架,可以用於開發分散式的Java程式。具體能做什麼,各位可以儘量發揮想象。技術,是服務於人而不是侷限住人的。
如果你已經萬事具備,那麼我們先從一段程式碼開始。程式設計師們習慣的上手第一步,自然是"Hello world",不過Netty官網的例子卻偏偏拋棄了"Hello world"。那我們就自己寫一個最簡單的"Hello world"的例子,作為上手。
- /**
- * Netty 服務端程式碼
- *
- * @author lihzh
-
* @alia OneCoder
- * @blog http://www.coderli.com
- */
- publicclass HelloServer {
- publicstaticvoid main(String args[]) {
- // Server服務啟動器
- ServerBootstrap bootstrap = new ServerBootstrap(
- new NioServerSocketChannelFactory(
-
Executors.newCachedThreadPool(),
- Executors.newCachedThreadPool()));
- // 設定一個處理客戶端訊息和各種訊息事件的類(Handler)
- bootstrap
- .setPipelineFactory(new ChannelPipelineFactory() {
- @Override
- public ChannelPipeline getPipeline()
-
throws
- return Channels
- .pipeline(new HelloServerHandler());
- }
- });
- // 開放8000埠供客戶端訪問。
- bootstrap.bind(new InetSocketAddress(8000));
- }
- privatestaticclass HelloServerHandler extends
- SimpleChannelHandler {
- /**
- * 當有客戶端繫結到服務端的時候觸發,列印"Hello world, I'm server."
- *
- * @alia OneCoder
- * @author lihzh
- */
- @Override
- publicvoid channelConnected(
- ChannelHandlerContext ctx,
- ChannelStateEvent e) {
- System.out.println("Hello world, I'm server.");
- }
- }
- }
- /**
- * Netty 客戶端程式碼
- *
- * @author lihzh
- * @alia OneCoder
- * @blog http://www.coderli.com
- */
- publicclass HelloClient {
- publicstaticvoid main(String args[]) {
- // Client服務啟動器
- ClientBootstrap bootstrap = new ClientBootstrap(
- new NioClientSocketChannelFactory(
- Executors.newCachedThreadPool(),
- Executors.newCachedThreadPool()));
- // 設定一個處理服務端訊息和各種訊息事件的類(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));
- }
- privatestaticclass HelloClientHandler extends SimpleChannelHandler {
- /**
- * 當繫結到服務端的時候觸發,列印"Hello world, I'm client."
- *
- * @alia OneCoder
- * @author lihzh
- */
- @Override
- publicvoid channelConnected(ChannelHandlerContext ctx,
- ChannelStateEvent e) {
- 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的啟動程式碼:
- // 服務端設定一個處理客戶端訊息和各種訊息事件的類(Handler)
- bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
- @Override
- public ChannelPipeline getPipeline() throws Exception {
- return Channels.pipeline(
- new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())),
- new ObjectServerHandler());
- }
- });
- // 客戶端設定一個處理服務端訊息和各種訊息事件的類(Handler)
- bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
- @Override
- public ChannelPipeline getPipeline() throws Exception {
- return Channels.pipeline(new ObjectEncoder(), new ObjectClientHandler());
- }
- });
要傳遞物件,自然要有一個被傳遞模型,一個簡單的Pojo,當然,實現序列化介面是必須的。
- publicclass Command implements Serializable {
- privatestaticfinallong serialVersionUID = 7590999461767050471L;
- private String actionName;
- public String getActionName() {
- return actionName;
- }
- publicvoid setActionName(String actionName) {
- this.actionName = actionName;
- }
- }
服務端和客戶端裡,我們自定義的Handler實現如下:
- /**
- * 物件傳遞服務端程式碼
- *
- */
- publicclass ObjectServerHandler extends SimpleChannelHandler {
- /**
- * 當接受到訊息的時候觸發
- */
- @Override
- publicvoid messageReceived(ChannelHandlerContext ctx, MessageEvent e)
- throws Exception {
- Command command = (Command) e.getMessage();
- // 列印看看是不是我們剛才傳過來的那個
- System.out.println(command.getActionName());
- }
- }
- /**
- * 物件傳遞,客戶端程式碼
- *
- */
- publicclass ObjectClientHandler extends SimpleChannelHandler {
- /**
- * 當繫結到服務端的時候觸發,給服務端發訊息。
- */
- @Override
- publicvoid channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
- // 向服務端傳送Object資訊
- sendObject(e.getChannel());
- }
- /**
- * 傳送Object
- *
- */
- privatevoid sendObject(Channel channel) {