Netty5入門(3),netty5入門

來源:互聯網
上載者:User

Netty5入門(3),netty5入門

一、樣本介紹

樣本取自《基於Netty5.0進階案例一之NettyWebsocket》,和《Netty inAction》中11章的例子一樣,這個例子通過WebSocket實現了一個聊天室的群發功能。但後者的例子我沒本事跑通。

建立一個Maven項目,項目名稱叫NettyWebSocket,具體過程請參考前一貼。別忘了在pom.xml中加入netty5.0的依賴。

在項目中建立4個class:


4個類的代碼你可以從後面的內容中找到,這裡先不考慮代碼的問題。用“複製>>粘貼”將代碼原樣拷貝到這4個源檔案中再說。

注意,如果原始碼中缺少import語句,請自行fixed一下。

 

在NettyServe.java上右鍵,Run As >> Java Application,運行服務端代碼。此時控制台將輸出:

服務端開啟等待用戶端串連 ... ...

然後在磁碟上建立一個index.html檔案,檔案內容你也可以在後面的內容中找到。在Finder中雙擊index.html檔案,用Safari開啟。

在Dock欄上右擊Safari表徵圖,選擇“建立視窗”,開啟另一個Safari視窗。然後將第一個Safari視窗中的地址複製到第二個Safari視窗地址欄中,斷行符號。

將兩個視窗並列,你可以看到在一個視窗中輸入的聊天訊息,在另一個視窗中會即時得到重新整理,顯然是服務端通過WebSocket同時向所有串連的用戶端進行了推送:


在服務端控制台中也會有輸出:


測試完畢,我們下面再來介紹代碼。

注意,如果使用Safari測試,當你關閉Safari,服務端會輸出“用戶端與服務端串連關閉”。如果使用Chrome測試,當你關閉Chrome時,服務端會拋出一個“UnsupportedOperationException”異常。

 

二、Global.java

這個類很簡單,就是定義了一個全域變數ChannelGroup  group,這樣在後面的其它類(主要是MyWebSocketServerHandler)中就不用定義group了,直接使用就行了。

原始碼(如果你已經複製/粘貼過原始碼了,請跳過):

public class Global {

   public staticChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

}

 

三、ChildChannelHandler.java

這個類實現了ChannelInitializer,即Channel與ChannelHandler的綁定,也就是向pipeline中填入各個ChannelHandler。

原始碼(如果你已經複製/粘貼過原始碼了,請跳過):

public classChildChannelHandler extends ChannelInitializer<SocketChannel>{

   @Override

   protected void initChannel(SocketChannel e) throws Exception {

     

      e.pipeline().addLast("http-codec",new HttpServerCodec());

      e.pipeline().addLast("aggregator",new HttpObjectAggregator(65536));

      e.pipeline().addLast("http-chunked",new ChunkedWriteHandler());

      e.pipeline().addLast("handler",newMyWebSocketServerHandler());

   }

}

 

四、MyWebSocketServerHandler.java

伺服器所有的商務邏輯被放到這裡,當然也包括我們的WebSocket群發。

原始碼(如果你已經複製/粘貼過原始碼了,請跳過):

public class MyWebSocketServerHandlerextends

      SimpleChannelInboundHandler<Object>{

   private static final Logger logger = Logger

         .getLogger(WebSocketServerHandshaker.class.getName());

   private WebSocketServerHandshaker handshaker;

   @Override

   public voidchannelActive(ChannelHandlerContext ctx) throws Exception {

      // 添加

      Global.group.add(ctx.channel());

      System.out.println("用戶端與服務端串連開啟");

   }

   @Override

   public voidchannelInactive(ChannelHandlerContext ctx) throws Exception {

      // 移除

      Global.group.remove(ctx.channel());

      System.out.println("用戶端與服務端串連關閉");

   }

   @Override

   protected void messageReceived(ChannelHandlerContext ctx, Object msg)

         throws Exception {

      if (msg instanceof FullHttpRequest) {

         handleHttpRequest(ctx, ((FullHttpRequest) msg));

      } else if (msg instanceofWebSocketFrame) {

         handlerWebSocketFrame(ctx, (WebSocketFrame) msg);

      }

   }

   @Override

   public voidchannelReadComplete(ChannelHandlerContext ctx) throws Exception {

      ctx.flush();

   }

   private voidhandlerWebSocketFrame(ChannelHandlerContext ctx,

         WebSocketFrameframe) {

      // 判斷是否關閉鏈路的指令

      if (frame instanceof CloseWebSocketFrame) {

         handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame

               .retain());

         return;

      }

      // 判斷是否ping訊息

      else if (frame instanceofPingWebSocketFrame) {

         ctx.channel().write(

               new PongWebSocketFrame(frame.content().retain()));

         return;

      }

      // 本常式僅支援簡訊,不支援二進位訊息

      else if (!(frame instanceofTextWebSocketFrame)) {

         System.out.println("本常式僅支援簡訊,不支援二進位訊息");

         throw newUnsupportedOperationException(String.format(

               "%s frame types notsupported", frame.getClass().getName()));

      }

      // 返回應答訊息

      Stringrequest = ((TextWebSocketFrame) frame).text();

      System.out.println("服務端收到:" + request);

      if (logger.isLoggable(Level.FINE)) {

         logger

               .fine(String.format("%s received %s", ctx.channel(),

                     request));

      }

      TextWebSocketFrametws = new TextWebSocketFrame(new Date().toString()

            +ctx.channel().id() + ":" + request);

      // 群發

      Global.group.writeAndFlush(tws);

      // 返回【誰發的發給誰】

      // ctx.channel().writeAndFlush(tws);

   }

   private voidhandleHttpRequest(ChannelHandlerContext ctx,

         FullHttpRequestreq) {

      if (!req.getDecoderResult().isSuccess()

            ||(!"websocket".equals(req.headers().get("Upgrade")))) {

         sendHttpResponse(ctx, req, new DefaultFullHttpResponse(

               HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));

         return;

      }

      WebSocketServerHandshakerFactorywsFactory = new WebSocketServerHandshakerFactory(

            "ws://localhost:7397/websocket", null, false);

      handshaker = wsFactory.newHandshaker(req);

      if (handshaker == null) {

         WebSocketServerHandshakerFactory

               .sendUnsupportedWebSocketVersionResponse(ctx.channel());

      } else {

         handshaker.handshake(ctx.channel(), req);

      }

   }

   private static void sendHttpResponse(ChannelHandlerContext ctx,

         FullHttpRequestreq, DefaultFullHttpResponse res) {

      // 返回應答給用戶端

      if (res.getStatus().code()!= 200) {

         ByteBufbuf = Unpooled.copiedBuffer(res.getStatus().toString(),

               CharsetUtil.UTF_8);

         res.content().writeBytes(buf);

         buf.release();

      }

      // 如果是非Keep-Alive,關閉串連

      ChannelFuturef = ctx.channel().writeAndFlush(res);

      if (!isKeepAlive(req) || res.getStatus().code() != 200) {

         f.addListener(ChannelFutureListener.CLOSE);

      }

   }

   private static boolean isKeepAlive(FullHttpRequest req) {

      return false;

   }

   @Override

   public voidexceptionCaught(ChannelHandlerContext ctx, Throwable cause)

         throws Exception {

      cause.printStackTrace();

      ctx.close();

   }

}


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.