Пинг не отправляется из Tomcat WebSocket - PullRequest
0 голосов
/ 19 сентября 2019

Прямо сейчас я использую WebSocket с очень голыми костями на Tomcat 7, просто на месте.Он отправляет пинг в каждый сеанс каждые 30 секунд и закрывает сеанс после 3 пропущенных понгов.Проблема, с которой я столкнулся, заключается в том, что сервер правильно отправляет первый пинг, но не отправляет его впоследствии.Код для отправки фактического пинга выполняется, но из анализа сети, пинг не отправляется.Это также не бросает никаких исключений.Кроме того, я выполнял это на Tomcat 9 ранее, и у него не было этой проблемы.

Вот код для сердцебиения

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.websocket.PongMessage;
import javax.websocket.Session;

public class HeartbeatManager {

    private static final ByteBuffer DATA = ByteBuffer.wrap("heartbeat".getBytes());
    private static final long HEARTBEAT_DELAY_MS = 30 * 1000;
    private static final long MISSED_PONG_LIMIT = 3;

    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
    private Map<String, Heartbeat> sessionHeartbeats = new HashMap<String, Heartbeat>();

    public void start(Session session) {
        Heartbeat heartbeat = new Heartbeat(session);
        heartbeat.future = executor.scheduleAtFixedRate(heartbeat, HEARTBEAT_DELAY_MS, HEARTBEAT_DELAY_MS, TimeUnit.MILLISECONDS);
        Heartbeat oldHeartbeat = sessionHeartbeats.put(session.getId(), heartbeat);
        if (oldHeartbeat != null) {
            oldHeartbeat.future.cancel(true);
        }
    }

    public void stop(Session session) {
        Heartbeat heartbeat = sessionHeartbeats.remove(session.getId());
        if (heartbeat != null) {
            heartbeat.future.cancel(true);
        }
    }

    public void onPong(Session session, PongMessage message) {
        Heartbeat heartbeat = sessionHeartbeats.get(session.getId());
        if (heartbeat != null) {
            heartbeat.onPong(message);
        }
    }

    private class Heartbeat implements Runnable {

        private final Object LOCK = new Object();

        private ScheduledFuture<?> future;
        private Session session;
        private int missedPongs;
        private boolean pong = true;

        public Heartbeat(Session session) {
            this.session = session;
        }

        public void onPong(PongMessage message) {
            if (Arrays.equals(DATA.array(), message.getApplicationData().array())) {
                synchronized (LOCK) {
                    pong = true;
                    missedPongs = 0;
                }
            }
        }

        @Override
        public void run() {
            synchronized (LOCK) {
                if (!pong && ++missedPongs >= MISSED_PONG_LIMIT) {
                    try {
                        session.close();
                    } catch (IOException e) {
                    }
                } else {
                    pong = false;
                    try {
                        session.getBasicRemote().sendPing(DATA);
                    } catch (IOException e) {
                    }
                }
            }
        }

    }

}

И если это помогает, вот ServerEndpoint

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/test")
public class Test {

    private static HeartbeatManager m = new HeartbeatManager();

    @OnOpen
    public void onOpen(Session session) {
        m.start(session);
    }

    @OnMessage
    public void onMessage(Session session, PongMessage message) {
        m.onPong(session, message);
    }

    @OnClose
    public void onClose(Session session) {
        m.stop(session);
    }

}
...