慕課網《Netty入門之WebSocket初體驗》學習總結

daqianmen發表於2021-09-09

慕課網《Netty入門之WebSocket初體驗》學習總結

時間:2018年04月11日星期三
說明:本文部分內容均來自慕課網。@慕課網: 
教學原始碼:
學習原始碼:
第一章:課程介紹
1-1 課程介紹

什麼是Netty

高效能、事件驅動、非同步非阻塞的IO Java開源框架
基於NIO的客戶端,服務端程式設計框架
非常可靠的穩定性和伸縮性

Netty使用場景

高效能領域:遊戲、大資料、分散式計算
多執行緒併發領域:多執行緒模型、主從多執行緒模型
非同步通訊領域:非同步非阻塞,主動獲取或透過通知機制來得到結果

課程提綱

IO通訊:BIO、偽非同步IO、NIO、AIO通訊
Netty入門:原生NIO的缺陷、Netty的優勢
WebSocket入門:什麼是WebSocket、如何建立連線、生命週期及關閉
Netty實現WebSocket通訊案例

課程要求

有一定的Java基礎
有一定的IO程式設計基礎
瞭解Java的BIO、偽非同步IO、NIO和AIO
第二章:IO通訊
2-1 IO通訊

BIO通訊

一個執行緒負責連線
一請求一應答
缺乏彈性伸縮能力

BIO通訊模型

圖片描述

偽非同步IO通訊

執行緒池負責連線
M請求N應答
執行緒池阻塞

偽非同步IO通訊模型

圖片描述

NIO通訊

緩衝區Buffer
通道Channel
多路複用器Selector

AIO通訊

連線註冊讀寫事件和回撥函式
讀寫方法非同步
主動通知程式

四種IO對比

圖片描述

第三章:Netty入門
3-1 Netty入門

原生NIO的缺陷

類庫和API繁雜
入門門檻高
工作量和難度大
JDK NIO存在BUG

Netty的優勢

API使用簡單,定製能力強,可以透過ChannelHandler對框架進行靈活的擴充套件
入門門檻低,功能強大,預製了多種編解碼功能,支援多種主流協議
效能高,透過與其他的業界主流的NIO框架對比,Netty的綜合效能最優
Netty比較成熟穩定,Netty修復了JDK NIO所有發現的BUG
第四章:WebSocket入門
4-1 WebSocket入門

什麼是WebSocket

H5協議規範:H5提出的協議規範
握手機制:使客戶端與伺服器能夠建立類似TCP的連線,方便通訊
解決客戶端與服務端實時通訊而產生的技術:基於TCP的協議

WebSocket的優點

節省通訊開銷:以前使用輪詢,開銷較大
伺服器主動傳送資料給客戶端:任意時刻,相互傳送資料
實時通訊:可以彼此相互推送資訊

WebSocket建立連線

客戶端發起握手請求
伺服器響應請求
連線建立

WebSocket生命週期

開啟事件:發生新的連線時呼叫、在端點上建立連線時且在任何事件之前
訊息事件:接收WebSocket對話中,另一端傳送的訊息
錯誤事件:在進行連線或端點發生錯誤時產生
關閉事件:表示WebSocket端點的連線關閉

WebSocket關閉連線

伺服器關閉底層TCP連線
客戶端發起TCP Close
第五章:通訊案例
5-1 通訊案例

基於Netty實現WebSocket通訊案例

功能介紹

Netty開發服務端
Html實現客戶端
實現服務端與客戶端的實時互動

程式碼編寫

1.建立名為netty-websocket的maven工程pom如下

4.0.0com.myimoocnetty-websocket0.0.1-SNAPSHOTio.nettynetty-all5.0.0.Alpha1org.apache.maven.pluginsmaven-compiler-plugin1.8<source>1.8UTF-8

2.編寫NettyConfig類

package com.myimooc.netty.websocket;

import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * 
* 標題: Netty 全域性配置類
* 描述: 儲存整個工程的全域性配置
* * @author zc * @date 2018/04/11 */ public class NettyConfig { /** * 儲存每一個客戶端接入進來時的 Channel */ public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); }

3.編寫MyWebSocketHandler類

package com.myimooc.netty.websocket;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

/**
 * 
* 標題: 處理客戶端WebSocket請求的核心業務處理類
* 描述: 接收/處理/響應 客戶端websocket請求的核心業務處理類
* * @author zc * @date 2018/04/11 */ public class MyWebSocketHandler extends SimpleChannelInboundHandler { private WebSocketServerHandshaker handshaker; private static final String WEB_SOCKET_URL = "ws://localhost:8888/websocket"; /** * 服務端處理客戶端websocket請求的核心方法 */ @Override protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { // 處理客戶端向服務端發起http握手請求的業務 FullHttpRequest request = (FullHttpRequest) msg; this.handHttpRequest(ctx, request); } else if (msg instanceof WebSocketFrame) { // 處理websocket連線的業務 WebSocketFrame frame = (WebSocketFrame) msg; this.handWebSocketFrame(ctx, frame); } } /** * 處理客戶端與服務端之前的websocket業務 */ private void handWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { if (frame instanceof CloseWebSocketFrame){ // 如果是關閉websocket的指令 handshaker.close(ctx.channel(),(CloseWebSocketFrame)frame.retain()); } if (frame instanceof PingWebSocketFrame){ // 如果是ping訊息 ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } if (!(frame instanceof TextWebSocketFrame)){ // 如果不是文字訊息,則丟擲異常 System.out.println("目前暫不支援二進位制訊息"); throw new RuntimeException("【"+this.getClass().getName()+"】不支援二進位制訊息"); } // 獲取客戶端向服務端傳送的文字訊息 String request = ((TextWebSocketFrame) frame).text(); System.out.println("服務端收到客戶端的訊息=====>>>" + request); // 將客戶端發給服務端的訊息返回給客戶端 TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + ctx.channel().id() + "====>>>" + request); // 群發,服務端向每個連線上來的客戶端群發訊息 NettyConfig.group.writeAndFlush(tws); } /** * 處理客戶端向服務端發起http握手請求的業務 */ private void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) { if (!request.getDecoderResult().isSuccess() || !("websocket").equals(request.headers().get("Upgrade"))) { // 不是websocket握手請求時 this.sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); return; } WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false); handshaker = wsFactory.newHandshaker(request); if (handshaker == null) { WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); } else { handshaker.handshake(ctx.channel(), request); } } /** * 服務端向客戶端響應訊息 */ private void sendHttpResponse(ChannelHandlerContext ctc, FullHttpMessage request, DefaultFullHttpResponse response) { if (response.getStatus().code() != 200) { ByteBuf buf = Unpooled.copiedBuffer(response.getStatus().toString(), CharsetUtil.UTF_8); response.content().writeBytes(buf); buf.release(); } // 服務端向客戶端傳送資料 ChannelFuture future = ctc.channel().writeAndFlush(response); if (response.getStatus().code() != 200) { future.addListener(ChannelFutureListener.CLOSE); } } /** * 工程出現異常時呼叫 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } /** * 客戶端與服務端建立連線時呼叫 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { NettyConfig.group.add(ctx.channel()); System.out.println("客戶端與服務端連線開啟..."); } /** * 客戶端與服務端斷開連線時呼叫 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { NettyConfig.group.remove(ctx.channel()); System.out.println("客戶端與服務端連線關閉..."); } /** * 服務端接收客戶端傳送過來的資料結束之後呼叫 */ @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } }

4.編寫MyWebSocketChannelHandler類

package com.myimooc.netty.websocket;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * 
* 標題: 初始化連線時的各個元件
* 描述: 初始化連線時的各個元件
* * @author zc * @date 2018/04/11 */ public class MyWebSocketChannelHandler extends ChannelInitializer { @Override protected void initChannel(SocketChannel ch) throws Exception { // 將請求和應答訊息解碼為HTTP訊息 ch.pipeline().addLast("http-codec",new HttpServerCodec()); // 將HTTP訊息的多個部分合成一條完整的HTTP訊息 ch.pipeline().addLast("aggregator",new HttpObjectAggregator(65536)); // 向客戶端傳送HTML5檔案 ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler()); ch.pipeline().addLast("handler",new MyWebSocketHandler()); } }

5.編寫AppStart類

package com.myimooc.netty.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * 
* 標題: 程式入口
* 描述: 啟動應用
* * @author zc * @date 2018/04/11 */ public class AppStart { public static void main(String[] args) { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workGroup); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.childHandler(new MyWebSocketChannelHandler()); System.out.println("服務端開啟等待客戶端連線..."); Channel channel = serverBootstrap.bind(8888).sync().channel(); channel.closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { // 優雅的退出程式 bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }

6.編寫websocket.html





WebSocket客戶端



客戶端接收到服務端返回的應答訊息

第六章:課程總結
6-1 課程總結

課程總結

課程介紹
IO通訊:四種IO通訊
Netty入門:原生NIO的缺點,Netty的優點
WebSocket入門:WebSocket的優點,如何使用
通訊案例:Netty實現WebSocket通訊案例

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1727/viewspace-2800290/,如需轉載,請註明出處,否則將追究法律責任。

相關文章