Java Servlet和SSE中的连接关闭

时间:2014-08-29 16:47:30

标签: java servlets server-sent-events

我尝试在服务器上使用Java Serlvet在我的Webapp中实现Server-Sent-Event。

是否可以在Servlet中检查客户端是否已关闭连接?即使客户端浏览器关闭,Servlet中的循环while(true)也是无限的。

客户代码

    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;
        };
    }

服务器代码

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();
        }
    }
}

2 个答案:

答案 0 :(得分:0)

  

是否可以在Servlet中检查客户端是否已关闭连接?

最终会抛出异常:如果您直接流式传输到套接字,则为IOException: connection reset;如果容器正在流式传输到内存,则为OutOfMemoryError,当您不使用时,它会执行固定长度或分块传输模式。

  

即使客户端浏览器关闭,Servlet中的循环while(true)也是无限的。

不,不是。

答案 1 :(得分:0)

检查连接已关闭的Servlet的一种方法是使用writer.checkError()方法。我在Chrome上测试了此修复程序并且可以正常运行你的代码是:

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

<强>详情:

PrintWriter's API说:

  

此类中的方法永远不会抛出I / O异常,尽管它有些异常   施工人员可以。客户可以询问是否有任何错误   通过调用checkError()发生。

checkError()说:

  

如果流未关闭则刷新并检查其错误状态