Socket.io emit looping - node.js

I would like to be consistent with my socket.io "hooks". I use this format below pretty consistently, and it does not appear to fall into an infinite loop.
// inside my nodejs app
socket.on('foo', function (data) {
socket.emit('foo');
});
It only runs once when the hook is triggered from the client side, so it appears to be safe. But why would it not go in an infinite loop in the server side ? is this by accident or design ?

This behavior is by design, not a coincidence or accident
client: emit a message to server with a method
server: receive message from client with an event handler
server: send a message to client with a method
client: receive a message from server with an event handler
server socket receive a message from client
client.socket.emit('foo',aMessage) on foo event
socket.on('foo', function (data) {
//then server emit a message to connected clients
//not to server this is why not go through an infinite loop
socket.emit('foo');
});
then 'client' receive the message from server
chat.socket.on('foo', function(data){
console.log('got new message from server...',data)
})

Related

How many times can Socket.io 'connection' and 'disconnection' events be fired for single client?

EDIT: I see that I'm getting ping timeout and transport error reasons in my handler for disconnect on the server. This makes it difficult to maintain state in my server (I'm trying to keep track of which users are connected in a chat-like setup(. I was reading that it may be related to background tabs in Chrome (which I'm running). Does anyone have any experience with these 'spurious' disconnect events?
I'm new to Socket.io and am having some trouble understanding the connection and disconnection process.
As I understand it, the server receives the connection event once when a client connects, and one registers all the handlers for that client in the callback on on.('connection'). Is that true?
I want to maintain an of connected users, so I add a user to that array on the connection handler.
Should I then listen for the disconnect event to know when to remove a user from that array? Can I be guaranteed that that event will only be fired once?
It's a bit confusing, because on the client side, there is the connect event, which apparently can be fired multiple times -- the documentation says
// note: you should register event handlers outside of connect,
// so they are not registered again on reconnection
which is a different paradigm than on the server side, where all the handlers are registered inside the connection handler. But if the client-side connect event can fire on re-connection, what is the reconnect event for? (The docs says this event is "Fired upon a successful reconnection.")
In general I'm confused about the process of connection, disconnection and re-connection and how this relates to events, whether it happens "randomly" due to connection issues or only under the programmer's control, and how many times one should anticipate receiving each of these events -- once only for server, multiple times for client?
Thanks for any help!
I'm new to Socket.io and am having some trouble understanding the
connection and disconnection process.
Welcome to the wonderful world of Node.js + Socket.io It's super powerful!
As I understand it, the server receives the connection event once when
a client connects, and one registers all the handlers for that client
in the callback on on.('connection'). Is that true?
Correct. Take a look at this example of my code:
Server-side
var clients = []; /* stores all sockets on the fly */
io.on('connection', function (socket) {
clients[socket.id] = socket; /* keeps an array of sockets currently connected */
socket.on('disconnect', function (data) {
console.log(socket.id + " disconnected");
delete clients[socket.id];
});
});
Client-side
socket = io.connect(YOUR_SOCKET_URI, { transports: ['websocket'] } );
socket_delegates();
socket_delegates = function() {
// Socket events
socket.on('connect', function(data) {
/* handle on connect events */
});
socket.on('disconnect', function () {
/* handle disconnect events - possibly reconnect? */
});
socket.on('reconnect', function () {
/* handle reconnect events */
});
socket.on('reconnect_error', function () {
/* handle reconnect error events - possible retry? */
});
}
Should I then listen for the disconnect event to know when to remove a
user from that array? Can I be guaranteed that that event will only be
fired once?
Yes. You will see in the above server code that we listen for disconnect and then do what we need to.
Nothing should be random. You should have code in place to handle the connect, disconnect on the server side and code to handle the connect, disconnect and reconnect on the client side.

Socket.IO Emit is emitting to all clients

So I'm writing an app in NodeJS, and to preface my question please understand how my setup currently works:
I have Clients who connect via Socket.io to "Server X", and then my "Server X" connects via Socket.io to "Server Y", and they all send data back to each other via Socket.io.
Client <---> Server X <---> Server Y
Clients refer to users and their browsers, and then I have a node app running on both my server x and my server y.
So in the code below on my Server X, if you look at line 4 it works exactly as it should. It emits the message ONLY to the client who requested it.
io.on('connection', function(socket){
// This works just fine.
socket.emit('return_login', 'Test Message');
socket.on('login', function(data){
// This line correctly sends the data to Server Y
server_y.emit('login', data);
});
server_y.on('return_login', function(data){
// This emits to all connected clients???
socket.emit('return_login', data);
});
});
Now my problem is when "Server Y" emits return_login to server x, what I want to happen is for server x to take the value emitted by server y and just send it back to the original client, or web browser. But for some reason that line emits to ALL connected clients.
I have a laptop, this computer and my phone all testing this and every time that emit happens it sends to EVERYONE.
If someone could please help me with this I would greatly appreciate it. If I need to post more code please let me know.
I am not sure about your code. But I usually use room to emit to an user and callback function of socket. This is my solution using callback instead of return_login event
io.on('connection', function(socket){
socket.on('login', function(data, callback){
// Using callback instead of return_login event
server_y.emit('login', data, function(responseData){
// put this socket to room as name = user_id
socket.join(responseData.user_id);
// return result by callback
callback(responseData)
});
});
});
// emit to exactly user_id with return_login event if you want
io.to(user_id).emit('return_login', {key: 'ok'})
It is sending to all clients because you have installed a separate listener for each socket for the return_login message. So, when one client logs in and the return_login message is sent back to your server, you have a separate listener for every single socket that sees that message and forwards it to that socket. In this way, it gets sent to every connected socket.
One way to fix that is to make sure that the return_login message is only sent to the socket that it belongs to. If you can send a socket.id with that message and have that server echo that id back as part of the response, then you can check that when you receive the message to make sure you only send it to the socket that it belongs to.
This is one of the issues with a pure message-based system. You are trying to do a request/response where only the requester sees the response, but socket.io isn't a request/response system. A response is sent to all listeners of that particular message and since you have a listener for that message for every single socket that is connected, every single socket is seeing it and then forwarding it on to it's client.
So, with a corresponding modification to the other server to echo back the id value, you could do this:
io.on('connection', function(socket){
// This works just fine.
socket.emit('return_login', 'Test Message');
socket.on('login', function(data){
// add our id so we can identify our response back
data.id = socket.id;
server_y.emit('login', data);
});
let fn = function(data) {
// check to see if this message is destined for this socket
if (data.id === socket.id) {
// make data copy with id value removed so
// we don't send that to the client
let tempData = Object.assign({}, data);
tempData.delete(id);
socket.emit('return_login', tempData);
// we're done with this listener so remove it
server_y.removeListener('return_login', fn);
}
});
server_y.on('return_login', fn);
});
It might be tempting to just remove your listener for the return_login message after you receive it, but that causes a race condition if two clients happen to both be in the process of logging in at the same time and thus both have listeners at the same time, then the first message would be received by both listeners.

How to Connect WSO2CEP with Socket.io?

I want to create an Event Publisher that connect via Websocket. When I try to connect it with my simple socket io server, the url is
ws://localhost:3000/socket.io/
It didn't receive the stream..
I've set the inline format for the stream like this :
42["input-message",{"LAT":{{latitude}},"LON":{{longitude}}}]
If I understand your question correctly,
you do not get any errors when the event is published from the CEP
server
but the socket io server does not show any indication that it
received the event either.
CEP server showing no error logs means:
CEP server is successfully connected to the socket io server. (if the connection is dropped, then you should see an error log, and CEP will try to reconnect)
Probably the event was sent to socket io server by Websocket publisher (or the Websocket publisher did not recieve any event at all to be sent to socket io server)
(When you send an event, if the CEP server cannot parse the event, then also you should see an error log.)
Here are some points which might help you to troubleshoot the issue:
Enable tracing in your websocket publisher (You may refer to this 'Event Tracer' doc). Then send an event and check the traces. This will allow you to verify whether the Websocket publisher recieved the event.
If there are traces shown for the publisher, but still no event received at the socket io server, then it could be that some error occurs at socket io server, and the exception is not logged (might have being swallowed).
Hope this will help.
Because I cannot directly connect to the socket.io, thus I created a simple websocket that act as a middleware that sending the input from WSO2CEP into the socket.io
var io = require('socket.io').listen(server);
io.set('origins', '*:*');
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 8087 })
//wss sending every message that it received to the socket.io
wss.on('connection', function connection(ws) {
console.log('a WSO2CEP-PUBLISHER is connected');
ws.on('message', function incoming(message) {
console.log('received: %s', message);
io.emit('input-message', JSON.parse(message));
});
});
notice that, the data that come from event publisher is string formatted, so if needed to send it as a JSON object, than use JSON.parse() function.

Verify that the client has received the message from the server

In a chat service that I'm building, I need to send messages directly from the server.
I have found no solution, there is an example in the documentation:
// SERVER
io.sockets.on('connection', function (socket) {
socket.on('ferret', function (name, fn) {
fn('woot');
});
});
// CLIENT
socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
socket.emit('ferret', 'tobi', function (data) {
console.log(data); // data will be 'woot'
});
});
but does the opposite of what I need!
I need to do the emit from the server and receive a confirmation of receipt from the client!
Is there a way to do this?
there is no guarantee as the connection can be killed before the servers message reaches the client. thus there is also no event on the server like "clientGotMessage". if a message MUST reach the user there is no other way than to tell the server that you received the message on the client.
You can do this 'easy' by sending a number down. client and server keep track of that number. each time the server sends, it counts up, each time the client receives, it counts up. When the client sends something, it sends the number, so the server will see if the client has everything. If the client missed a message, the next message will have a number that the client wont accept and request the lost message from the server.

node.js + socket.io broadcast from server, rather than from a specific client?

I'm building a simple system like a realtime news feed, using node.js + socket.io.
Since this is a "read-only" system, clients connect and receive data, but clients never actually send any data of their own. The server generates the messages that needs to be sent to all clients, no client generates any messages; yet I do need to broadcast.
The documentation for socket.io's broadcast (end of page) says
To broadcast, simply add a broadcast flag to emit and send method calls. Broadcasting means sending a message to everyone else except for the socket that starts it.
So I currently capture the most recent client to connect, into a variable, then emit() to that socket and broadcast.emit() to that socket, such that this new client gets the new data and all the other clients. But it feels like the client's role here is nothing more than a workaround for what I thought socket.io already supported.
Is there a way to send data to all clients based on an event initiated by the server?
My current approach is roughly:
var socket;
io.sockets.on("connection", function (s) {
socket = s;
});
/* bunch of real logic, yadda yadda ... */
myServerSideNewsFeed.onNewEntry(function (msg) {
socket.emit("msg", { "msg" : msg });
socket.broadcast.emit("msg", { "msg" : msg });
});
Basically the events that cause data to require sending to the client are all server-side, not client-side.
Why not just do like below?
io.sockets.emit('hello',{msg:'abc'});
Since you are emitting events only server side, you should create a custom EventEmitter for your server.
var io = require('socket.io').listen(80);
events = require('events'),
serverEmitter = new events.EventEmitter();
io.sockets.on('connection', function (socket) {
// here you handle what happens on the 'newFeed' event
// which will be triggered by the server later on
serverEmitter.on('newFeed', function (data) {
// this message will be sent to all connected users
socket.emit(data);
});
});
// sometime in the future the server will emit one or more newFeed events
serverEmitter.emit('newFeed', data);
Note: newFeed is just an event example, you can have as many events as you like.
Important
The solution above is better also because in the future you might need to emit certain messages only to some clients, not all (thus need conditions). For something simpler (just emit a message to all clients no matter what), io.sockets.broadcast.emit() is a better fit indeed.

Resources