【问题标题】:Connection close in Java Servlet and SSEJava Servlet 和 SSE 中的连接关闭
【发布时间】:2014-08-29 16:47:29
【问题描述】:

我尝试使用服务器上的 Java Serlvet 在我的 Web 应用程序中实现 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();
        }
    }
}

【问题讨论】:

  • 在我看来,您正在尝试重新实现 WebSockets。或者彗星。
  • 如果您试图保持打开状态,请在“持久连接”上进行一些谷歌搜索,否则默认情况下,我相信一旦服务器传输了所有数据,连接就应该关闭。要创建持久连接,请参阅stackoverflow.com/questions/3304006/…

标签: java servlets server-sent-events


【解决方案1】:

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

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

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

不,不是。

【讨论】:

  • 是的,它是:)。不会抛出异常。我也预料到了。使用的应用服务器:WebSphere Application Server Liberty Profile
  • 嗨,@sasha_trn,您能与我们分享您对这个问题的结论吗?谢谢!
  • 嗨@Ricardo,那是很久以前的事了。 AFAIR,我没有找到一种方法来确定客户端关闭连接。我在没有无限循环的情况下更改了代码。
  • 谢谢,@sasha_trn,来自HttpServletHttpServletRequestHttpServletResponse source codes,我没有看到任何关于连接的信息,也没有Exception。最好的,
【解决方案2】:

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() 说:

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

【讨论】:

  • 感谢我的朋友 Shuge Li 和 Enyong Chen,还没有加入这个社区。​​span>
猜你喜欢
  • 2012-06-08
  • 2015-04-26
  • 2018-06-10
  • 1970-01-01
  • 1970-01-01
  • 2015-01-10
  • 1970-01-01
  • 1970-01-01
  • 2015-01-19
相关资源
最近更新 更多