Подключение к Stomp через WebSockets, реализованное с использованием Spring 4 из Java-клиента

Я получил веб-приложение, реализованное в Spring с использованием STOMP через WebSockets Messaging, подобное тому, что описано здесь (с RabbitMQ на сервере). Он работает на Tomcat, и я могу подключиться к приложению, используя обычные URL-адреса (например, http://host/server/). Мне также дали демо-клиент - страницу JSPX, которая использует Modernizr WebSockets и SockJS. Код подключения в демонстрационном клиенте выглядит так:

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. Проблема в том, что библиотеки, которые я пробовал, требуют в качестве параметров подключения хост и порт: например, с Библиотека ActiveMQ Stomp:

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

Если я укажу port как 61613, соединение будет успешным, но я попаду в RabbitMQ напрямую, а не на свой сервер (это не то, что я хочу). Если указать 8080 (или 80), при подключении выдает ошибку

java.io.EOFException в java.io.DataInputStream.readByte (Неизвестный источник) в org.apache.activemq.transport.stomp.StompWireFormat.unmarshal (StompWireFormat.java:137) в org.apache.activemq.transport.stompnection. .receive (StompConnection.java:77) в org.apache.activemq.transport.stomp.StompConnection.receive (StompConnection.java:68) в org.apache.activemq.transport.stomp.StompConnection.connect (StompConnection.java: ) at .... stomp.impl.activemq.ActiveMQStompDriver.connect (ActiveMQStompDriver.java:39) ... еще 25

и трассировка показывает, что это происходит потому, что CONNECT кадр никогда не получает ожидаемого CONNECTED кадра (фактически, он ничего не получает обратно).

Поэтому я озадачен: я использую не тот порт? или иметь дело с несовместимостью библиотек? или мне нужно каким-то образом указать Tomcat, что я хочу обновить HTTP-соединение до WebSockets?

Если на вышеприведенный вопрос трудно ответить, то этот одинаково полезен: как мне подключиться к приложению Spring с помощью STOMP через канал обмена сообщениями WebSockets, запущенный на Tomcat с использованием Java?


person Kiril S.    schedule 08.08.2016    source источник


Ответы (1)


Я должен был опубликовать ответ, когда нашел его. Чтобы реализовать клиент Java, подключающийся к Tomcat, а не напрямую к RabbitMQ, клиент Java должен не только реализовать STOMP Over WebSockets, но и вступительное рукопожатие:

Открывающее рукопожатие предназначено для обеспечения совместимости с серверным программным обеспечением и посредниками на основе HTTP, так что один порт может использоваться как HTTP-клиентами, обращающимися к этому серверу, так и клиентами WebSocket, общающимися с этим сервером. С этой целью рукопожатие клиента WebSocket представляет собой HTTP-запрос на обновление.

В библиотеках, которые я пытался использовать, эта возможность просто отсутствовала. Они попытались напрямую установить соединение WebSocket без подтверждения.

Итак, чтобы ответить на мои собственные вопросы:

  • Библиотека должна поддерживать открывающее рукопожатие
  • Для хоста и порта вам необходимо указать хост Tomcat (не такой, как RabbitMQ) и веб-порт (например, 8080), а не порт RabbitMQ.

В итоге я использовал библиотеку Spring WebSocket, которая позволяет все это довольно легко. Вот простейшая установка:

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 в справочнике Spring

person Kiril S.    schedule 22.10.2019