使用来自 Java 客户端的 Spring 4 通过 WebSockets 连接到 Stomp

Connecting to Stomp over WebSockets implemented using Spring 4 from Java client

我收到了一个 Web 应用程序,它是通过 Spring 使用 STOMP over WebSockets Messaging 实现的,类似于 here 所描述的(在后端使用 RabbitMQ)。它在 Tomcat 上运行,我可以使用常规 URL(例如 http://host/server/)连接到应用程序。我还获得了一个演示客户端——一个使用 Modernizr WebSockets 和 SockJS 的 JSPX 页面。演示客户端中的连接代码如下所示:

if (Modernizr.websockets) {
    socket = new WebSocket('ws://host/server/endpointx');
}
else {
    socket = new SockJS('/server/endpointy');
}

stompClient = Stomp.over(socket);
stompClient.connect(headers, function(frame) { ... });
....

演示客户端工作正常(当我在浏览器中加载 JSPX 页面时,我可以连接到服务器)。但我的目标是使用一些 Java STOMP 库连接到同一台服务器。问题是,我试过的库需要 hostport 作为连接参数:例如 ActiveMQ Stomp library:

StompConnection connection = new StompConnection();
connection.open("host", port);
connection.connect(new HashMap<String, String>());

如果我将 port 指定为 61613,连接成功,但我直接访问了 RabbitMQ,而不是我的服务器(这不是我想要的)。如果我指定 8080(或 80),在连接时我会得到一个错误

java.io.EOFException at java.io.DataInputStream.readByte(Unknown Source) at org.apache.activemq.transport.stomp.StompWireFormat.unmarshal(StompWireFormat.java:137) at org.apache.activemq.transport.stomp.StompConnection.receive(StompConnection.java:77) at org.apache.activemq.transport.stomp.StompConnection.receive(StompConnection.java:68) at org.apache.activemq.transport.stomp.StompConnection.connect(StompConnection.java:139) at ....stomp.impl.activemq.ActiveMQStompDriver.connect(ActiveMQStompDriver.java:39) ... 25 more

并且跟踪显示这是因为 CONNECT 帧从未接收到预期的 CONNECTED 帧(事实上,它没有收到任何返回信息)。

所以我很疑惑:我是不是用错端口了?或处理一些图书馆不兼容?还是我需要以某种方式表明 Tomcat 我想要 upgrade HTTP connection to WebSockets?

如果上述问题难以回答,那么这个问题同样有用:如何通过 Tomcat 上的 WebSockets 消息通道 运行 使用 STOMP 连接到 Spring 应用程序Java?

我应该在找到答案时发布答案。 为了实现 Java 客户端,连接到 Tomcat,而不是直接连接到 RabbitMQ,Java 客户端不仅应该实现 STOMP Over WebSockets,还应该 an opening handshake:

The opening handshake is intended to be compatible with HTTP-based server-side software and intermediaries, so that a single port can be used by both HTTP clients talking to that server and WebSocket clients talking to that server. To this end, the WebSocket client's handshake is an HTTP Upgrade request

我尝试使用的库只是缺少此功能。他们试图在没有握手的情况下直接执行WebSocket连接。

所以回答我自己的问题:

  • 库必须支持打开握手
  • 对于主机和端口,您需要指定 Tomcat 的主机(与 RabbitMQ 不同)和 web 端口(例如 8080),而不是 RabbitMQ 端口

我最终使用了 Spring WebSocket library,这让这一切变得非常容易。这是最简单的设置:

taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(N); // N = number of threads
taskScheduler.afterPropertiesSet();

stompClient = new WebSocketStompClient(new StandardWebSocketClient());
stompClient.setTaskScheduler(taskScheduler);
stompClient.setMessageConverter(new MappingJackson2MessageConverter()); 

WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders(); // you can add additional headers here for Tomcat

// Here's the main piece other libraries were missing:
// The following request will perform both, handshake and connection
// hence it gets both sets of headers (for Tomcat and for Stomp)
ListenableFuture<StompSession> future = stompClient.connect(
            url,
            handshakeHeaders,
            new StompHeaders(),
            new CustomStompSessionHandlerAdapter(this)); // called from class that also was implementing CustomStompSessionHandlerAdapter, but this could be separate class as well.

另见 WebSocket Support section in Spring reference