WebSocket 協議是一種在單個 TCP 鏈接上進行全雙工通訊的協議,在創建鏈接完成握手階段後,服務端也能夠主動推送數據給客戶端,使得 Web 瀏覽器和服務器之間的交互性更強大。html
目前 WebSocket 協議應用很是普遍,大部分瀏覽器均已支持 WebSocket,不單單在 Web 應用中,其餘不少類型應用(例如遊戲)也常常用到 WebSocket 協議。java
WebSocket 分爲握手階段( handshake )和數據傳輸階段( data transfer )。git
在客戶端和服務器創建 WebSocket 鏈接以前,客戶端首先要發送一個 HTTP 協議的握手請求:github
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
其中請求頭 Connection: Upgrade
和 Upgrade: websocket
表示客戶端想要升級協議爲 WebSocket。服務器進行以下響應完成握手:web
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat
完成握手後,接下來就是雙向的數據傳輸的過程。segmentfault
數據傳輸階段傳輸的內容以幀( frame )爲單位,其中分爲控制幀(Control Frame)和數據幀(Data Frame):瀏覽器
Close
、Ping
、Pong
幀,Close
用於關閉 WebSocket 鏈接,Ping
和 Pong
用於心跳檢測Text
和 Binary
幀,分別用於傳輸文本和二進制數據public class WebSocketServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); // HTTP 協議解析,用於握手階段 pipeline.addLast(new HttpObjectAggregator(65536)); // HTTP 協議解析,用於握手階段 pipeline.addLast(new WebSocketServerCompressionHandler()); // WebSocket 數據壓縮擴展 pipeline.addLast(new WebSocketServerProtocolHandler("/", null, true)); // WebSocket 握手、控制幀處理 pipeline.addLast(new MyWebSocketServerHandler()); } }); ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } class MyWebSocketServerHandler extends SimpleChannelInboundHandler<WebSocketFrame> { @Override protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { if (frame instanceof TextWebSocketFrame) { // 此處僅處理 Text Frame String request = ((TextWebSocketFrame) frame).text(); ctx.channel().writeAndFlush(new TextWebSocketFrame("收到: " + request)); } } }
以上是 Netty 實現的一個簡單的 WebSocket 的服務器。啓動成功後,能夠網上搜索 WebSocket 在線測試工具鏈接 ws://localhost:8080/ 進行測試。服務器
WebSocketServerProtocolHandler
會幫咱們處理握手、Close
、Ping
、Pong
幀等 WebSocket 協議底層,而且將 Text
和 Binary
數據幀傳遞給 pipeline 中下一個 handler。也就是在下一個 handler 中,咱們只須要實現業務邏輯而無需關注 WebSocket 協議自己的細節。websocket
在 WebSocketServerProtocolHandler.java
216 行代碼中,會在 pipeline 中添加一個 WebSocketServerProtocolHandshakeHandler
,用於處理握手階段。具體代碼位置:
https://github.com/netty/nett...socket
WebSocketServerProtocolHandshakeHandler
處理握手請求並響應,同時它會將自身從 pipeline 中移除,由於握手在創建 TCP 鏈接後僅須要處理一次。具體代碼位置:
https://github.com/netty/nett...