Клиент веб-сокета Tyrus @OnMessage никогда не вызывался - проект Storj с открытым исходным кодом

Я работаю над проектом с открытым исходным кодом, Storj. Я пишу клиент Java, который подключается к серверной части веб-сокета Node.js. Клиент использует Tyrus. Общение должно проходить следующим образом:

  • Соединять
  • Клиент отправляет токен авторизации (текст).
  • Сервер отправляет файл обратно (двоичный).
  • Сервер закрывает соединение.

У меня проблемы, так как мой @OnMessage никогда не вызывается. Я пробовал с помощью простого javascript-клиента в Интернете по тому же URL-адресу и с тем же токеном: https://www.websocket.org/echo.html

Я получаю ответ, используя это, который говорит мне, что что-то не так с проектом Java.

Прежде чем я смогу загрузить файл, на более раннем этапе я могу загрузить файл без каких-либо проблем. Однако этот шаг не требует вызова @OnMessage (он просто загружает файл, а затем сервер отключается с сообщением), поэтому я не уверен, что мой @OnMessage когда-либо работает.

Вот соответствующий код для Websocket (также доступен на Github): https://github.com/NutterzUK/storj-java-bridge-client/blob/master/storj-client/src/main/java/storj/io/client/websockets/WebsocketFileRetriever.java

package storj.io.client.websockets;

import com.google.gson.Gson;
import storj.io.restclient.model.FilePointer;

import javax.websocket.*;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

import java.util.concurrent.CountDownLatch;
import java.util.logging.Logger;

/**
 * Created by steve on 12/07/2016.
 */
@ClientEndpoint
public class WebsocketFileRetriever {

    private Logger logger = Logger.getLogger(this.getClass().getName());
    private Gson gson = new Gson();
    private FilePointer filePointer;
    private File outputFile;
    private AuthorizationModel authModel;
    private CountDownLatch latch;

    public WebsocketFileRetriever(FilePointer filePointer, File outputFile, CountDownLatch latch){
        this.filePointer = filePointer;
        this.latch = latch;
        this.outputFile = outputFile;
        authModel = new AuthorizationModel();
        authModel.setToken(filePointer.getToken());
        authModel.setOperation(filePointer.getOperation());
        authModel.setHash(filePointer.getHash());
    }

    @OnMessage
    public void onMessage(String s){
        logger.info("Received ... " + s);
    }

    @OnMessage
    public void onMessage(ByteBuffer message, Session session) {
        logger.info("Received ...." + message);
    }

    @OnOpen
    public void onOpen(Session session, EndpointConfig endpointConfig) {
        logger.info("Opened");
        try {
            session.getBasicRemote().sendText(gson.toJson(authModel), true);
        } catch (IOException e) {
            e.printStackTrace();
        }

        logger.info("sent: " + gson.toJson(authModel));
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info("Closed Websocket: " + closeReason.getCloseCode() + " " + closeReason.getReasonPhrase());
        //latch.countDown();
    }

    @OnError
    public void onError(Session session, Throwable t) {
        t.printStackTrace();
    }
}

И код, запускающий этот веб-сокет, доступен здесь https://github.com/NutterzUK/storj-java-bridge-client/blob/master/storj-client/src/main/java/storj/io/client/DefaultStorjClient.java :

        CountDownLatch latch;
        latch = new CountDownLatch(1);
        ClientManager wsClient = ClientManager.createClient();
        try {
            wsClient.setDefaultMaxBinaryMessageBufferSize(Integer.MAX_VALUE);
            wsClient.setDefaultMaxTextMessageBufferSize(Integer.MAX_VALUE);
            logger.info("CONNECTING TO: " + "ws://" + pointer.getFarmer().getAddress() + ":" + pointer.getFarmer().getPort());
            final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();

            wsClient.connectToServer(new WebsocketFileRetriever(pointer, encryptedOutputFile, latch), cec, new URI("ws://" + pointer.getFarmer().getAddress() + ":" + pointer.getFarmer().getPort()));
            latch.await();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

Я также попытался обновить Tyrus до последней версии и получил тот же результат. Любые идеи?

Вывод этого кода:

    Aug 25, 2016 8:55:31 PM storj.io.client.DefaultStorjClient downloadFile
INFO: CONNECTING TO: ws://164.storj.eu:8607
Aug 25, 2016 8:55:35 PM storj.io.client.websockets.WebsocketFileRetriever onOpen
INFO: Opened
Aug 25, 2016 8:55:35 PM storj.io.client.websockets.WebsocketFileRetriever onOpen
INFO: sent: {"token":"06c36d4bac4f07ee1751068b5b2230f22e884b38","hash":"837b79bec927a1d8fa7fedd2ea0bb276e0d86e0f","operation":"PULL"}
Aug 25, 2016 8:56:11 PM storj.io.client.websockets.WebsocketFileRetriever onClose
INFO: Closed Websocket: NORMAL_CLOSURE Closing

После отправки сообщения оно зависает на некоторое время перед сообщением «NORMAL_CLOSURE» из @OnClose.

Обновление: очень простой способ запустить это, чтобы воспроизвести проблему

Я добавил тестовое имя пользователя и пароль в репозиторий git, поэтому доступный код находится здесь: https://github.com/NutterzUK/storj-java-bridge-client

Для его запуска нужно просто запустить storj.io.client.main.MainTest

Краткий обзор того, что он делает. Сначала он отправит несколько HTTP-запросов для получения токена. Он будет использовать этот токен для подключения к чьей-либо машине через веб-сокет и отправит этот токен в виде текста. В ответ он должен получить файл в виде байтов.

Перед подключением он распечатывает токен и адрес, к которому он собирается подключиться. Он немного зависнет, прежде чем будет закрыт, и метод onMessage никогда не будет вызываться. Для тестирования, если вы поместите туда System.exit (раскомментируйте строку 152 в DefaultStorjClient.java), он не будет подключаться, поэтому вы можете использовать этот токен в другом клиенте. Я протестировал использование https://www.websocket.org/echo.html (убедитесь, что ваш браузер будет разрешать небезопасные URL-адреса, поскольку это не «wss», чтобы сделать это в Chrome, вам нужно щелкнуть значок в правом верхнем углу. Я вижу, что сервер действительно отвечает: Изображение, показывающее получение большого двоичного объекта

Это показывает, что большой двоичный объект действительно отправляется в ответ на текстовое сообщение, но @OnMessage в Tyrus никогда не срабатывает.


person ThePerson    schedule 25.08.2016    source источник
comment
Сервер JS отправляет это как большой двоичный объект, может ли это быть связано/или быть проблемой?   -  person ThePerson    schedule 26.08.2016
comment
Было бы неплохо иметь что-то проверяемое. Я имею в виду... Я подключился к сервису, но не могу заставить его отправить мне файл, поэтому я не могу проверить, может ли Тайрус получить сообщение с вашего сервера. В любом случае, Tyrus также работает с эхо-сервером, так что это не очень полезный тест. (просто укажите ваш клиент Tyrus на ws://echo.websocket.org и отправьте сообщение).   -  person Pavel Bucek    schedule 27.08.2016
comment
Спасибо. Да, это работает с эхо-сервером, но я не думаю, что эхо-сервер отправляет обратно байты. Я думаю, если вы создадите учетную запись на storj.io (это бесплатно), вы можете запустить testmain и отправить файл для проверки. Все это есть на github, к сожалению, я действительно озадачен тем, почему это происходит, поэтому я собираюсь добавить награду к этому вопросу через 7 часов, когда смогу.   -  person ThePerson    schedule 27.08.2016
comment
На самом деле я могу создать тестовую учетную запись, я думаю, и могу указать здесь имя пользователя/пароль, чтобы никто другой не делал этого. Я сделаю это и добавлю к вопросу, а также обновлю TestMain, чтобы просто загрузить один файл, чтобы выделить проблему.   -  person ThePerson    schedule 27.08.2016
comment
Я добавил обновление: действительно простой способ запустить это, чтобы увидеть проблему в описании. Это должно быть очень легко воспроизвести, я также дал вывод от клиента эхо-теста, который может вызывать тот же сервер веб-сокетов и возвращает большой двоичный объект. Я добавлю награду, как только SO позволит мне, через 6 часов.   -  person ThePerson    schedule 27.08.2016


Ответы (1)


В итоге я перешел на TallNate, и этой проблемы не существует.

После определения времени я обнаружил, что он всегда отключал меня через 30 секунд. Обычно ответы приходят быстрее, чем через 30 секунд, поэтому я не уверен, почему он зависал, а затем отключался. Я пытался установить тайм-аут в Тайрусе, но он все равно отключился на отметке 30 с. В конце концов я попробовал TallNate, чтобы посмотреть, могу ли я установить тайм-аут там... и из коробки это просто сработало.

https://github.com/TooTallNate/Java-WebSocket/wiki/Drafts

person ThePerson    schedule 28.08.2016
comment
Вы нашли настоящую причину? Возможно, другой клиент просто отправляет периодические эхо-запросы (что также поддерживается в Tyrus, см. tyrus.java.net/apidocs/1.13/org/glassfish/tyrus/core/ - person Pavel Bucek; 03.09.2016