21

i have this simple node.js Servercode using socket.io (1.5):

var io = require('socket.io').listen(8080);

io.on('connection', function(socket) {

    console.log(' %s sockets connected', io.engine.clientsCount);

    socket.on('disconnect', function() {
        console.log("disconnect: ", socket.id);
    });
});

If i run this code und press F5 several times, in some cases new connection is created, before the old one is disconnected. After some time, i think its the Heartbeat Timout, all the connections will be closed. See the result:

 2 sockets connected
 3 sockets connected
 4 sockets connected
 5 sockets connected
 6 sockets connected
 7 sockets connected
 8 sockets connected
 9 sockets connected
 10 sockets connected
 11 sockets connected
disconnect:  0h_9pkbAaE3ftKT9AAAL
 11 sockets connected
 12 sockets connected
 13 sockets connected
 14 sockets connected
disconnect:  oB4HQRCOY1UIvvZkAAAP
 14 sockets connected
 15 sockets connected
disconnect:  LiIN0oDVoqbePgxFAAAR
 15 sockets connected
 16 sockets connected
 17 sockets connected
 18 sockets connected
disconnect:  zxvk-uhWABHzmu1uAAAV
 18 sockets connected
 19 sockets connected
 20 sockets connected
disconnect:  FlboxgTzcjf6ScffAAAY
 20 sockets connected
 21 sockets connected
disconnect:  9UGXbnzukfGX_UtWAAAa
 21 sockets connected
disconnect:  pAfXOEz6RocKZdoZAAAb
 21 sockets connected
disconnect:  DIhTyVgG2LYBawaiAAAc
 21 sockets connected
disconnect:  W4XOc1iRymfTE2U0AAAd
 21 sockets connected
disconnect:  WZzegGPcoGDNLRTGAAAe
 21 sockets connected
 22 sockets connected
disconnect:  KVR3-fYH0cz77BmgAAAC
disconnect:  ANQknhnxr4l-OAuIAAAD
disconnect:  KZE5orNx6u9MbOArAAAE
disconnect:  TS6LL3asXrcznfcPAAAF
disconnect:  SVNxS3I7KqecdqKhAAAG
disconnect:  IE2WE5Y0PJzvxgBfAAAH
disconnect:  v69bdJav9PjpThBGAAAI
disconnect:  mJKT1ggfOOTshZKgAAAJ
disconnect:  YlycVjdcWe0emCAcAAAK
disconnect:  MoIDJSzP_L-1RUwuAAAM
disconnect:  wAl0x5qwCkrnDDYQAAAN
disconnect:  eiTlPEk2Hx_X-L-fAAAO
disconnect:  KgkrXxzG_EpXOsPTAAAQ
disconnect:  Lvf3kK-6XXEbu3NWAAAS
disconnect:  -hOoGdYOIvVK04K_AAAT
disconnect:  3EUmaAYpK-U3Ss9tAAAU
disconnect:  HQ6M98FebtKlU3OfAAAW
disconnect:  OwgrbRBYbS4j84nmAAAX
disconnect:  yN8FZAP4RjUNl2MeAAAZ
disconnect:  K9IFTjlgAWzdNfpUAAAf

My Question is: Is this a Bug or is this the normal behavior of socket.io? How can i prevent the connection flooding, simple pressing F5?

Best Regards Marc

mcbookwood
  • 213
  • 1
  • 2
  • 5
  • The browser should close open webSockets when you refresh the browser and then you should get a disconnect. If it's not, then that would be a browser bug. In case the browser does not immediatley close the webSocket, socket.io will see that the socket is no longer active (because of the heartbeat) and the socket will eventually get cleaned up. – jfriend00 Jan 30 '17 at 02:17
  • 1
    This happens with all actual Browsers. – mcbookwood Jan 30 '17 at 17:56
  • What is the amount of time between when you hit F5 and when the server finally gets the disconnect? It also looks like something may be wrong in your count because you don't show the count going down when you get a disconnect. – jfriend00 Jan 30 '17 at 23:40
  • 1
    If you press F5 only once the, disconnect comes immediatly. But if you press F5 fast enough some discennects are "lost". I dont't think the counter is wrong, because the example server only counts when a new connection opens – mcbookwood Jan 31 '17 at 06:23
  • My comment about the counter is that you get a disconnect message, but the count doesn't go down. That seems pretty visible in your logging. If it's just rapid fire refreshes that cause the problem, I don't know why you're worried about the issue at all. They do clean up after themselves after some short time so there's no accidental long term accumulation of anything. Does this cause any real issue? You can shorten the socket.io inactivity timeout so they clean themselves up sooner, but that could create other compromises so if it's not actually causing a real problem, I wouldn't change. – jfriend00 Jan 31 '17 at 06:28
  • Sorry i was not clear enough...For example 1: 14 sockets connected 2: disconnect: oB4HQRCOY1UIvvZkAAAP 3: 14 sockets connected First row: There is a new connection => Count = 14 Second row: There was a disconnection => Count = 13 (You can't see the 13 in log, because its only write into log for a new connection) Third row: There is a new connect => 13 + 1 = 14 If this is a real Problem? I don't know...but if you emit Messages to other Sockets on a disconnect event it can be a Problem. – mcbookwood Jan 31 '17 at 08:31
  • @mcbookwood have a look at my answer to my own question here - it fixed it for me. https://stackoverflow.com/questions/48137228/socket-io-changefeed-multiple-emits-on-refresh-reload/ – Bolli Jan 08 '18 at 13:34

3 Answers3

43

I made my own test app and was able to figure out what is going on.

If you hit F5 quite fast multiple times, It does temporarily accumulate some extra socket.io connections in Chrome, but within a relatively short time (maybe a few minutes), it recovers and the total count of connected sockets is back to 1.

After further testing, I discovered that this is not a browser issue. This is an issue with how socket.io starts a socket.io connection. If you replace this in the client:

var socket = io();

with this:

var socket = io({transports: ['websocket'], upgrade: false});

which forces socket.io to ONLY use a webSocket and never use HTTP polling, then the problem disappears.

So, the issue is because the default behavior for socket.io is to start with an http polling version of a socket.io connection. After a little data is exchanged, socket.io will then attempt to switch over to a real webSocket. If that real webSocket works, then it will stop using the http polling connection.

But, if you hit an F5 in the middle of this transition between polling and a real webSocket, there is no persistent connection yet for socket.io to know that the web page it was just communicating with is now gone. So, all it can do is to figure out some time later that there is no longer any incoming communication from that web page and thus it should clear up it's socket.io connection (it was in polling mode when you hit F5).

But, if you turn off that initial polling mode with the above client code, then it only ever uses a real webSocket (never uses the simulated polling mode) and the browsers are very good at cleaning up the webSocket when you hit F5 so the server either hasn't finished establishing it's socket.io connection (in which case there's no connection yet to get temporarily orphaned) or it's already converted over to a webSocket (and the browser will cleanly close that on the F5).

So, this is a design limitation of the http polling mode that socket.io starts in. Since there is no continuous connection when in that mode, there is no immediately notification by the browser when that page is replaced with F5 and thus the server has no way of knowing that the client just disappeared. But, if you skip the http polling mode and start with a real webSocket, then there is no such window of time where there's a socket.io connection, but no real webSocket and thus the server is always told immediately by the browser closing the webSocket connection when the page goes away.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    Ah, ok that makes sense. And after editing the socket Connection on client and server the problem doesn't occur any more. Thank you for this explanation! – mcbookwood Jan 31 '17 at 13:38
  • @jfriend00 - this was very helpful to tackle the page refresh. I was using flask-socket-io as backends but this technique should always work in client side socket-io. – kta Apr 08 '20 at 01:59
2

Solution: update the versions of your socket.io packages front and back

example : back : socket.io 3.0.3

example : front : socket-io-client 3.0.3

now is compatible and transport socket is websocket and not polling !

import io from ....

const socket = io('http//localhost:3000') -> server node

Canonne Gregory
  • 378
  • 4
  • 9
0

I had kind of the same error, I was getting multiple connections with the same user, the error was that I was using a different version on the backend and I added this piece of code on the front-end :

socket.on("connect_error", (err) => { console.log(connect_error due to ${err.message}); });

in that way, I could know the error, I hope this can help someone