Как проверить, живо ли соединение Websocket

У меня есть подключение к серверу через веб-сокет:

import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

@ClientEndpoint
public class WebsocketExample {

    private Session userSession;

    private void connect() {

        try {
            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            container.connectToServer(this, new URI("someaddress"));
        } catch (DeploymentException | URISyntaxException | IOException e) {
            e.printStackTrace();
        }
    }

    @OnOpen
    public void onOpen(Session userSession) {
        // Set the user session
        this.userSession = userSession;
        System.out.println("Open");
    }

    @OnClose
    public void onClose(Session userSession, CloseReason reason) {
        this.userSession = null;
        System.out.println("Close");
    }

    @OnMessage
    public void onMessage(String message) {
        // Do something with the message
        System.out.println(message);
    }
}

Через некоторое время кажется, что я больше не получаю сообщений от сервера, но метод onClose не был вызван.

Я хотел бы иметь своего рода таймер, который, по крайней мере, регистрировал бы ошибку (и в лучшем случае пытался бы повторно подключиться), если бы я, например, не получал никакого сообщения в течение последних пяти минут. Таймер сбрасывается, когда я получаю новое сообщение.

Как я могу это сделать?

Почему ты не играешь в пинг / понг? tools.ietf.org/html/rfc6455#section-5.5.2NOTE: A Ping frame may serve either as a keepalive or as a means to verify that the remote endpoint is still responsive.

Tschallacka 01.05.2018 15:25

Есть ли стандартный способ сделать это?

Ben 01.05.2018 15:25

читайте спецификацию. Вот как это должно работать. Посмотрите, как это реализовано в вашем фреймворке websocket, или напишите свой собственный. Поскольку вы не указали, что вы используете для своего кода веб-сокета, какой фреймворк / библиотеку и т. д. Я не могу использовать Google для вас, если он является частью этой структуры.

Tschallacka 01.05.2018 15:26
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
3
9 970
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот что я сделал. Я изменил javax.websocket на причал и реализовал вызов ping:

import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;

import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@WebSocket
public class WebsocketExample {

    private Session userSession;
    private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

    private void connect() {
        try {
            SslContextFactory sslContextFactory = new SslContextFactory();
            WebSocketClient client = new WebSocketClient(sslContextFactory);
            client.start();
            client.connect(this, new URI("Someaddress"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @OnWebSocketConnect
    public void onOpen(Session userSession) {
        // Set the user session
        this.userSession = userSession;
        System.out.println("Open");

        executorService.scheduleAtFixedRate(() -> {
                    try {
                        String data = "Ping";
                        ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
                        userSession.getRemote().sendPing(payload);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                },
                5, 5, TimeUnit.MINUTES);
    }

    @OnWebSocketClose
    public void onClose(int code, String reason) {
        this.userSession = null;
        System.out.println("Close");
    }

    @OnWebSocketMessage
    public void onMessage(String message) {
        // Do something with the message
        System.out.println(message);
    }
}

Редактировать: Это просто пример пинга ... Я не знаю, все ли серверы должны отвечать понгом ...

Edit2: Вот как поступить с сообщением pong. Уловка заключалась не в том, чтобы прослушивать сообщения String, а в сообщениях Frame:

@OnWebSocketFrame
@SuppressWarnings("unused")
public void onFrame(Frame pong) {
    if (pong instanceof PongFrame) {
        lastPong = Instant.now();
    }
}

Чтобы управлять тайм-аутом сервера, я изменил запланированную задачу следующим образом:

scheduledFutures.add(executorService.scheduleAtFixedRate(() -> {
                    try {
                        String data = "Ping";
                        ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
                        userSession.getRemote().sendPing(payload);

                        if (lastPong != null
                                && Instant.now().getEpochSecond() - lastPong.getEpochSecond() > 60) {
                            userSession.close(1000, "Timeout manually closing dead connection.");
                        }

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                },
                10, 10, TimeUnit.SECONDS));

... и обработать переподключение в методе onClose

как насчет session.getBasicRemote().sendPing(null);?

Sep GH 12.05.2018 12:28

Вы должны обойти эту проблему, внедрив систему контрольных сигналов, одна сторона которой отправляет ping, а другая отвечает pong. Почти каждый клиент и сервер websocket (насколько я знаю) внутренне поддерживают эту функцию. Эти рамки для пинг / понга могут быть отправлены с обеих сторон. Я обычно реализую его на стороне сервера, потому что обычно знаю, что у него больше шансов остаться в живых, чем у клиентов (мое мнение). Если клиенты не отправляют обратно понг в течение длительного времени, я знаю, что соединение разорвано. На стороне клиента я проверяю то же самое: если сервер долгое время не отправлял сообщения ping, я знаю, что соединение мертво.

Если ping / pong не реализованы в используемых вами библиотеках (которые, я думаю, есть в javax websocket), вы можете создать для этого свой собственный протокол.

Спасибо. У меня нет сервера, поэтому я не могу ничего кодировать на его стороне. Когда я отправляю ping-сообщения от своего клиента на сервер, я никогда не получаю ответов pong.

Ben 12.05.2018 12:20

не могли бы вы предоставить код того, как вы отправляете фреймы ping?

Sep GH 12.05.2018 12:25

Это в моем ответе выше

Ben 12.05.2018 12:27

Хорошо, я проверил ответ. Если session.getBasicRemote().sendPing(null); не работает, вам просто нужно связаться с бэкэнд-командой и уточнить это у них.

Sep GH 12.05.2018 12:31

Другие вопросы по теме