Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make sure only one socket.io connection is open per user?

I'm using socket.io and socket.io-client on the Express and React sides, respectively.

On the server side, I console.log in the connection event with the socket.id, and it always displays two connections when I only have one page open, like so:

A user connected woF_Gu_8ElqMB7a5AAAC A user connected lBAycJ6j0X5Q469SAAAD

However, when emitting messages from the front end, only one connection is used.

Here's how I set up socket.io server side:

// ** bin/www file **
var server = http.createServer(app);
const io = require('socket.io')(server);

// Get socket module and pass it the io socket from here
require('../sockets')(io);

// ** sockets.js **
module.exports = io => {
  io.on('connection', (socket) => {
    console.log("A user connected", socket.id); // Fires twice with different IDs

    socket.on("Setup", data => {
      console.log("Data received:", data); // Only fires once per emit from frontend
    }

    socket.on("disconnect", () => {
      console.log("User disconnected");
    }

}

Here's how I set up client side, though I'm not sure this matters since it's happening consistently even when I don't have this code in:

import io from 'socket.io-client';
const socket = io(window.location.origin);

Any idea why I'm getting two connections all the time? Thanks!

EDIT: Adding network screenshot.enter image description here

like image 896
YoungZaphod Avatar asked Apr 25 '20 19:04

YoungZaphod


2 Answers

I solved my problem and wanted to share the solution here though I don't know exactly why what I was doing previously wasn't working.

I was using React Router in App.js, and I was importing socket.io-client in a component that got routed to. That was giving a double-connection for some reason (or more, depending on where I put the const socket = io(); line).

My solution was to import socket.io-client in App.js and pass io to the component as a prop where needed, like so:

import * as io from 'socket.io-client';

const socket = io();

class App extends Component {
 render() {
  return (
   <Router>
    <Header/>
    <Switch>
     <Route exact path="/" render={props => (<Home/>)}/>
     <Route exact path="/story" render={props => (<Start socket={socket} />)}/>
    </Switch>
   </Router>
  );
 }
}

export default App;

This defines the socket at a high level and passes down where needed, so nothing is getting called twice and I stay at one connection.

like image 148
YoungZaphod Avatar answered Oct 09 '22 11:10

YoungZaphod


OK, let's analyze your network trace in the screenshot. I'll start at the line that says "localhost" because that is presumably where you reloaded a page.

  1. localhost - Normal reload of a cached page
  2. 8 script files requested - All load normally
  3. Two socket.io requests arrive that are ?EIO=3&transport=polling&t=N6pOEHc. These are not normal requests. They are missing an sid=xxxxx value from the URL.
  4. A socket.io request arrives that is ?EIO=3&transport=polling&t=N6pOEKbsid=zhaGN0gOV6Qh3bg-AAAq. These look a normal socket.io connection initiation with a few polling requests before it switches over to a webSocket.
  5. A fetch Ajax request for incomplete arrives.
  6. A second socket.io polling request on the sid=zhaGN0gOV6Qh3bg-AAAq arrives. This again looks normal.
  7. A new socket.io request arrives from ?EIO=3transport=polling&t=N6pOEPQ&sid=wWH-VzFditNgmdWNAAAr. This is the second normal looking socket.io connection request. But, we don't know why there is a second attempt to connect.
  8. A webSocket connection sent out to socketjs-node and accepted (101 status) as a webSocket connection.
  9. Another socket.io polling request sent by sid=zhaGN0gOV6Qh3bg-AAAq. This is probably a request to convert to a webSocket connection, but is still marked as "pending" so it apparently did not succeed (as of the screenshot, perhaps never).
  10. Another polling request on the second socket.io connection sid=wWH-VzFditNgmdWNAAAr
  11. manifest.json and favicon.ico are requests
  12. 4 more polling requests from the second connection &sid=wWH-VzFditNgmdWNAAAr.
  13. 2 more polling requests from the first connection sid=zhaGN0gOV6Qh3bg-AAAq which definitely indicates that the previous attempt to turn this into a webSocket apparently did not work.

So, there are actually three socket.io attempts here. The first one looks a bit mal-formed, not sure why that is. The second two look like they are both initiated by client code. Neither one of them succeeds in switching to the webSocket transport.

My best guess would be that you have mismatched socket.io versions in client and server and thus they are having a hard time making the right type of lasting connection. It could also be that there's some sort of infrastructure in the way like a proxy that is preventing a webSocket connection from succeeding.

For other possible reasons, see this: Whats the problem with the socketio connection?


Another thing you can do for debugging purposes is to change the client code from this:

io(window.location.origin);

to this:

io(window.location.origin, {transports: ['websocket']});

This will force this connection attempt to ONLY connect with a webSocket. If we see a network trace from this, we can more clearly see whether a webSocket connection succeeds of fails.

It is also unusual to be using window.location.origin as the URL. If you just leave the URL out entirely, socket.io will just connect back to the host of the page which is generally what you want.

like image 42
jfriend00 Avatar answered Oct 09 '22 12:10

jfriend00