Socket.IO

Real-Time Connections with Socket.IO

A WebSocket upgrade begins as an ordinary HTTP request, which Node exposes as the upgrade event on an http.Server, leaving you raw RFC 6455 frames. Few write that by hand; Socket.IO 24,482 (https://github.com/socketio/socket.io 63,210 ) (4.8.3, MIT) adds reconnection, acknowledgements, rooms, namespaces and a long-polling fallback.

A Socket.IO server and client, with rooms and an acknowledgementJavaScript
import { Server } from 'socket.io';
import { io as connect } from 'socket.io-client';
const server = new Server(3010, { cors: { origin: 'https://app.example.com' } });
server.on('connection', (socket) => {
  console.log('server:', socket.id, 'via', socket.conn.transport.name);
  socket.conn.on('upgrade', (t) => console.log('server: upgraded to', t.name));
  socket.join('room:books');
  socket.on('price', (payload, ack) => {
    server.to('room:books').emit('price', { ...payload, at: 'server' });
    ack({ ok: true });
  });
});
const client = connect('http://127.0.0.1:3010');
client.on('price', (p) => console.log('client: broadcast', p.isbn, p.at));
client.on('connect', async () => {
  console.log('client: ack', await client.emitWithAck('price', { isbn: '978-1', usd: 42 }));
  client.close(); server.close();
});
Output
server: yR0S0qbT4HQtz2NWAAAA via polling
client: broadcast 978-1 server
server: upgraded to websocket
client: ack { ok: true }

The session starts on polling and upgrades to websocket a moment later: Engine.IO, the transport underneath, establishes a connection guaranteed to work before trying a better one — also why a Socket.IO client cannot talk to a plain WebSocket server. emitWithAck turns the acknowledgement callback — the optional last argument of an emit — into a promise. ws 22,807 (https://github.com/websockets/ws 22,807 ) is the minimal alternative it uses internally (GraphQL and Real-Time APIs).