86

I'm building an application in java that has an embedded websocket server based on jetty. The client is the default websocket implementation in google chrome. Everything is working ok, only if there is no transfer between server and client after a certain time the connection is closed. I'm not sure who's closing the connection: the jetty server or the chrome browser.

The solution to this I think is to send a message every x seconds, but I'm opened to better solutions.

SO... my questions are:

  1. Is this something that the websocket protocol requires and in this case the chrome browser is closing my connection?

  2. Is this something that is more jetty related and has more or less to do with the websocket protocol? In this case how do I disable this in jetty?

  3. Is there another problem??

Thanks

UPDATE: even if I send 1 message/second still the connection is closed

Doua Beri
  • 10,612
  • 18
  • 89
  • 138
  • 1
    Do you have any proxy between the client and the server ? Proxies are known to sometimes close websockets (http://stackoverflow.com/questions/9017113/websockets-behind-enterprises-proxies) – ndeverge Jan 30 '12 at 09:33
  • 1
    I'm using Jetty and I have the same problem. No proxy - I have a server on localhost with the browser on the same machine. – Ant Kutschera Mar 08 '15 at 21:46
  • mmm are you testing it on Internet Explorer?? cause I'm still suffering about this strange IE beahvior: https://connect.microsoft.com/IE/feedback/details/804653/rfc6455-websocket-pong-frame – Plastic Oct 20 '17 at 10:30

12 Answers12

57

In answer to your third question: your client wants to be able to cope with temporary network problems anyway, e.g. let's say the user closes their laptop between meetings which hibernates it, or the network simply goes down temporarily.

The solution is to listen to onclose events on the web socket client and when they occur, set a client side timeout to re-open the connection, say in a second:

function setupWebSocket(){
    this.ws = new WebSocket('wss://host:port/path');
    this.ws.onerror = ...;
    this.ws.onopen = ...;
    this.ws.onmessage = ...;
    this.ws.onclose = function(){
        setTimeout(setupWebSocket, 1000);
    };
}
Ant Kutschera
  • 6,257
  • 4
  • 29
  • 40
20

You need to send ping messages from time to time. I think the default timeout is 300 seconds. Sending websocket ping/pong frame from browser

Community
  • 1
  • 1
10

I found another, rather quick and dirty, solution. If you use the low level approach to implement the WebSocket and you Implement the onOpen method yourself you receive an object implementing the WebSocket.Connection interface. This object has a setMaxIdleTime method which you can adjust.

LeoR
  • 666
  • 1
  • 6
  • 20
8

You can actually set the timeout interval at the Jetty server side configuration using the WebSocketServletFactory instance. For example:

WebSocketHandler wsHandler = new WebSocketHandler() {
    @Override
    public void configure(WebSocketServletFactory factory) {
        factory.getPolicy().setIdleTimeout(1500);
        factory.register(MyWebSocketAdapter.class);
        ...
    }
}
Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
user3499459
  • 81
  • 1
  • 1
4

Just found the solution to this for myself. What you want to set is the maxIdleTime of WebSocketServlet, in millis. How to do that depends on how you config your servlet. With Guice ServletModule you can do something like this for timeout of 10 hours:

serve("ws").with(MyWSServlet.class, 
new HashMap<String, Sring>(){{ put("maxIdleTime", TimeUnit.HOURS.toMillis(10) + ""); }});

Anything <0 is infinite idle time I believe.

AkaZn
  • 59
  • 1
  • 3
4

I think this timeout you are experiencing is actually part of TCP/IP and the solution is to just send empty messages once in a while.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • 1
    hey thanks.. but this doesn't help me. I've just tried with 1 message every second and the connection is still closing – Doua Beri Jan 29 '12 at 20:49
  • David, can you back that up? My understanding is that TCP leaves policy decisions like idle timeout up to the application, OS, and network infrastructure (TCP tries to be policy agnostic). Perhaps you are thinking of keep-alive which is periodic messages to ensure that the connection hasn't died silently (which eventually timeout and close the connection usually after 2 hours). – kanaka Jan 30 '12 at 17:57
  • @kanaka I read that somewhere but I can't really back it up. I know for sure that when I was writing my own websocket server with Python, I found that I needed to send an empty message once in a while to prevent it from disconnecting. – David Grayson Jan 31 '12 at 01:01
  • This is not part of TCP/IP, but can be the result of a stateful firewall losing the state of the connection because it has been idle for too long. Likely, you are thinking of TCP Keepalives, which solve this problem. – David Hoelzer Jul 24 '19 at 01:47
3

I believe this is a Jetty issue. I have not seen any browsers close WebSocket connections due to inactivity nor have I encountered other WebSocket servers that timeout WebSocket connections.

Jetty is (was) primarily focused on building HTTP based application servlets. In that context, HTTP connections need to be cleaned up pretty aggressively and HTTP was not designed for long-lived connections so having a short default timeout is reasonable.

I've not seen the precise problem you described (closing even with activity) but I do see WebSocket connections closed after 30 second of inactivity. It's possible that in older versions of Jetty or in the current version for some other reason, the timer is not reset by WebSocket activity. I get around this by using the setMaxIdleTime method on my BlockingChannelConnector object to set the timeout value to Integer MAX_VALUE.

kanaka
  • 70,845
  • 23
  • 144
  • 140
2

Here is an example on how to configure Jetty's websocket timeout (the most likely culprit) using WebSocketServlet (in scala, sorry, but the syntax is pretty much the same).

import javax.servlet.annotation.WebServlet
import org.eclipse.jetty.websocket.servlet.{WebSocketServletFactory, WebSocketServlet}

@WebServlet(name = "WebSocket Servlet")
class WebsocketServlet extends WebSocketServlet {
  override def configure(factory: WebSocketServletFactory): Unit = {
    factory.getPolicy.setIdleTimeout(1000 * 3600)
    factory.register(classOf[ClientWebsocket])
  }
}
user1338062
  • 11,939
  • 3
  • 73
  • 67
1

I have a similar experience and I believe that it might be the browser that is cutting the session short. I also set the maxIdleTimeout, but the session is dropped regardless. To me, it looks like it is the client (the browser) that is timing out the session and then hangs up.

Don't know how to work around it.

AndersW
  • 58
  • 7
1

Since @Doua Beri is experiencing connection close even when there are 1 Hz SENDs, it may be instead be due to size limits on messages.

This passage from Spring's WebSockets may be useful, with my emphasis ...

Although in theory a WebSocket message can be almost unlimited in size, in practice WebSocket servers impose limits — for example, 8K on Tomcat and 64K on Jetty. For this reason STOMP clients such as stomp.js split larger STOMP messages at 16K boundaries and send them as multiple WebSocket messages thus requiring the server to buffer and re-assemble.

Ken Lin
  • 1,819
  • 21
  • 21
0

Same issue: Was using WebSockets & sockjs-client/1.0.3/sockjs library with @ServerEndPoint on Java Server side. The websocket connections kept breaking variably.

I moved to using Stomp and sockJS (abandoning the @ServerEndpoint) but encountered another issue popular on SO - /info=34424 - with 404 error -

I had to abandon using the xml approach of Stomp Spring library as suggested at other places. I have Spring 4.2 in my project and many SockJS Stomp implementations usually work well with Spring Boot implementations. This implementation from Baeldung worked(for me without changing from Spring 4.2 to 5).

After Using the dependencies mentioned in his blog, it still gave me ClassNotFoundError. I added the below dependency to fix it.

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.2.3.RELEASE</version>
    </dependency>
veritas
  • 378
  • 1
  • 6
  • 16
0

This worked for me while the other solutions were not!

  1. Update your jupyters
  2. Start your jupyters via your preferred notebook or lab but add this code at the end of the line in the console: <--no-browser>

This is stopping to need to be connected to your EC2 instance all the time. Therefore even if you loose connection due to internet connection loss, whenever you gain a new wifi access it automatically re-connects to the actively working kernel.

Also, don't forget to start your jupyter in a tmux account or a ngnix or other environments like that.

Hope this helps!