1

我决定让我的客户端定期“ping”服务器,这样我就知道客户端还活着。我正在使用嵌入在 Java 应用程序中的 Jetty 服务器,并且我有一个 HttpServlet 来处理客户端请求。

在服务器端,我怎么知道客户端有一段时间没有“ping”(发送请求)?

4

5 回答 5

2

通过其 sessionID 识别客户端,并跟踪最后一次 ping。定期扫描所有最后的 ping 并找到在 N 分钟内没有 ping 的那些。

于 2012-07-20T19:09:40.880 回答
2

扩展 srini.venigalla 的答案,您可以使用类似这样的内容作为起点。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.*;

public class PingServlet extends HttpServlet {
    private static final long EXPIRATION_MILLIS = TimeUnit.SECONDS.toMillis(30);

    private final ConcurrentMap<String, Long> sessionIdToLastPingTime = new ConcurrentHashMap<String, Long>();
    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    public void init() {
        executor.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                scanForExpiredSessions();
            }
        }, 30, 30, TimeUnit.SECONDS);
    }

    private void scanForExpiredSessions() {
        for (Map.Entry<String, Long> entry : sessionIdToLastPingTime.entrySet()) {
            if (Thread.currentThread().isInterrupted()) {
                return;
            }

            final String sessionId = entry.getKey();
            final Long lastPing = entry.getValue();
            if (lastPing == null) {
                // entry was removed from the map by another thread
                continue;
            }
            if (System.currentTimeMillis() > lastPing + EXPIRATION_MILLIS) {
                sessionIdToLastPingTime.remove(sessionId);
                try {
                    expireSession(sessionId);
                } catch (Exception e) {
                    // production code should use a logger
                    e.printStackTrace();
                }
            }
        }
    }

    private void expireSession(String sessionId) {
        // production code should use a logger
        System.out.println("client stopped pinging for session " + sessionId);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String sessionId = req.getSession().getId();
        sessionIdToLastPingTime.put(sessionId, System.currentTimeMillis());
    }

    @Override
    public void destroy() {
        executor.shutdownNow();

        try {
            // this is optional, but may prevent Tomcat from reporting the webapp as a memory-leaker
            executor.awaitTermination(3, TimeUnit.SECONDS);
        } catch (InterruptedException e) {                                                                          
            // production code should use a logger
            System.out.println("interrupted while waiting for executor to shut down");
            Thread.currentThread().interrupt();
        }
    }
}
于 2012-07-20T20:34:43.913 回答
0

小事一桩:

  1. 告诉客户端每 N 秒 ping 一次
  2. 保留客户端最后一次 ping 的时间戳,在每次客户端 ping 后更新时间戳
  3. 如果此时间戳明显大于 N 秒,则客户端已停止 ping(至少现在)
于 2012-07-20T19:09:03.080 回答
0

在服务器端,您可以使用 java Date 类来存储您上次收到 ping 的时间。

long lastRecv = System.getTimeMillis();

在另一个循环中(可能是另一个定期检查所有已知客户端数组的线程),您将检查 lastRecv 时间码并将其与现在进行比较。

if( date.getTime() - lastRecv > TIMEOUT_IN_MS )
    // Handle user timing out

每次收到新的 ping 时,只需将 lastRecv 时间更新为新的当前时间。您将知道用户何时超时,因为您有一些预设阈值(例如 TIMEOUT_IN_MS)小于您上次看到 ping 进入的时间。

显然,您必须修改我在此处提供的示例代码以反映客户端的数量、您的线程模型和数据模型,但我认为它已经很好地传达了这个想法。

于 2012-07-20T19:11:29.253 回答
0

也许使用具有短会话超时的 HttpSessionListener?

http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpSessionListener.html

于 2012-07-20T20:30:33.043 回答