SpringBoot整合WebSocket详解

环境:Springboot3.0.5
WebSocket介绍WebSocket协议RFC 6455提供了一种标准化的方式,通过一个TCP连接在客户端和服务器之间建立全双工、双向的通信通道 。它是一个不同于HTTP的TCP协议,但设计为在HTTP之上工作,使用80和443端口,并允许重用现有的防火墙规则 。
WebSocket交互开始于一个HTTP请求,使用HTTP Upgrade header进行升级,在本例中是切换到WebSocket协议 。下面的例子展示了这种交互:
GET /spring-websocket-portfolio/portfolio HTTP/1.1Host: localhost:8080Upgrade: websocket// ①Connection: Upgrade// ②Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==Sec-WebSocket-Protocol: v10.stomp, v11.stompSec-WebSocket-Version: 13Origin: http://localhost:8080①:Upgrade header头部信息
②:使用 Upgrade 连接
支持WebSocket的服务器会返回类似下面的输出,而不是通常的200状态码:
HTTP/1.1 101 Switching Protocols Upgrade: websocketConnection: UpgradeSec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=Sec-WebSocket-Protocol: v10.stomp握手成功后,HTTP upgrade请求的TCP套接字保持打开,客户端和服务器可以继续发送和接收消息 。
如果WebSocket服务器运行在web服务器(例如Nginx)后面,你可能需要配置它来将WebSocket升级请求传递给WebSocket服务器 。同样,如果应用程序运行在云环境中,请查看云提供商提供的有关WebSocket支持的说明 。
HTTP与WebSocket尽管WebSocket在设计上是与HTTP兼容的,而且从HTTP请求开始,但重要的是要明白,这两种协议导致了非常不同的架构和应用程序编程模型 。
在HTTP和REST中,应用程序被建模为多个url 。为了与应用程序交互,客户端以请求-响应的方式访问这些url 。服务器根据HTTP URL、方法和首部将请求路由到适当的处理程序 。
相比之下,在websocket中,初始连接通常只有一个URL 。随后,所有应用程序消息都在同一个TCP连接上流动 。这是一种完全不同的异步、事件驱动的消息传递架构 。
WebSocket也是一种底层传输协议,与HTTP不同,它对消息内容没有任何语义规定 。这意味着除非客户端和服务器在消息语义上达成一致,否则无法路由或处理消息 。
WebSocket客户端和服务器可以通过HTTP握手请求的Sec-WebSocket-Protocol头部来协商使用更高级别的消息传递协议(例如STOMP) 。在这种情况下,他们需要制定自己的惯例 。
什么时候该使用WebSocketWebSockets可以使网页具有动态性和交互性 。然而,在许多情况下,Ajax和HTTP流或长轮询的组合可以提供简单而有效的解决方案 。
例如,新闻、邮件和社交源需要动态更新,但每隔几分钟更新一次完全没问题 。另一方面,协作、游戏和金融应用需要更接近实时 。
延迟本身并不是决定性因素 。如果消息量相对较少(例如监视网络故障),HTTP流或轮询可以提供有效的解决方案 。低延迟、高频率和高容量的组合才是WebSocket的最佳选择 。
还要记住,在互联网上,你无法控制的限制性代理可能会阻止WebSocket交互,要么是因为它们没有配置为传递Upgrade header,要么是因为它们关闭了看起来空闲的长连接 。这意味着对防火墙内的内部应用程序使用WebSocket比面向公众的应用程序更直接 。
WebSocket核心APISpring框架提供了一个WebSocket API,可以用它来编写处理WebSocket消息的客户端和服务器端应用程序 。

  • WebSocketHandler
创建WebSocket服务器很简单,只需实现WebSocketHandler,或者扩展TextWebSocketHandler或BinaryWebSocketHandler 。下面的例子使用了TextWebSocketHandler:
public class MessageHandler extends TextWebSocketHandler {@Overridepublic void handleTextMessage(WebSocketSession session, TextMessage message) {System.out.printf("SessionId: %s, 接收到消息: %s%n", session.getId(), message.getPayload()) ;try {session.sendMessage(new TextMessage("服务端接收到消息 - " + message.getPayload())) ;} catch (IOException e) {e.printStackTrace();}}@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {System.out.printf("连接成功, 会话Id: %s, Attribute: %s%n", session.getId(), session.getAttributes()) ;}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {System.out.printf("连接关闭, 会话Id: %s, 关闭状态: %s%n", session.getId(), status.getCode() + " - " + status.getReason()) ;}}WebSocket配置
@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(messageHandler(), "/message")}@Beanpublic WebSocketHandler messageHandler() {return new MessageHandler();}}


推荐阅读