WebSocket API

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

WebSocketHandler

创建一个 WebSocket 服务器就像实现 WebSocketHandler 一样简单,或者更可能是扩展 TextWebSocketHandlerBinaryWebSocketHandler 中的一个。 以下示例使用了 TextWebSocketHandler

public class MyHandler extends TextWebSocketHandler {

  @Override
  protected void handleTextMessage(WebSocketSession session, TextMessage message) {
    // ...
  }
}

有专门的 WebSocket 编程配置支持,用于将前面的 WebSocket 处理程序映射到特定的 URL,如下例所示:

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {

  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(myHandler(), "/myHandler");
  }

  @Bean
  public WebSocketHandler myHandler() {
    return new MyHandler();
  }
}

前面的示例适用于 Web MVC 应用程序,并应包含在 DispatcherHandler 的配置中。 然而 WebSocket 支持不依赖于 Web MVC。通过 WebSocketHttpRequestHandler 的帮助,将 WebSocketHandler 集成到其他 HTTP 服务环境中相对简单。

一种选择是使用 ConcurrentWebSocketSessionDecorator 包装 WebSocketSession

WebSocket 握手

通过 HandshakeInterceptor 是定制初始 HTTP WebSocket 握手请求的最简单方法,它公开了 "before""after" 握手的方法。 您可以使用此类拦截器来阻止握手或使任何属性可用于 WebSocketSession。 以下示例使用内置拦截器将 HTTP 会话属性传递到 WebSocket 会话:

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {

  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(new MyHandler(), "/myHandler");
    //.addInterceptors(new WebSessionHandshakeInterceptor());
  }

}

更高级的选项是扩展 DefaultHandshakeHandler,它执行 WebSocket 握手的步骤, 包括验证客户端来源、协商子协议和其他细节。如果应用程序需要配置自定义的 RequestUpgradeStrategy 以适应尚不支持的 WebSocket 服务器引擎和版本(有关此主题的更多信息, 请参见 部署)时,也可能需要使用此选项。 Java 配置和 XML 命名空间都可以配置自定义的 HandshakeHandler

Infra 提供了一个 WebSocketHandlerDecorator 基类,您可以使用它来装饰 WebSocketHandler,以添加额外的行为。 在使用 WebSocket Java 配置或 XML 命名空间时,默认情况下会提供并添加日志记录和异常处理实现。 ExceptionWebSocketHandlerDecorator 捕获所有由任何 WebSocketHandler 方法引发的未捕获异常,并使用状态 1011 关闭 WebSocket 会话,这表示服务器错误。

部署

WebSocket API 很容易集成到 Web MVC 应用程序中,其中 DispatcherHandler 既提供 HTTP WebSocket 握手,也提供其他 HTTP 请求服务。 通过调用 WebSocketHttpRequestHandler,它也很容易集成到其他 HTTP 处理场景中。这非常方便且易于理解。然而,在涉及到 JSR-356 运行时时,需要特别考虑。

Allowed Origins

The default behavior for WebSocket and SockJS is to accept only same-origin requests. It is also possible to allow all or a specified list of origins. This check is mostly designed for browser clients. Nothing prevents other types of clients from modifying the Origin header value (see RFC 6454: The Web Origin Concept for more details).

The three possible behaviors are:

  • Allow only same-origin requests (default): In this mode, when SockJS is enabled, the Iframe HTTP response header X-Frame-Options is set to SAMEORIGIN, and JSONP transport is disabled, since it does not allow checking the origin of a request. As a consequence, IE6 and IE7 are not supported when this mode is enabled.

  • Allow a specified list of origins: Each allowed origin must start with http:// or https://. In this mode, when SockJS is enabled, IFrame transport is disabled. As a consequence, IE6 through IE9 are not supported when this mode is enabled.

  • Allow all origins: To enable this mode, you should provide * as the allowed origin value. In this mode, all transports are available.

You can configure WebSocket and SockJS allowed origins, as the following example shows:

WebSocket 的默认行为是仅接受同源请求。 也可以允许所有或指定的一组来源。 此检查主要设计用于浏览器客户端。 没有任何阻止其他类型的客户端修改 Origin 标头值(有关详细信息,请参见 RFC 6454: Web Origin 概念)。

三种可能的行为是:

  • 仅允许同源请求(默认):在此模式下,当启用 SockJS 时,Iframe HTTP 响应头 X-Frame-Options 被设置为 SAMEORIGIN, 并且禁用了 JSONP 传输,因为它不允许检查请求的来源。因此,当启用此模式时,不支持 IE6 和 IE7。

  • 允许指定的一组来源:每个允许的来源必须以 http://https:// 开头。

  • 允许所有来源:要启用此模式,应将 * 作为允许的来源值提供。在此模式下,所有传输都可用。

您可以配置 WebSocket 和 SockJS 允许的来源,如下例所示:

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {

  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(myHandler(), "/myHandler")
            .setAllowedOrigins("https://mydomain.com");
  }

  @Bean
  public WebSocketHandler myHandler() {
    return new MyHandler();
  }
}