SpringBoot如何集成WebSocket

在本文中将讨论 WebSockets 的一些工作原理,以及如何在 Spring Boot 应用程序集成 WebSockets API。

在不断发展的网络开发环境中,用户的期望已转向更具交互性和实时性的体验。传统的网络应用主要依赖于请求-响应模型,即客户端(通常是网络浏览器)向服务器发送 HTTP 请求,然后服务器响应所请求的数据。然而,这种方法在实现真正的实时通信和动态更新方面存在局限性,无法实现持续轮询。

这就是 WebSockets 发挥作用的地方。WebSockets 是一种通信协议,通过客户端和服务器之间的单个长期连接提供全双工双向通信通道。与客户端发起每次通信的传统请求-响应模式不同,WebSockets 允许客户端和服务器独立发起消息。这使得 WebSockets 成为构建需要实时更新、实时通知、在线游戏、协作工具、金融平台等应用的理想选择。

WebSockets 协议概述

WebSockets 以标准的 HTTP 请求和响应开始。在请求-响应链中,客户端请求打开一个 WebSocket 连接,服务器做出响应(如果可以的话)。如果初始握手成功,客户端和服务器就同意将为 HTTP 请求建立的现有 TCP/IP 连接用作 WebSocket 连接。现在,数据可以使用基本的框架信息协议在此连接上流动。一旦双方确认 WebSocket 连接应该关闭,TCP 连接就会被切断。

建立 WebSocket 连接

WebSockets 不使用 http:// 或 https:// 方案(因为它们不遵循 HTTP 协议)。相反,WebSocket URI 使用新方案 ws: (或 wss: 用于安全 WebSocket)。URI 的其余部分与 HTTP URI 相同:主机、端口、路径和任何查询参数。

"ws:" "//" host [ ":" port ] path [ "?" query ]
"wss:" "//" host [ ":" port ] path [ "?" query ]

WebSocket 连接只能与遵循此方案的 URI 建立。也就是说,如果你看到的 URI 的方案是 ws://(或 wss://),那么客户端和服务器都必须遵循 WebSocket 连接协议,以符合 WebSocket 规范。

WebSocket 连接是通过升级 HTTP 请求/响应对建立的。支持 WebSockets 并希望建立连接的客户端会发送一个 HTTP 请求,其中包括一些必要的标头:

  • Connection: Upgrade
    Connection 标头通常控制当前事务结束后网络连接是否保持打开状态。该标头的常用值是 keep-alive,以确保连接的持久性,从而允许向同一服务器发出后续请求。在 WebSocket 开启握手过程中,我们将连接头设置为 Upgrade,表明我们希望保持连接有效,并将其用于非HTTP 请求。
  • Upgrade: websocket
    客户端使用Upgrade标头要求服务器按优先顺序降序切换到列出的协议之一。在这里指定websocket以表明客户端想要建立 WebSocket 连接。
  • Sec-WebSocket-Key: q4xkcO32u266gldTuKaSOw==
    Sec-WebSocket-Key
    是客户端生成的一次性随机值(随机数)。该值是一个经过 base64 编码的 16 字节随机值。
  • Sec-WebSocket-Version: 13
    WebSocket 协议唯一接受的版本是 13。此标头中列出的任何其他版本均无效。

这些标头组合在一起,会导致客户端向 ws:// URI 发送 HTTP GET 请求,如下例所示:

GET ws://example.com:8181/ HTTP/1.1
Host: localhost:8181
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: q4xkcO32u266gldTuKaSOw==

客户端发送初始请求打开 WebSocket 连接后,就会等待服务器的回复。回复必须有 HTTP 101 Switching Protocols切换响应代码。HTTP 101 Switching Protocols切换响应表示服务器正在切换到客户端在升级请求标头中请求的协议。此外,服务器还必须包含验证连接已成功升级的 HTTP 标头:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fA9dggdnMPU79lJgAE3W4TRnyDM=
  • Connection: Upgrade
    确认连接已升级。
  • Upgrade: websocket
    确认连接已升级。
  • Sec-WebSocket-Accept: fA9dggdnMPU79lJgAE3W4TRnyDM=`
    Sec-WebSocket-Accept经 base64 编码的 SHA-1 散列值。您可以通过连接客户端 Sec-WebSocket-Key nonce 和 RFC 6455 中定义的静态值 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 来生成此值。虽然 Sec-WebSocket-Key 和 Sec WebSocket-Accept` 看起来很复杂,但它们的存在是为了让客户端和服务器都能知道对方支持 WebSockets。由于 WebSocket 重用了 HTTP 连接,如果任何一方将 WebSocket 数据理解为 HTTP 请求,就会产生潜在的安全问题。

客户端收到服务器响应后,WebSocket 连接就会打开,开始传输数据。

在 Spring boot 应用程序中集成 WebSocket API

Spring Framework 提供了 WebSocket API,您可以用它来编写处理 WebSocket 消息的客户端和服务器端应用程序。

步骤 1:

Spring initializr 生成一个项目文件,并添加 spring-websocket 依赖关系:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

步骤 2

通过实现 WebSocketHandler 接口创建一个 WebSocket 处理程序类。

在示例项目中,我创建了一个 Gamehandler,其中包含处理 WebSocket 请求所需的所有重载方法。

在下面的类中,方法名称说明了该方法要做的事情,为了保持逻辑简单,我只为某些方法添加了一行日志。

当使用 WebSocketHandler(GameHandler)的 WebSocket 连接收到消息时,就会执行 handleMessage 方法。

为了简单起见,我在该方法中做了一些基本操作,基本上是从请求中获取 gameId,然后使用 session.send 方法在间隔 1 秒后回复 2 条消息,该方法将在接收请求的同一会话中发送消息。

根据您的业务逻辑,我们可以在这里做很多事情,例如获取 JSON 格式的请求,然后将有效载荷解析为所需对象,再以 JSON 格式回复等。

package com.kishore.poc.config.handler;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GameHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("Connection established on session: {}", session.getId());
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception 
        String gameId = (String) message.getPayload();
        session.sendMessage(new TextMessage("Started processing game: " + gameId));
        Thread.sleep(1000);
        session.sendMessage(new TextMessage("Completed processing game: " + gameId));
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.info("Exception occured: {} on session: {}", exception.getMessage(), session.getId());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        log.info("Connection closed on session: {} with status: {}", session.getId(), closeStatus.getCode());
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

步骤 3:

现在,我们需要将 GameHandler 类指向一个 API,为此,我们需要向 WebSocketHandlerRegistry 注册 WebSocketHandler(GameHandler),注册工作在下面的配置类中完成。

package com.kishore.poc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import com.kishore.poc.config.handler.GameHandler;


@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(gameHandler(), "/game")
                        .setAllowedOrigins("*");
    }

    @Bean
    public WebSocketHandler gameHandler() {
        return new GameHandler();
    }
}

在上述代码块中,我在 GameHandler 上创建了一个 Bean,并将其注册到 WebSocketHandlerRegistry 指定的 URL 路径(即 /game)。

这样,当客户端尝试连接到 /game 时,所有请求都将重定向到 GameHandler。

同样,我们也可以根据业务需要,通过在 WebSocketHandlerRegistry 注册添加和配置任意数量的 WebSocketHandler。

步骤 4:

测试刚刚创建的 WebSocket API,为了测试,我们可以使用互联网上的各种工具和扩展。我将使用 Postman 作为 WebSocket 客户端:

SpringBoot如何集成WebSocket

在上面的屏幕截图中,可以看到 Postman 能够使用 ws: URI 方案而不是用于 Rest API 连接的 http: 来连接 /game 端点,通过添加 ws: 说它将使用 WebSocket 协议与服务器连接。

之后,当发送消息时,它会根据我们添加到 GameHandler 类的逻辑处理该消息并回复 2 次。

SpringBoot如何集成WebSocket

结论

在对 WebSockets 以及如何在 Spring Boot 中使用 WebSockets 进行总结时,我们探索了一种使网站更具交互性的强大方法。WebSockets 让我们可以在用户和网站之间快速交换信息,从而带来即时更新和引人入胜的体验。虽然 WebSockets 有很多优点,如快速通信和双向聊天,但我们也需要应对一些挑战,如管理资源和确保旧系统仍能使用 WebSockets。有了创建 Spring Boot WebSocket 的实践指南,我们就打开了一扇大门,可以制作出令人兴奋的网站,将人们实时联系起来。当我们结束旅程时,请记住 WebSockets 就像一个特殊的工具,可以帮助我们构建更加动态和生动的在线空间。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论