WebSocket: OnClose() никогда не вызывается

Я реализую приложение с конечной точкой WebSocket. Вот код:

@ApplicationScoped
@ServerEndpoint(value="/socket", encoders = {MessageEncoder.class, CommandEncoder.class})
public class SocketEndpoint {

    /** Default-Logger */
    private final static Logger LOG = LoggerFactory.getLogger(SocketEndpoint.class);

    @Inject
    SessionHandler sessionHandler;

    @OnOpen
    public void open(Session session, EndpointConfig config) {
        LOG.debug("Connected session => '{}' - '{}'", session, config);
        sessionHandler.initSession(session);
    }

    @OnMessage
    public void onMessage(Session session, String messageJson) {
        // do something
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        LOG.debug("Closing session => '{}' - '{}'", session, reason);
        sessionHandler.removeSession(session);
    }

    @OnError
    public void onError(Session session, Throwable ex) { 
        LOG.error("WebSocket error => '{}'", ex.getMessage()); 
    }
}

Один из классов encode выглядит так:

public class MessageEncoder implements Encoder.Text<Message> {

    /** Default-Logger */
    private final static Logger LOG = LoggerFactory.getLogger(MessageEncoder.class);

    @Override
    public void init(EndpointConfig config) {
        LOG.debug("Init MessageEncoder");
    }

    @Override
    public void destroy() {
        LOG.debug("Destroy MessageEncoder");
    }

    @Override
    public String encode(MessageE message) throws EncodeException {
        return message.toString();
    }
}

Открытие WebSocket вызывает SocketEndpoint.open(), как и ожидалось. Закрытие WebSocket вызывает только MessageEncoder.destroy(), но не SocketEndpoint.close().

Может кто подскажет, что я сделал не так? Без решения мне пришлось бы вручную проверять, живы ли зарегистрированные сеансы, поскольку MessageEncoder.destroy() не имеет параметров.

Заранее спасибо!


ОБНОВЛЕНИЕ

Только что реализовал фиктивную конечную точку:

@ApplicationScoped
@ServerEndpoint("/dummy")
public class DummyEndpoint {

    /** Default-Logger */
    private final static Logger LOG = LoggerFactory.getLogger(DummyEndpoint.class);

    @OnOpen
    public void open(Session session, EndpointConfig config) {
        LOG.debug("Connected session with principal => '{}'", session.getId());
    }

    @OnMessage
    public void onMessage(Session session, String messageJson) {
        LOG.debug("on message => '{}' => '{}'", session.getId(), messageJson); 
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        LOG.debug("Closing session => '{}' - '{}'", session, reason);
    }

    @OnError
    public void onError(Session session, Throwable ex) { 
        LOG.error("WebSocket error => '{}' => '{}'", session, ex.getMessage()); 
    }
}

При использовании этой фиктивной конечной точки @OnClose вызывается правильно. Я вижу только одно существенное отличие от класса SocketEndpoint: DummyEndpoint не использует никаких классов Encoder.

Любые подсказки?


person Second2None    schedule 25.07.2017    source источник
comment
какой сервер приложений вы используете? и когда вы говорите о закрытии WebSocket, что вы имеете в виду? вы закрываете его от клиента vi, вызывающего websocket.close ()? или закрыть браузер?   -  person Itherael    schedule 30.07.2017
comment
Я использую сервер приложений Wildfly 10.0.1. попытался закрыть веб-сокет, вызвав ws.close() и закрыв браузер.   -  person Second2None    schedule 30.07.2017
comment
Возможный дубликат с: stackoverflow.com/questions/24717403/   -  person Duong Nguyen    schedule 31.07.2017
comment
@Duong Nguyen Это не дубликат. Увидел сообщение, которое вы имеете в виду, прежде чем опубликовать это. Я протестировал свой код с Firefox, Chrom, IE, Edge и некоторыми надстройками FF/Chrome для веб-сокетов... Кроме того: мой реализованный метод @OnError также не вызывается. Единственный метод, который вызывается при закрытии веб-сокета, — это «@OnDestroy» в классе кодировщика.   -  person Second2None    schedule 31.07.2017
comment
Используя этот краткий запуск и добавив аннотацию OnClose, метод вызывается при закрытии вкладки. Я использовал wildfly 10.1.0.Final, Java 8. Что это за SessionHandler вы используете в своей конечной точке?   -  person Adonis    schedule 31.07.2017
comment
SessionHandler — это просто класс, предоставляющий карту, которая содержит все зарегистрированные сеансы и операции на этой карте.   -  person Second2None    schedule 31.07.2017
comment
С тем, что вы предоставили выше, я могу создавать кодировщики, bean-компоненты, вводить их, и метод onclose по-прежнему вызывается правильно, поэтому проблема возникает откуда-то еще, было бы неплохо, если бы вы могли указать репозиторий с вашим кодом для воспроизведения именно вопрос, так как пока это неосуществимо.   -  person Adonis    schedule 31.07.2017
comment
Это действительно странно! Я только что создал новый класс для конечной точки веб-сокета и скопировал и вставил туда все, что любопытно, теперь он работает... Спасибо за внимание!   -  person Second2None    schedule 01.08.2017
comment
На самом деле это заставляет меня задуматься об одной вещи, у меня недавно была проблема с Wildfly, потому что при перезапуске сервера он не очищал ранее старые ресурсы (я использую серверы с Eclipse), мне пришлось перейти на вкладки сервера и использовать параметр Clean на моя Уайлдфлай. Я подозреваю, что что-то подобное происходит, потому что ваш код хорошо работает на моем экземпляре. (Кстати, при ответе используйте @username, чтобы я увидел ваш ответ в своем почтовом ящике)   -  person Adonis    schedule 01.08.2017


Ответы (1)


Как упоминалось в комментариях, код работает просто отлично. Если мы начнем с этого wildfly-websocket-quickstart, добавив декорированный метод @OnClose на ServerEndpoint, он отлично работает с Wildfly 10.x и последними браузерами (например, Chrome v59.x). Пример работы ServerEndpoint здесь (чтобы использовать @Inject, не забудьте добавить beans.xml в папку WEB-INF):

@ApplicationScoped
@ServerEndpoint(value="/shout", encoders = {MessageEncoder.class})
public class ShoutServerEndpoint {

    @Inject
    SessionHandler s;

    @OnOpen
    public void open(Session session, EndpointConfig config) throws Exception {
        s.initSession(session);
    }

    @OnMessage
    public void shout(String text, Session client) {
        System.out.println("Session: " + client + " has text: " + text);
        Message m = new Message();
        try {
            client.getBasicRemote().sendObject(m);//use the encoder to write some dummy message
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (EncodeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        client.getAsyncRemote().sendText(text.toUpperCase());
    }


    @OnClose
    public void onClose(Session client, CloseReason reason){
        System.out.println("Session " + client + " closing for " + reason);
        s.destroySession(client);

    }

    @OnError
    public void onError(Session session, Throwable ex) { 
        System.out.println("error: " + ex.getMessage() );
    }
}

Таким образом, виновником, по-видимому, является более старая версия кода, используемая wildfly, которая не была очищена во время повторного развертывания веб-приложения, например, с использованием Eclipse, стоит в случае странного поведения использовать параметр Clean на используемый сервер (см.: этот документ Eclipse)

При развертывании непосредственно с помощью wildfly вы можете очистить свои ресурсы, удалив все в (из эта статья):

  • /[wildfly-location]/standalone/data
  • /[wildfly-location]/standalone/deployments
  • /[wildfly-location]/standalone/tmp

Это гарантирует, что старые копии вашего кода не останутся во время будущего развертывания.

person Adonis    schedule 01.08.2017