1. 引言

HTTP(超文本传输协议)是一种无状态的请求-响应协议。其简单设计使其具备良好的可扩展性,但对于高度交互的实时Web应用来说却存在明显缺陷——每个请求/响应都需要传输大量开销数据,导致效率低下。

由于HTTP是同步协议,而实时应用需要异步通信,轮询或长轮询(如Comet技术)等解决方案往往复杂且低效。

为解决上述问题,我们需要一个基于标准的双向全双工协议,能同时被服务器和客户端使用。JSR 356 API应运而生——本文将展示其实际应用示例。

2. 环境配置

在项目中添加Spring WebSocket依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>6.0.13</version>
 </dependency>
 <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>6.0.13</version>
 </dependency>

⚠️ 始终从Maven中央仓库获取最新版本:

3. STOMP协议

STOMP(流文本定向消息协议)是一种简单、可互操作的线格式协议,允许客户端与几乎所有消息代理通信。它是AMQP(高级消息队列协议)和JMS(Java消息服务)的替代方案。

STOMP定义了客户端/服务器使用消息语义进行通信的协议,这些语义构建在WebSocket之上,定义了映射到WebSocket帧的帧格式。

使用STOMP可以灵活地用不同编程语言开发客户端和服务器。本示例将使用STOMP实现客户端与服务器间的消息传递。

4. WebSocket服务器

关于WebSocket服务器的构建细节,可参考这篇文章

5. WebSocket客户端

要与WebSocket服务器通信,客户端需通过发送HTTP请求发起连接,请求中需正确设置Upgrade头部:

GET ws://websocket.example.com/ HTTP/1.1
Origin: http://example.com
Connection: Upgrade
Host: websocket.example.com
Upgrade: websocket

✅ 注意:

  • WebSocket URL使用wswss协议(后者表示安全WebSocket)
  • 服务器若支持WebSocket,会在响应中返回Upgrade头部:
HTTP/1.1 101 WebSocket Protocol Handshake
Date: Wed, 16 Oct 2013 10:07:34 GMT
Connection: Upgrade
Upgrade: WebSocket

完成此过程(即WebSocket握手)后,初始HTTP连接被同一TCP/IP连接上的WebSocket连接取代,此后双方即可自由交换数据。

客户端连接通过WebSocketStompClient实例发起。

5.1 WebSocketStompClient详解

如第3节所述,首先需建立WebSocket连接,这通过WebSocketClient类实现。WebSocketClient可通过以下方式配置:

  • JSR-356实现(如Tyrus)提供的StandardWebSocketClient
  • Jetty 9+原生WebSocket API提供的JettyWebSocketClient
  • Spring的任何WebSocketClient实现

本示例使用StandardWebSocketClient

WebSocketClient client = new StandardWebSocketClient();

WebSocketStompClient stompClient = new WebSocketStompClient(client);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());

StompSessionHandler sessionHandler = new MyStompSessionHandler();
stompClient.connect(URL, sessionHandler);

new Scanner(System.in).nextLine(); // 防止程序立即退出

❌ 踩坑提醒:

  • 默认情况下WebSocketStompClient使用SimpleMessageConverter
  • 处理JSON消息时,必须设置为MappingJackson2MessageConverter才能实现JSON与对象的转换

连接接口时需传入StompSessionHandler实例,用于处理afterConnectedhandleFrame等事件。

若服务器支持SockJS,可将客户端改为使用SockJsClient替代StandardWebSocketClient

5.2 StompSessionHandler实现

使用StompSession订阅WebSocket主题需创建StompSessionHandlerAdapter实例(实现StompSessionHandler接口)。

StompSessionHandler提供STOMP会话的生命周期事件:

  • 会话建立时的回调
  • 失败时的通知机制

当WebSocket客户端连接到接口后,StompSessionHandler会收到通知并调用afterConnected()方法,在此方法中通过StompSession订阅主题:

@Override
public void afterConnected(
  StompSession session, StompHeaders connectedHeaders) {
    session.subscribe("/topic/messages", this);
    session.send("/app/chat", getSampleMessage());
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
    Message msg = (Message) payload;
    logger.info("Received : " + msg.getText()+ " from : " + msg.getFrom());
}

✅ 运行效果: 确保WebSocket服务器运行后启动客户端,控制台将输出:

INFO o.b.w.client.MyStompSessionHandler - New session established : 53b993eb-7ad6-4470-dd80-c4cfdab7f2ba
INFO o.b.w.client.MyStompSessionHandler - Subscribed to /topic/messages
INFO o.b.w.client.MyStompSessionHandler - Message sent to websocket server
INFO o.b.w.client.MyStompSessionHandler - Received : Howdy!! from : Nicky

6. 总结

本教程简单粗暴地实现了基于Spring的WebSocket客户端。完整代码可在GitHub仓库获取。


原始标题:A Java Client for a WebSockets API