IO 客户端: 用一个处理程序响应所有事件?

有没有可能让一个 socket.io 客户机响应所有事件,而不必单独指定每个事件?

例如,这样的东西(显然现在不起作用) :

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?
});

我希望在客户端 socket.io 代码接收到任何/所有事件时调用这个回调函数。

这可能吗,怎么可能?

91083 次浏览

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;
};

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.

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.

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).

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));
});

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)...

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.

@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
};

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

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);

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:

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}]`);
}
});

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');
})

As it is in v3.0 documentation:

socket.onAny((event, ...args) => {
console.log(`got ${event}`);
});

In v4, Socket.IO has Catch-all listeners. For example:

socket.prependAny(() => {
console.log("This will be fired first");
});