在線聊天室的實現(2)--基於Netty 4.x的Echo服務器實現


前言:
  就如前文所講述的, 聊天室往往是最基本的網絡編程的學習案例. 本文以WebSocket為底層協議, 實現一個簡單的基於web客戶端的Echo服務.
  服務器采用Netty 4.x來實現, 源於其對websocket的超強支持, 基於卓越的性能和穩定.
  本系列的文章鏈接如下:
  1). websocket協議和javascript版的api 

要點提示:
  Netty作為高性能網絡編程框架, 其所有的網絡IO操作皆為異步方式驅動. 而其核心的概念之一: ChannelHandler. 由一組ChannelHandler構成了ChannelPipeline, 決定了其編解碼(Codec)/數據流(DataFlow)/業務處理(Logic Handler)的具體行為.
  ChannelHanlder的自由組合和清晰的職責划分, 讓Netty更加的靈活和重要.

  
  WebSocket協議包括握手數據傳輸這兩個階段. 前者的握手是基於HTTP/HTTPS協議的, 而后者的數據傳輸則基於TCP的雙向通訊模式. 數據以Frame的方式來組織和交互.
  本文不是Netty的學習文章, 這邊就略為帶過, 具體見后邊的解釋代碼.

服務端:
  基於之上的要點提要, 我們迅速來進行服務端的代碼編寫.
  使用netty 4.x版本, 其maven的依賴配置如下:

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.0.29.Final</version>
        </dependency>

  服務端的代碼如下:

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
  ServerBootstrap serverBootstrap = new ServerBootstrap();

  serverBootstrap.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .childHandler(new ChannelInitializer<SocketChannel>() {
      @Override
      protected void initChannel(SocketChannel socketChannel) throws Exception {
        // pipeline的設置, 參看下面
      }
    });

  ChannelFuture f = serverBootstrap.bind(8123).sync();
  f.channel().closeFuture().sync();
} finally {
  bossGroup.shutdownGracefully();
  workerGroup.shutdownGracefully();
}

  注: 這邊是主體的服務器配置和啟動代碼, 其一如既然的簡潔.
  核心的pipeline設置代碼如下所示:

ChannelPipeline cp = socketChannel.pipeline();
// *) 支持http協議的解析
cp.addLast(new HttpServerCodec());
cp.addLast(new HttpObjectAggregator(65535));
// *) 對於大文件支持 chunked方式寫
cp.addLast(new ChunkedWriteHandler());
// *) 對websocket協議的處理--握手處理, ping/pong心跳, 關閉
cp.addLast(new WebSocketServerProtocolHandler("/echoserver"));
// *) 對TextWebSocketFrame的處理
cp.addLast(new SimpleChannelInboundHandler<TextWebSocketFrame>() {
  @Override
  protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
    // *) echo 邏輯
    ctx.writeAndFlush(new TextWebSocketFrame(msg.text()));
  }
});

  注: HttpServerCodec和HttpObjectAggregator已經幫我們封裝好了WebSocket的握手FullHttpRequest/FullHttpResponse包和各類數據Frame包. WebSocketServerProtocolHandler隱藏了握手的細節處理, 以及心跳處理和關閉響應. 多個ChannelHanlder的疊加和WebSocket協議本身的復雜是密切先關的.

客戶端:
  這邊只是個演示項目, 因此盡量簡潔地去實現.

<div style="margin:0 auto; width: 800px;">
  <textarea id="taMessages" style="width: 360px; height: 200px;" readonly ></textarea>
  <br />
  <input id="btnMessage" type="text" style="float:left; width:300px;" />
  <input id="btnSend" type="button" value="Send" disabled="disabled" onclick="sendMessage();"/>
</div>

<script>
  /* 注意瀏覽器js的執行順序 */
  var wsServer = 'ws://localhost:8123/echoserver'; //服務器地址
  var websocket = new WebSocket(wsServer); //創建WebSocket對象

  websocket.onopen = function(evt) {
    document.getElementById("btnSend").disabled = false;
  }
  websocket.onmessage = function(evt) {
    document.getElementById("taMessages").value += evt.data;
  }
  websocket.onclose = function(evt) {
  }
  websocket.onerror = function(evt) {
  }

  function sendMessage() {
    var message = document.getElementById('btnMessage').value;
    if ( websocket.readyState == WebSocket.OPEN ) {
      websocket.send(message);
    }
    document.getElementById('btnMessage').value = '';
  }
</script>

  注: 發送數據到服務端, 然后把服務端返回的數據追加到文本區域中.

效果:
  在chrome瀏覽器中, 效果如下:
  
  點擊send按鈕后, 經服務器返回其消息.
  
  消息在大文本區域中展示. 看來Echo服務一切正常.
  其實這是個悲傷的故事, 你覺得呢?

寫在最后:
  
如果你覺得這篇文章對你有幫助, 請小小打賞下. 其實我想試試, 看看寫博客能否給自己帶來一點小小的收益. 無論多少, 都是對樓主一種由衷的肯定.

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM