How to use socket event from another function? - node.js

In socket.io we create a instance and use socket like this
var server = require('http').createServer();
var io = require('socket.io')(server);
io.on('connection', function(socket){
socket.on('event', function(data){});
socket.on('disconnect', function(){});
});
server.listen(3000);
How we can create a socket event in other function like this. Is there any way or trick to this properly.
var server = require('http').createServer();
var io = require('socket.io')(server);
io.on('connection', function(socket){
socket.on('event', function(data){});
socket.on('disconnect', function(){});
});
var f1 = function (socketID) {
if(socketID){
io.to(socketID).emit('event1', 'event1 data'); //working
socket.on('call2', function (data) { // not working
});
}
}
server.listen(3000);

When you are sending a request and trying to wait for a specific response to that request, it is bit tricky to use a message-based protocol like socket.io for that situation. HTTP is perfectly designed for request/response since a single connection is used only for that one request/response and therefore the response is the response that belongs to your request.
This is not true with socket.io. It's a message based scheme. When you send a message and then wait for a message back from the other end (in a multi-user environment), you have to be very careful that you don't:
Install too many event listeners and end up with duplicates.
That the response you're getting is specifically for your original request and not for some other request that might be happening in a similar time frame.
One way to solve this issue is to add a request identifier to all your request/response messages. When you send the request, you add a unique request ID. When the response is returned, it includes that request ID and then your code can tell which response belongs with which request.
Here's an idea for how to implement the request ID concept (this needs the other end to cooperate and return the same request ID that it was sent):
var idCntr = 0;
function f1(socketID) {
if(socketID){
var id = idCntr++;
io.to(socketID).emit('event1', {id: id, data: 'event data'});
function listener(data) {
// if this is our message
if (data.id === id) {
// once our response is received, remove event handler
socket.removeListener('responseToEvent1', listener);
// process response here
// code here...
}
}
// wait for our response
socket.on('responseToEvent1', listener);
}
}
Or, here's a more object oriented scheme that adds a method to the socket object called .emitRequest():
var idCntr = 0;
socket.emitRequest = function(msg, data, responseMsg, responseFn) {
var id = idCntr++;
function listener(response) {
// if this is our message
if (response.id === id) {
// once our response is received, remove event handler
this.removeListener(responseMsg, listener);
// call response handler
responseFn(response.data);
}
}
this.emit(msg, {id: id, data: data});
this.on(responseMsg, listener);
}
Note: With both of these pieces of code, the other end of your connection needs to cooperate and return the id property of the original request so that the receiving listener can tell which response goes with which request.

Related

Node.js how to find and stop socket connection

I am using socket.io in one of express route to populate data from server and send it to on client with different event names, firstsocket and secondsocket.
var io
router.get('/start', function(req, res, next) {
io = req.app.get('socketio');
var socketlist = [{name:"firstsocket"},{name:"secondsocket"}]
for (var key in socketlist){
myInterval = setInterval(function(){
JsonObj.marks = socketlist[key].name+' '+parseInt(Math.random()*100);
io.emit(socketname, JsonObj);
}, 2000);
}
});
it is working and i can see the data on client console for these to socket events. Now i want to stop one of the socket event based on user button click. I am not getting idea to list and stop sockets.
router.get('/stop', function(req, res) {
if(req.socketname=='firstsocket') {
//stop and destroy firstsocket
}
if(req.socketname=='secondsocket') {
//stop and destroy secondsocket
}
res.redirect('/');
});
please suggest, how to identify and stop one socket session out of multiple sessions.
Thanks
If you store the socket.id information of each socket within your
var socketlist = [{name:"firstsocket"},{name:"secondsocket"}]; array,
then you would be able to close a socket connection by doing;
io.sockets.sockets[socketlist[req.socketname].socket_id].disconnect(true);
Though, I would recommend you to check if
io.sockets.sockets[socketlist[req.socketname].socket_id]
returns undefined before accessing it's disconnect property.
If you try to access disconnect property of an already closed socket, the code above will return undefined and you will have an error thrown saying
"Can't access the property disconnect of undefined"
Final code should be something like this;
if (io.sockets.sockets[socketlist[req.socketname].socket_id]) {
io.sockets.sockets[socketlist[req.socketname].socket_id].disconnect(true);
}
You can get the socket.id information of a socket on the initial connection to the server.
I'd advise you to move your io decleration out of the route and declare it like this with your socket list;
var io = require('socket.io')(server);
var socketlist = [], first_socket_connected = false;
The server variable should be your express server.
Then, use the code below to receive socket connections and push to your socket list;
io.on("connection", function(socket) {
socketlist.push({name: first_socket_connected ? "secondsocket" : "firstsocket", id: socket.id});
first_socket_connected = true;
});

Socket.io add listeners in Express 4 routes

I declare socket server in separate module. I have access to the object of the server everywhere in application, for example I can emmit. But I can't add listener in the route, for example:
router.post('/example', function(req, res, next) {
var socketio = req.app.get('sock');
socketio.sockets.on('connection', function (socket) {
socket.on('message', function (text) {
console.log('ok');
});
});
Is there any way to do this?
If there is just a single device that you want to communicate with and it should have already connected to your server and req.app.get('sock') is how you get access to the socketio server object, then you can do it like this:
var theDeviceSocket;
req.app.get('sock').on('connection', function(socket) {
theDeviceSocket = socket;
});
router.post('/example', function(req, res, next) {
if (theDeviceSocket) {
theDeviceSocket.emit("someMsg", "someData");
}
// send whatever response you want to send
res.end();
});
If you were trying to get a response from the single device and return that as the response to the POST, then you could so something like this:
// store the one connection to/from the special device
var theDeviceSocket;
// keep a request cntr so we can tell which response goes with which request
var requestCntr = 0;
req.app.get('sock').on('connection', function(socket) {
theDeviceSocket = socket;
});
router.post('/example', function(req, res, next) {
var timer, thisRequestId;
function gotData(data) {
// if this is our specific response
if (data.requestId === thisRequestId) {
theDeviceSocket.removeListener("someMsgResponse", gotData);
res.send(data);
clearTimeout(timer);
}
}
if (theDeviceSocket) {
theDeviceSocket.on("someMsgResponse", gotData);
thisRequestId = requestCntr++;
theDeviceSocket.emit("someMsg", {requestId: thisRequestId});
// set up a timeout in case we don't get the proper response
timer = setTimeout(function() {
theDeviceSocket.removeListener("someMsgResponse", gotData);
res.send("error");
}, 5000);
}
});
This second scheme is complicated by the fact that you have architected this to be a request/response scheme, but socket.io is not a request/response protocol. So, in order to know which response belongs to which request (when there are potentially multiple clients interacting with the server at the same time), you have to implement some sort of requestId in the data you send and receive over socket.io. This means you have to change the device to echo back the requestId you sent it with its response. All of this would not be necessary if you used a protocol that is designed for request/response like HTTP instead of socket.io.

Socket.io 1.0 Middleware join room not working properly

I tried to add new socket to some rooms in a middleware, but it seems not working while a first emit haven't be done for a socket(client side). When a socket (client side) send a 'message' event it will then work and be part of the room.
Is it a normal behavior?
Am I mandatory to join room in 'connection' event?
app.js (server side)
var app = require('http').createServer(function (req, res){
res.end('no rest');
});
var io = require('socket.io')(app);
app.listen(7076);
io.use(function(socket, next){
socket.join('toto');
next();
});
io.on('connection', function (socket) {
socket.on('message', function (data) {
socket.to('toto').emit('message', data);
});
});
According to the documentation, socket.to('toto').emit... syntax is not correct. You should use one of the following forms:
send everyone in "toto" room:
io.to('toto').emit('message', data);
send everyone in "toto" room except the sender:
socket.broadcast.to('toto').emit('message', data);
In fact the problem wasn't on the server side at all... It was my client that i didn't describe.
When clicked on a button to send a message here is the function called
function messageManagement(cb)
{
var message = $('#message_text').val();
if (!message || message.length == 0)
message = 'I am watching you';
socketClient.emit('message', {message:message});
socketClient.on('message', function (data){
console.log(data.messsage);
drawMessage(data);
});
}
As you can see each time the client emit a message it also register to the response event. So each time I emit a message a registered one more time to same event... It was messy so i changed this and it worked..

Socket.io broadcasts everything to everybody in node/express app

I wondered if someone could help figure out what I am doing wrong:
My client web page initiates a connection with my server, and listens to a long running process whose state is getting updated in the db by a worker process on another thread, emitting updates back to the browser. I define a socket.io connection in the app.post() method. This is handled by the poll() function below (scroll down a bit past the invite checking code)
However, when a new web client connects, it's messages get added to the previous client's as if there were just one channel. Why isn't there a separate unique channel for each browser?
//Create server
var express = require('express'),
app = express(),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server);
io.set('log level', 1); // reduce logging
io.configure(function () {
io.set("transports", ["xhr-polling"]);
io.set("polling duration", 10);
});
app.post('/api/users', function (req, res) {
if (!req.body.auth.accessToken) {
req.body.auth.accessToken = req.body.auth.authResponse.accessToken;
} //fb return object is different depending on whether it is a first login or subsequent
logger.log('debug', '/api/users:POST', req.body);
io.sockets.on('connection', function (socket) {
socket = socket;
socket.emit('update', {
status: 200 //send initialization ping
});
//check if user has valid invite, if not try to invite
db.getTotalUserInvites(function (err_inv, res_total) {
db.getUserInvite(req.body.fid, function (err_check, res_check) {
logger.log('debug', 'Total invites issued=' + res_total);
//process report - all we need is accesToken, processReport will do the rest
mine_fb.processUser(req.body.auth.accessToken, socket, function (User,socket) { //pass channel properly
db.getReportStatus(User.fid,socket, function (result,socket) {
logger.log('debug', 'report status', result);
if (result) {
if (socket && (result.report_status == -1)) {
logger.log('debug', 'report already processed. retrieving uniq_id ' + result.uniq_id);
socket.emit('update', {
status: -1,
uniq_id: result.uniq_id
});
return true;
} else {
if (socket && (result.report_status >= 0)) {
logger.log('debug', 'we are in the middle of processing report ' + result.uniq_id);
//in this case we become a listener and not a speaker
function poll(socket) {
db.getReportStatus(User.fid, socket,function (r,socket) {
socket.emit('update', { //!!!! THIS EMITS TO ALL CONNECTED BROWSERS
status: r.report_status,
uniq_id: r.uniq_id
}); //...socket
if ((r.report_status >= 0) && (socket)) {
logger.log('debug', 'polling...');
_.delay(poll, 2000, socket);
}
}); //get rerpot
}; //end poll
socket.on('disconnect', function () {
socket=null;
});
poll(socket);
} // else we're in the middle
} //done checking status
} //end of seq
});
return res.send();
});
});
});
});
});
While it is not clear how to help you I can tell what's going on in your code:
app.post('/api/users', function (req, res) {
// some code
io.sockets.on('connection', function (socket) {
// some code
});
});
Whenever a user POSTs something to /api/users a new handler is attached to io.sockets (that's what .on does). But these handlers are never removed, so each time a new connection is established all attached handlers fire. That's where your broadcasting comes from.
You have to separate app.post(...) from io.sockets.on('connection',...) (they should be independent, both defined at module level, not nested). I'm sure it won't be easy (you will probably have to authenticate a user twice for example) but that's the only reasonable way.
You shouldn't put your io.sockets.on('connection', function (socket) inside the app.post scope.
Just put it outside and try again, it will probably work correctly.
Listening to connexion should be done once when the server starts, not each time a client hits some URL.

Socket.io : How do I handle all incoming messages on the server?

I want to be able to handle all messages that are coming in from clients in a single handler.
Example client code:
var socket = io.connect('http://localhost');
socket.emit('news', { hello: 'test' });
socket.emit('chat', { hello: 'test' });
Example server code:
io.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
console.log(data);
}); });
I'd like to be able to log every message even if its sent on news, chat or whatever other name using emit. Is this possible?
Note: The above server code does not work. There is nothing currently logged. I am just wondering if there is a single event which could be handled for all messages for every emit name.
That is possible by overriding socket.$emit function
//Original func
var x = socket.$emit;
socket.$emit = function(){
var event = arguments[0];
var feed = arguments[1];
//Log
console.log(event + ":" + feed);
//To pass listener
x.apply(this, Array.prototype.slice.call(arguments));
};
It's even easier on Socket.Io >3 using the socket.onAny(listener):
this.socket.onAny(m => {
..
});
This is supported out of the box now as of Socket-io 2.0.4, you simply need to register a middle ware (source from socketio-wildcard):
As of Socket.io v2.0.4 (commit), you can use a socket middleware to
catch every incoming Packet, which satisfies most of
socketio-wildcard's use cases.
io.on('connection', (socket) => {
socket.use((packet, next) => {
// Handler
next();
});
});
This is an open issue with Socket.IO.
At this time, if you really need it, you will probably have to fork Socket.IO. See 3rd-Edens comment for how to do it.

Resources