netty对websocket协议的实现

这篇具有很好参考价值的文章主要介绍了netty对websocket协议的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. websocket协议

websocket协议是对http协议的扩充, 也是使用的TCP协议可以全双工通信的应用层协议。 websocket协议允许服务端向客户端推送消息。 浏览器和服务端只需要进行一次握手,不必像http协议一样,每次连接都要新建立连接,两者之间创建持久性的连接,并进行双向的数据交互。

http/1.1 是 请求-响应设计的,后来支持了更多的传输类型 图片,但都是基于请求响应。

不足:

  • 传输数据为文本,且请求头与响应头冗长重复。
  • 请求-响应模式,只能客户端发送请求给服务端,服务端才可以发送响应数据给客户端。
1. websocket连接建立过程

netty实现websocket,netty,websocket,netty,nio,http,Handler

websocket首次请求服务端建立连接,也是客户端发起的,基于http请求的。 请求头中多携带消息

GET /test HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: tFGdnEL/5fXMS9yKwBjllg==
Origin: http://example.com
Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Version: 13

首先客户端(如浏览器)发出带有特殊消息头(UpgradeConnection)的请求到服务器,服务器判断是否支持升级,支持则返回响应状态码101,表示协议升级成功,对于WebSocket就是握手成功。

  • Connection必须设置Upgrade,表示客户端希望连接升级。
  • Upgrade: websocket表明协议升级为websocket。
  • Sec-WebSocket-Key字段内记录着握手过程中必不可少的键值,由客户端(浏览器)生成,可以尽量避免普通HTTP请求被误认为Websocket协议。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13。
  • Origin字段是必须的。如果缺少origin字段,WebSocket服务器需要回复HTTP 403 状态码(禁止访问),通过Origin可以做安全校验。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HaA6EjhHRejpHyuO0yBnY4J4n3A=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
Sec-WebSocket-Protocol: v12.stomp

Sec-WebSocket-Accept的字段值是由握手请求中的Sec-WebSocket-Key的字段值生成的。成功握手确立WebSocket连接之后,通信时不再使用HTTP的数据帧,而采用WebSocket独立的数据帧。

2. 消息帧

WebSocket使用二进制消息帧作为双向通信的媒介。何为消息帧?发送方将每个应用程序消息拆分为一个或多个帧,通过网络将它们传输到目的地,并重新组装解析出一个完整消息。

有别于HTTP/1.1文本消息格式(冗长的消息头和分隔符等),WebSocket消息帧规定一定的格式,以二进制传输,更加短小精悍。二者相同之处就是都是基于TCP/IP流式协议(没有规定消息边界)

netty实现websocket,netty,websocket,netty,nio,http,Handler

  • FIN: 1 bit,表示该帧是否为消息的最后一帧。1-是,0-否。
  • RSV1,RSV2,RSV3: 1 bit each,预留(3位),扩展的预留标志。一般情况为0,除非协商的扩展定义为非零值。如果接收到非零值且不为协商扩展定义,接收端必须使连接失败。
  • Opcode: 4 bits,定义消息帧的操作类型,如果接收到一个未知Opcode,接收端必须使连接失败。(0x0-延续帧,0x1-文本帧,0x2-二进制帧,0x8-关闭帧,0x9-PING帧,0xA-PONG帧(在接收到PING帧时,终端必须发送一个PONG帧响应,除非它已经接收到关闭帧),0x3-0x7保留给未来的非控制帧,0xB-F保留给未来的控制帧)
  • Mask: 1 bit,表示该帧是否为隐藏的,即被加密保护的。1-是,0-否。Mask=1时,必须传一个Masking-key,用于解除隐藏(客户端发送消息给服务器端,Mask必须为1)。
  • Payload length: 7 bits, 7+16 bits, or 7+64 bits,有效载荷数据的长度(扩展数据长度+应用数据长度,扩展数据长度可以为0)。
  • Masking-key: 0 or 4 bytes,用于解除帧隐藏(加密)的key,Mask=1时不为空,Mask=0时不用传。
  • Payload data: (x+y) bytes,有效载荷数据包括扩展数据(x bytes)和应用数据(y bytes)。有效载荷数据是用户真正要传输的数据。

这样的二进制消息帧设计,与HTTP协议相比,WebSocket协议可以提供约500:1的流量减少和3:1的延迟减少。

3. 关闭连接

挥手相对于握手要简单很多,客户端和服务器端任何一方都可以通过发送关闭帧来发起挥手请求。发送关闭帧的一方,之后不再发送任何数据给对方;接收到关闭帧的一方,如果之前没有发送过关闭帧,则必须发送一个关闭帧作为响应。关闭帧中可以携带关闭原因。

在发送和接收一个关闭帧消息之后,就认为WebSocket连接已关闭,且必须关闭底层TCP连接。

除了通过关闭握手来关闭连接外,WebSocket连接也可能在另一方离开或底层TCP连接关闭时突然关闭。

协议介绍与图片来自https://blog.csdn.net/weixin_36586120/article/details/120025498

4. 优点
  • 较少的控制开销。在连接建立后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对于HTTP请求每次都要携带完整的头部,显著减少。
  • 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少。
  • 保持连接状态。与HTTP不同的是,Websocket需要先建立连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  • 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
  • 支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。
  • 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著提高压缩率。
2. netty对websocket协议的实现
1.过程
  1. 初始handler需要添加http协议的编解码器。

    添加 HttpServerCodec 转为HttpRequest,添加HttpObjectAggregator将http消息聚合为一个FullHttpRequest,因为websocket的协议handler的channerRead接受的是该参数。

  2. 添加WebSocketServerProtocolHandler,这个是websocket协议的处理器,会处理首次请求的握手操作,并升级协议,更换处理器。

  3. 当客户端连接时,WebSocketServerProtocolHandler 触发 handlerAdded()回调,会立刻为这个channel注册一个握手处理器。 握手处理器位置是先于websocket处理器 但是晚于http协议处理器。

    if (cp.get(WebSocketServerProtocolHandshakeHandler.class) == null) {
                // Add the WebSocketHandshakeHandler before this one.
                ctx.pipeline().addBefore(ctx.name(), WebSocketServerProtocolHandshakeHandler.class.getName(),
                        new WebSocketServerProtocolHandshakeHandler(websocketPath, subprotocols,
                                allowExtensions, maxFramePayloadLength, allowMaskMismatch, checkStartsWith));
            }
    
    
  4. 当客户端发送第一个http握手请求,请求升级为websocket协议。先是经过http协议处理器,将消息转为了FullHttpMessgae,之后在 握手处理的read方法中。

    //1. 根据ws协议版本号创建了一个指定版本的处理器。
    final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                        getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols,
                                allowExtensions, maxFramePayloadSize, allowMaskMismatch);
    final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
    
    //2. 指定版本的处理器 处理握手请求
    final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req);
    
    //3.握手处理器,替换为新的处理器, 这个处理器对非ws请求拒绝处理。
    ctx.pipeline().replace(this, "WS403Responder",
                       WebSocketServerProtocolHandler.forbiddenHttpRequestResponder());
    
    
  5. 4的第二步 将处理都交到指定ws协议WebSocketServerHandshaker手中,这个处理是先创建一个响应,告诉客户端服务升级。 之后将http相关的处理器都移除掉, 添加ws协议相关的处理器。

    // 1. 首先创建服务端确定升级ws的响应
    FullHttpResponse response = newHandshakeResponse(req, responseHeaders);
    
    //2.移除http协议处理器
    ChannelPipeline p = channel.pipeline();
    if (p.get(HttpObjectAggregator.class) != null) {
        p.remove(HttpObjectAggregator.class);
    }
    if (p.get(HttpContentCompressor.class) != null) {
        p.remove(HttpContentCompressor.class);
    }
    
    //3. 添加ws协议的编解码器 ,也是handler
    p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder());
    p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder());
    
    
  6. 此时客户端收到服务端响应,就升级为ws协议了,之后的用户自定义的处理器,就能处理了。文章来源地址https://www.toymoban.com/news/detail-809402.html

2. netty编程ws协议服务端
package eWebscoket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder;
import io.netty.handler.codec.http.websocketx.WebSocket08FrameEncoder;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * netty对websocket协议实现测试:
 * 1.  netty 提供了{@link WebSocketServerProtocolHandler} handler用于处理websocket协议。
 * handler中有ws的握手支持,ping、pong响应,close请求这些支持, 同时将Text  与 binary 数据传递
 * 给之后的handler进行业务处理。WebSocketServerProtocolHandler 通过WebSocketServerProtocolHandshakeHandler 首次read请求
 * 确定websocket协议版本(一般是13), 给channel绑定了不容版本协议下的WebSocket13FrameDecoder。
 *
 * <p>
 * 2.WebSocketServerProtocolHandler在有新的channel连接注册回调方法中{@link WebSocketServerProtocolHandler#handlerAdded(ChannelHandlerContext)},
 * 会在当前handler中添加一个新的handler {@link WebSocketServerProtocolHandshakeHandler}专门用于处理首次客户端请求的握手操作, 查看
 * {@link io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandshakeHandler#channelRead(ChannelHandlerContext, Object)}的接收客户端数据的处理,会为客户返回
 * 一个FullHttpResponse,响应头中,含有确认更新为websocket协议的响应。 当这个channel写会给客户端,此时这个channel的通信协议就有http转为websocket,这个握手用的handler也会
 * 自己移除了。
 * <p>
 * 3. 握手处理过程:
 * 就会将pipLine中用于握手的http的解析handler给移除掉了,
 * p.remove(HttpContentCompressor.class);
 * p.remove(HttpObjectAggregator.class);
 * 然后在 HttpServerCodec 的 handler 之前添加 websocket协议的handler:
 * p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder()); {@link WebSocket08FrameDecoder#decode(io.netty.channel.ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List)}
 * p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder()); {@link WebSocket08FrameEncoder#encode(io.netty.channel.ChannelHandlerContext, io.netty.handler.codec.http.websocketx.WebSocketFrame, java.util.List)}
 * 之后将response写会客户端成功后升级,将HttpServerCodec移除掉,删除代码在handshake()方法的写会回调中。
 * 在握手升级之后WebSocketServerProtocolHandshakeHandler 没有用了,就会将这个handler,替换成WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()
 * 对非ws的请求拒绝处理。
 *
 * @author mahao
 * @date 2022/10/18
 */
public class ServerWebSocket {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        //添加http消息的转换,将socket数据流 转换为 HttpRequest, websocket协议添加这个是为了
                        //握手时候使用。 HttpServerCodec 与 HttpObjectAggregator会在第一次http请求后,被移除掉,握手结束了
                        //协议升级就会只用websocket协议了。
                        pipeline.addLast(new HttpServerCodec());
                        //未知
                        pipeline.addLast(new ChunkedWriteHandler());
                        //将拆分的http消息聚合成一个消息。
                        pipeline.addLast(new HttpObjectAggregator(8096));

                        //用户websocket协议的 握手,ping pang处理, close处理,对于二进制或者文件数据,直接交付给下层
                        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

                        pipeline.addLast("myHandler", new WebSocketHandler());

                    }
                });
        ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();
        channelFuture.channel().closeFuture().sync();

        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();

    }
}
3. html 客户端
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webSock客户端</title>
</head>
<body>
<script type="text/javascript">
    var socket;
    if (window.WebSocket){
        socket= new WebSocket("ws://localhost:9999/ws");

        socket.onmessage = function (ev) {
            var ta = document.getElementById("responseText");
            ta.value = ta.value + "\n" + ev.data;
        }
        socket.onopen = function (ev) {
            var ta = document.getElementById("responseText");
            ta.value = "连接开启";
        }
        socket.onclose = function (ev) {
            var ta = document.getElementById("responseText");
            ta.value = ta.value + "\n" + "连接关闭";
        }
    } else {
        alert("浏览器不支持websocket");
    }

    function send(message) {
        alert(123);
        if (!window.WebSocket) {
            return;
        }
        if (socket.readyState == WebSocket.OPEN){
            alert(123);
            socket.send(message);
        }

    }

</script>

<form onsubmit="return false;">
    <textarea name="message" style="width: 400px;height: 200px"></textarea>
    <input type="button" value="发送消息" onclick="send(this.form.message.value);">

    <h3>服务器端输出</h3>
    <textarea id="responseText" style="width: 400px;height: 300px"></textarea>
    <input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空内容">

</form>
</body>
</html>

到了这里,关于netty对websocket协议的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java IO流(五)Netty实战[TCP|Http|心跳检测|Websocket]

    Server端  Client 运行结果 Server Client 运行结果  注意: 需要调整readerIdleTime|writerIdleTime|allIdleTime参数才会显示对应超时信息 服务端 客户端(浏览器) 效果图

    2024年02月11日
    浏览(32)
  • 【 基于Netty实现聊天室聊天业务学习】第4节.什么是BIO与NIO

    IO在读写的时候是阻塞的,无法做其他操作,并发处理能力的非常低,线程之间访问资源通信时候也是非常耗时久,依赖我们的网速,带宽。 我们看一下他的白话原理 我们来看一下这张图那么这张图的话它里面有一个server还有三个客户端那么客户端的话它可以有很多,那么我

    2024年04月26日
    浏览(49)
  • Netty 教程 – 实现WebSocket通讯

    WebSocket 协议是基于 TCP 的一种新的网络协议,它实现了浏览器与服务器 全双工(full-duplex)通信 ,允许 服务器主动发送信息给客户端 优点及作用 Http协议的弊端: Http协议为半双工协议。(半双工:同一时刻,数据只能在客户端和服务端一个方向上传输) Http协议冗长且繁琐 易

    2024年02月09日
    浏览(33)
  • 【netty基础四】netty与nio

    阻塞I/O在调用InputStream.read()方法时是 阻塞的,它会一直等到数据到来 (或超时)时才会返回; 同样,在调用ServerSocket.accept()方法时,也会一直 阻塞到有客户端连接 才会返回,每个客户端连接成功后,服务端都会启动一个线程去处理该客户端的请求。 阻塞I/O的通信模型示意

    2024年02月10日
    浏览(87)
  • SpringBoot+Netty+Websocket实现消息推送

    这样一个需求:把设备异常的状态每10秒推送到页面并且以弹窗弹出来,这个时候用Websocket最为合适,今天主要是后端代码展示。 添加依赖 定义netty端口号 netty服务器 Netty配置 管理全局Channel以及用户对应的channel(推送消息) 管道配置 自定义CustomChannelHandler 推送消息接口及

    2024年02月04日
    浏览(47)
  • SpringBoot整合Netty+Websocket实现消息推送

           Netty是一个高性能、异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。以下是Netty的主要优势: 高性能 :Netty基于NIO(非阻塞IO)模型,采用事件驱动的设计,具有高性能的特点。它通过零拷贝技术、内存池化技术等手段,进一步提高

    2024年01月20日
    浏览(43)
  • Springboot中使用netty 实现 WebSocket 服务

    依赖 创建启动类 创建WebSocket 服务 WsServerInitialzer 初始化 创建信息ChatHandler 处理类

    2024年02月14日
    浏览(37)
  • Websocket协议-http协议-tcp协议区别和相同点

    通讯形式 单工通讯-数据只能单向传送一方来发送数据,另一方来接收数据 半双工通讯-数据能双向传送但不能同时双向传送 全双工通讯-数据能够同时双向传送和接受 注:http的通讯方式是分版本 http1.0 :单工。因为是短连接,客户端发起请求之后,服务端处理完请求并收到

    2024年02月15日
    浏览(54)
  • WebSocket和HTTP协议有什么区别?&& 连环问:WebSocket和HTTP长轮询的区别?

    什么是WebSocket? 答:WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。 特点: 1. TCP连接,与HTTP协议兼容 2. 双向通信,主动推送(服务端向客户端) 3. 无同源限制,协议标识符是ws(加密wss) WebSocket: 1. 支持端对端通讯 2. 可以由client发起

    2024年02月11日
    浏览(41)
  • HTTP、WebSocket、STOMP、MQTT 协议

    TCP/IP 是用于因特网 (Internet) 的通信协议,是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信。 TCP/IP是基于TCP和IP这两个最初的协议之上的不同的通信协议的大集合,是一个协议族。 1-1、TCP(传输控制协议,Transmission Control Protocol) 在计算机网

    2024年04月15日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包