views:

480

answers:

4

Hi,

I have a servlet which processes a request for a long time. It suppose to keep doing stuff in the loop inside doPost and send data through response's out writer. Effectively that continuously appends data in the clients browser . But the problems accures when client just closes the browser. Inspite of the broken connection the response's writer stream in the servlet never gets closed, thus servlet is unaware of the brocen connection, and keep dumping data into the writer without any errors. How is that posssible? And how do I detect and cancel long request processing in case of browser disconnect?

I thougth checkError() on the response writer should do the trick, but it does not seam to work. Any ideas why?

This is the servlet code which never stops:

protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
   HttpSession session = request.getSession();
   System.out.println("Session " + session.getId() + " started");

   response.setContentType("text/html;charset=UTF-8");

   PrintWriter out = response.getWriter();
   try
   {
       while (!out.checkError())
       {
           try
           {
               Thread.sleep(1000);
           } catch (InterruptedException ex)
           {
               ex.printStackTrace();
           }

           Date date = new Date();

           // TODO append output to the client browser here
           out.println(".....");

           System.out.println("Session " + session.getId() + "data sent at: " + date);

           out.flush();
       //break;  // _TEST
       }
   } finally
   {
       System.out.println("Session " + session.getId() + " finished");
       out.close();
   }
}

Thanks,
Mike

A: 

The only real way of dealing with this is to have the browser constantly tell you that it's still there, say every 5-10 seconds. If you go for a minute without hearing anything, you assume the client is gone and forget what you're doing.

There are several ways to implement this:

  • Meta refresh: I don't like this because it's disconcerting and obvious for the user;
  • An iframe that does that (less obvious); or
  • Some AJAX technique, which is probably the best option.

There's no real way you can detect if the client is still reading what you're sending. It takes time for TCP connections to die and the path to the client is often indirect anyway (through proxies and so forth) so forget about detecting that.

cletus
It looks like you are right. I would have to have some mechanizm to send ACKs from client side to indicate that server should send data. That is unfortunate and strange because http uses TCP and thus suppose to be connection-aware.
Ma99uS
It's a misnomer to describe HTTP as TCP. Sure HTTP the protocol is but the Web isn't (necessarily). Consider Squid caches that communicate and deliver content over UDP. Also your Web app might deliver content, close the connection and the proxy loses connectoin to the browser.
cletus
Does that mean that if you started downloading big file through web browser, and than you close it. The server will still send you the whole file using trafic or/and server resources?
Ma99uS
A: 

The servlet has an output buffer that must fill up before the data is actually sent to the browser. If this buffer has not been filled, no data is actually sent on the network. Until you send data on the network, you wont know that the connection is closed.

You can set the buffer size by calling a method on your response object before getting an output stream or writer.

Martin OConnor
I have tried adding response.setBufferSize(0); but it did not seem to have any effect.
Ma99uS
You cannot set it to zero, you could set it to some small value and try to write to the buffer.
Martin OConnor
A: 

You're trying to do something that is not supported by the HTTP protocol. The whole nature of the Web is based on a request/response.

One way to simulate what you need is to use Ajax. The flow would be something like this:

  1. Browser sends, using Ajax, a request for items 1 through 10
  2. Server process request, build and send response with items from 1 through 10.
  3. Browser receives response, parse result, display items from 1 to 10 and repeat step 1, requesting items 11 to 20.

This architecture will eliminate the need to check for the "availability" of the client, and also will improve the scalability of your server side components.

I hope it helps.

Handerson
A: 

java.io.PrintWriter javadoc sez: Methods in this class never throw I/O exceptions - so use javax.servlet.SocketOutputStream (response.getOutputStream()) instead, and you'll get some IOException eventually.

joe