简体   繁体   English

Java Servlet和SSE中的连接关闭

[英]Connection close in Java Servlet and SSE

I try to implement Server-Sent-Event in my Webapp with Java Serlvet on server. 我尝试在服务器上使用Java Serlvet在Webapp中实现服务器发送事件。

Is it possible to check in Servlet that connection is closed by client? 是否可以在Servlet中检查客户端是否关闭了连接? The loop while(true) in Servlet is infinite even if client browser is closed. 即使关闭客户端浏览器,Servlet中的while(true)循环也是无限的。

Client code 客户代码

    function startLogSSE(lastEventId, level) {
        var eventSource = new EventSource("log-sse?last-event-id=" + lastEventId + "&level=" + level);
        eventSource.onmessage = function (event) {
            document.getElementById('log').innerHTML = event.data + "\n" + document.getElementById('log').innerHTML;
        };
    }

Server code 服务器代码

public class LogSSEServlet extends HttpServlet {
    private static final Logger logger = LoggerFactory.getLogger(LogSSEServlet.class);

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");

        PrintWriter writer = response.getWriter();

        // get logger purgerDB appender
        PurgerDBAppender appender = LogUtils.getPurgerDBAppender();
        if (appender == null) {
            writer.write("data: [ERROR] Appender 'purgerDB' isn't found for logger 'com.bp3'\n\n");
            writer.close();
            return;
        }

        int eventId = 0;
        // get last-event-id
        String lastEventId = request.getHeader("last-event-id");
        if (lastEventId == null) {
            // try to get lastEventId from parameter
            lastEventId = request.getParameter("last-event-id");
        }
        if (lastEventId != null) {
            try {
                eventId = Integer.parseInt(lastEventId);
            } catch (NumberFormatException e) {
                logger.error("Failed to parse last-event-id: " + lastEventId);
            }
        }
        String minLevel = request.getParameter("level");
        if (minLevel == null) {
            minLevel = "TRACE";
        }

        // get logs from purgerDB logger appender
        LogServices logServices = new LogServices();
        try {
            logServices.open();
        } catch (SQLException e) {
            throw new ServletException(e);
        }
        try {
            while (true) {
                List<LogMessage> messages = logServices.getLastMessages(Level.toLevel(minLevel), eventId, 0);
                if (messages.size() > 0) {
                    writer.write("id: " + messages.get(0).getEventId() + "\n");
                    writer.write("data: " + LogUtils.formatLog(messages) + "\n");
                    writer.flush();
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    break;
                }
            }
        } catch (SQLException e) {
            throw new ServletException(e);
        } finally {
            logServices.closeQuietly();
        }
    }
}

Is it possible to check in Servlet that connection is closed by client? 是否可以在Servlet中检查客户端是否关闭了连接?

Eventually an exception will be thrown: either an IOException: connection reset if you are streaming directly to the socket, or an OutOfMemoryError if the container is streaming to memory, which it does when you aren't using a fixed-length or chunked transfer mode. 最终将引发异常:如果直接将数据流传输到套接字,则抛出IOException: connection reset如果容器的数据流传输到内存,则将OutOfMemoryError ,这是在不使用固定长度或分块传输模式时发生的。 。

The loop while(true) in Servlet is infinite even if client browser is closed. 即使关闭客户端浏览器,Servlet中的while(true)循环也是无限的。

No it isn't. 不,不是。

One way to check, wihin the Servlet , that connection is closed, is using the writer.checkError() method. Servlet检查连接是否关闭的一种方法是使用writer.checkError()方法。 I tested this fix on Chrome and it works. 我在Chrome上测试了此修复程序,并且可以正常工作。 Your code would be: 您的代码为:

            boolean error=false;
            while (!error) {
                //...                   
                writer.write("data: " + /*...*/  "\n");
                //writer.flush();
                error = writer.checkError();    //internally calls writer.flush()
            }

Details : 详细资料

The PrintWriter 's API says: PrintWriter的API说:

Methods in this class never throw I/O exceptions, although some of its constructors may. 此类中的方法永远不会抛出I / O异常,尽管它的某些构造函数可能会抛出异常。 The client may inquire as to whether any errors have occurred by invoking checkError() . 客户端可以通过调用checkError()来查询是否发生了任何错误。

and the checkError() says: checkError()表示:

Flushes the stream if it's not closed and checks its error state 如果未关闭,则刷新流并检查其错误状态

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM