程序员

基于 Netty 实现 WebSocket 服务器

作者:admin 2021-04-16 我要评论

WebSocket 协议介绍 WebSocket 协议是一种在单个 TCP 连接上进行全双工通信的协议,在建立连接完成握手阶段后,服务端也可以主动推送数据给客户端,使得 Web 浏...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

WebSocket 协议介绍

WebSocket 协议是一种在单个 TCP 连接上进行全双工通信的协议,在建立连接完成握手阶段后,服务端也可以主动推送数据给客户端,使得 Web 浏览器和服务器之间的交互性更强大。

目前 WebSocket 协议应用非常广泛,大部分浏览器均已支持 WebSocket,不仅仅在 Web 应用中,其他很多类型应用(例如游戏)也经常用到 WebSocket 协议。

WebSocket 建立连接的过程

WebSocket 分为握手阶段( handshake )和数据传输阶段( data transfer )。

握手阶段( handshake )

在客户端和服务器建立 WebSocket 连接之前,客户端首先要发送一个 HTTP 协议的握手请求:

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。服务器进行如下响应完成握手:

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

完成握手后,接下来就是双向的数据传输的过程。

数据传输阶段( 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 协议本身的细节。

WebSocketServerProtocolHandler.java 216 行代码中,会在 pipeline 中添加一个 WebSocketServerProtocolHandshakeHandler,用于处理握手阶段。具体代码位置:
https://github.com/netty/nett...

WebSocketServerProtocolHandshakeHandler 处理握手请求并响应,同时它会将自身从 pipeline 中移除,因为握手在建立 TCP 连接后仅需要处理一次。具体代码位置:
https://github.com/netty/nett...

参考

关注我的公众号

扫码关注


本文转自网络,版权归原作者所有,原文链接:https://segmentfault.com/a/1190000039824948

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • 四两拨千斤——你不知道的VScode编码Ty

    四两拨千斤——你不知道的VScode编码Ty

  • 我是如何在 Vue 项目中做代码分割的

    我是如何在 Vue 项目中做代码分割的

  • position:sticky 粘性定位的几种巧妙应

    position:sticky 粘性定位的几种巧妙应

  • 从零到一搭建React组件库

    从零到一搭建React组件库

腾讯云代理商
海外云服务器