基於 Netty 實現 WebSocket 服務器

WebSocket 協議介紹

WebSocket 協議是一種在單個 TCP 鏈接上進行全雙工通訊的協議,在創建鏈接完成握手階段後,服務端也能夠主動推送數據給客戶端,使得 Web 瀏覽器和服務器之間的交互性更強大。html

目前 WebSocket 協議應用很是普遍,大部分瀏覽器均已支持 WebSocket,不單單在 Web 應用中,其餘不少類型應用(例如遊戲)也常常用到 WebSocket 協議。java

WebSocket 創建鏈接的過程

WebSocket 分爲握手階段( handshake )和數據傳輸階段( data transfer )。git

握手階段( handshake )

在客戶端和服務器創建 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: UpgradeUpgrade: websocket 表示客戶端想要升級協議爲 WebSocket。服務器進行以下響應完成握手:web

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

完成握手後,接下來就是雙向的數據傳輸的過程。segmentfault

數據傳輸階段( data transfer )

數據傳輸階段傳輸的內容以幀( frame )爲單位,其中分爲控制幀(Control Frame)和數據幀(Data Frame):瀏覽器

  • 控制幀(Control Frame):包括 ClosePingPong 幀,Close 用於關閉 WebSocket 鏈接,PingPong 用於心跳檢測
  • 數據幀(Data Frame):包括 TextBinary 幀,分別用於傳輸文本和二進制數據

Netty 實現 WebSocket 服務器

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 會幫咱們處理握手、ClosePingPong 幀等 WebSocket 協議底層,而且將 TextBinary 數據幀傳遞給 pipeline 中下一個 handler。也就是在下一個 handler 中,咱們只須要實現業務邏輯而無需關注 WebSocket 協議自己的細節。websocket

WebSocketServerProtocolHandler.java 216 行代碼中,會在 pipeline 中添加一個 WebSocketServerProtocolHandshakeHandler,用於處理握手階段。具體代碼位置:
https://github.com/netty/nett...socket

WebSocketServerProtocolHandshakeHandler 處理握手請求並響應,同時它會將自身從 pipeline 中移除,由於握手在創建 TCP 鏈接後僅須要處理一次。具體代碼位置:
https://github.com/netty/nett...

參考

關注個人公衆號

掃碼關注

相關文章
相關標籤/搜索