90

Is it possible to have a socket.io client respond to all events without to have specify each event individually?

For example, something like this (which obviously doesn't work right now):

var socket = io.connect("http://myserver");

socket.on("*", function(){
  // listen to any and all events that are emitted from the
  // socket.io back-end server, and handle them here.

  // is this possible? how can i do this?
});

I want this callback function to be called when any / all events are received by the client-side socket.io code.

Is this possible? How?

Derick Bailey
  • 70,813
  • 22
  • 200
  • 217
  • I opened the following issue on forwarding all event types, I've also been adding the solutions I found: https://github.com/Automattic/socket.io/issues/1715 – Peter Uithoven Aug 27 '14 at 19:41

14 Answers14

87

Updated solution for socket.io-client 1.3.7

var onevent = socket.onevent;
socket.onevent = function (packet) {
    var args = packet.data || [];
    onevent.call (this, packet);    // original call
    packet.data = ["*"].concat(args);
    onevent.call(this, packet);      // additional call to catch-all
};

Use like this:

socket.on("*",function(event,data) {
    console.log(event);
    console.log(data);
});

None of the answers worked for me, though the one of Mathias Hopf and Maros Pixel came close, this is my adjusted version.

NOTE: this only catches custom events, not connect/disconnect etc

Flion
  • 9,716
  • 12
  • 43
  • 64
  • 2
    Thank you, this helped me debug missing events! – Maitreya May 19 '16 at 08:02
  • 1
    Unfortunately not working with things like 'user.*';. Maybe I can manage to implement it. – C4d Mar 16 '17 at 16:54
  • Hi, very good, but it could be helpful if you could have some output examples ^^ I feel like I need to run the code to understand your answer :) – TOPKAT Oct 03 '19 at 16:47
25

It looks like the socket.io library stores these in a dictionary. As such, don't think this would be possible without modifying the source.

From source:

EventEmitter.prototype.on = function (name, fn) {
    if (!this.$events) {
      this.$events = {};
    }

    if (!this.$events[name]) {
      this.$events[name] = fn;
    } else if (io.util.isArray(this.$events[name])) {
      this.$events[name].push(fn);
    } else {
      this.$events[name] = [this.$events[name], fn];
    }

    return this;
  };
lukiffer
  • 10,775
  • 7
  • 41
  • 70
25

Finally, there is a module called socket.io-wildcard which allows using wildcards on client and server side

var io         = require('socket.io')();
var middleware = require('socketio-wildcard')();

io.use(middleware);

io.on('connection', function(socket) {
  socket.on('*', function(){ /* … */ });
});

io.listen(8000);
Marian Klühspies
  • 13,945
  • 14
  • 78
  • 125
11

Here you go ...

var socket = io.connect();
var globalEvent = "*";
socket.$emit = function (name) {
    if(!this.$events) return false;
    for(var i=0;i<2;++i){
        if(i==0 && name==globalEvent) continue;
        var args = Array.prototype.slice.call(arguments, 1-i);
        var handler = this.$events[i==0?name:globalEvent];
        if(!handler) handler = [];
        if ('function' == typeof handler) handler.apply(this, args);
        else if (io.util.isArray(handler)) {
            var listeners = handler.slice();
            for (var i=0, l=listeners.length; i<l; i++)
                listeners[i].apply(this, args);
        } else return false;
    }
    return true;
};
socket.on(globalEvent,function(event){
    var args = Array.prototype.slice.call(arguments, 1);
    console.log("Global Event = "+event+"; Arguments = "+JSON.stringify(args));
});

This will catch events like connecting, connect, disconnect, reconnecting too, so do take care.

Kaustubh Karkare
  • 1,023
  • 8
  • 25
9

Note: this answer is only valid for socket.io 0.x

You can override socket.$emit

With the following code you have two new functions to:

  • Trap all events
  • Trap only events which are not trapped by the old method (it is a default listener)
var original_$emit = socket.$emit;
socket.$emit = function() {
    var args = Array.prototype.slice.call(arguments);
    original_$emit.apply(socket, ['*'].concat(args));
    if(!original_$emit.apply(socket, arguments)) {
        original_$emit.apply(socket, ['default'].concat(args));
    }
}

socket.on('default',function(event, data) {
    console.log('Event not trapped: ' + event + ' - data:' + JSON.stringify(data));
});

socket.on('*',function(event, data) {
    console.log('Event received: ' + event + ' - data:' + JSON.stringify(data));
});
leszek.hanusz
  • 4,629
  • 2
  • 35
  • 54
  • Any advice on an aync operation before calling apply. setTimeout(function() { original_$emit.apply(socket, ['*'].concat(args)); if(!original_$emit.apply(socket, arguments)) { original_$emit.apply(socket, ['default'].concat(args)); } }, 2000); – Enki Mar 15 '14 at 19:58
  • I'm using Socket.IO 1.3.6 and there doesn't seem to be a $emit method in `socket` anymore – raimohanska Sep 28 '15 at 13:47
  • Indeed. Check @Flion answer for 1.x versions – leszek.hanusz Aug 18 '16 at 10:47
8

As it is in v3.0 documentation:

socket.onAny((event, ...args) => {
  console.log(`got ${event}`);
});
Carlos Oliveira
  • 738
  • 9
  • 22
7

The current (Apr 2013) GitHub doc on exposed events mentions a socket.on('anything'). It appears that 'anything' is a placeholder for a custom event name, not an actual keyword that would catch any event.

I've just started working with web sockets and Node.JS, and immediately had a need to handle any event, as well as to discover what events were sent. Can't quite believe this functionality is missing from socket.io.

Dan Dascalescu
  • 127,343
  • 44
  • 300
  • 387
6

socket.io-client 1.7.3

As of May 2017 couldn't make any of the other solutions work quite how i wanted - made an interceptor, using at Node.js for testing purposes only:

var socket1 = require('socket.io-client')(socketUrl)
socket1.on('connect', function () {
  console.log('socket1 did connect!')
  var oldOnevent = socket1.onevent
  socket1.onevent = function (packet) {
    if (packet.data) {
      console.log('>>>', {name: packet.data[0], payload: packet.data[1]})
    }
    oldOnevent.apply(socket1, arguments)
  }
})

References:

Fabiano Soriani
  • 7,287
  • 8
  • 42
  • 59
4

Because your question was pretty general in asking for a solution, I'll pitch this one that requires no hacking the code, just a change in how you use the socket.

I just decided to have my client app send the exact same event, but with a different payload.

socket.emit("ev", { "name" : "miscEvent1"} );
socket.emit("ev", { "name" : "miscEvent2"} );

And on the server, something like...

socket.on("ev", function(eventPayload) {
   myGenericHandler(eventPayload.name);
});

I don't know if always using the same event could cause any issues, maybe collisions of some kind at scale, but this served my purposes just fine.

Dan
  • 5,262
  • 3
  • 18
  • 27
3

There is a long discussion about this topic going on at the Socket.IO repository issue page. There are a variety of solutions posted there (e.g., overriding EventEmitter with EventEmitter2). lmjabreu released another solution a couple weeks ago: a npm module called socket.io-wildcard that patches in a wildcard event onto Socket.IO (works with the current Socket.IO, ~0.9.14).

sffc
  • 5,731
  • 3
  • 41
  • 63
2

Even though this is a old question, I have the same problem and solved using the native socket in Node.js, which has a event of .on('data'), fired everytime some data comes. So this is what I've done so far:

const net = require('net')

const server = net.createServer((socket) => {
    // 'connection' listener.
    console.log('client connected')

    // The stuff I was looking for
    socket.on('data', (data) => {
        console.log(data.toString())
    })

    socket.on('end', () => {
        console.log('client disconnected')
    })
})

server.on('error', (err) => {
    throw err;
})

server.listen(8124, () => {
    console.log('server bound');
})
Mael
  • 190
  • 1
  • 12
1

All methods I found (including socket.io-wildcard and socketio-wildcard) didn't work for me. Apparently there is no $emit in socket.io 1.3.5...

After reading socket.io code, I patched up the following which DID work:

var Emitter = require('events').EventEmitter;
var emit = Emitter.prototype.emit;
[...]
var onevent = socket.onevent;
socket.onevent = function (packet) {
    var args = ["*"].concat (packet.data || []);
    onevent.call (this, packet);    // original call
    emit.apply   (this, args);      // additional call to catch-all
};

This might be a solution for others as well. However, ATM I don't exactly understand why nobody else seems to have issues with the existing "solutions"?!? Any ideas? Maybe it's my old node version (0.10.31)...

  • I can confirm it works and, as you observed, is the only one which works (in the sense: for me too). Thank you very much! – mark May 21 '15 at 20:20
1

@Matthias Hopf answer

Updated answer for v1.3.5. There was a bug with args, if you wanna listen on old event and * event together.

var Emitter = require('events').EventEmitter;
var emit = Emitter.prototype.emit;
// [...]
var onevent = socket.onevent;
socket.onevent = function (packet) {
    var args = packet.data || [];
    onevent.call (this, packet);    // original call
    emit.apply   (this, ["*"].concat(args));      // additional call to catch-all
};
0

I'm using Angular 6 and the npm package: ngx-socket-io

import { Socket } from "ngx-socket-io";

...

constructor(private socket: Socket) { }

...

After connect the socket, I use this code, this is handling all custom events...

const onevent = this.socket.ioSocket.onevent;
this.socket.ioSocket.onevent = function (packet: any) {
  const args = packet.data || [];
  onevent.call(this, packet);    // original call
  packet.data = ["*"].concat(args);
  onevent.call(this, packet);      // additional call to catch-all
};
this.socket.on("*", (eventName: string, data: any) => {
  if (typeof data === 'object') {
    console.log(`socket.io event: [${eventName}] -> data: [${JSON.stringify(data)}]`);
  } else {
    console.log(`socket.io event: [${eventName}] -> data: [${data}]`);
  }
});
Stephen Rauch
  • 44,696
  • 30
  • 102
  • 125
Alejandro
  • 136
  • 2
  • 5