NodeJS, Express, and eventful Socket.io - node.js

I want to build a NodeJS API so that when I hit an endpoint, the app will trigger an event that will cause its unique socket connection to emit a message to its listeners. I have built a solution before using Python/Django, Redis, and NodeJS/Socket.io with Django as the API and Redis as the 'event trigger', but I would like to consolidate the different technologies into NodeJS and Socket.io.
I tried moving the socket.emit() code into different modules and then app.use()'d those modules, but the code broke because it didn't have an instance of the socket.
I also know that you can broadcast to all socket connections inside on an endpoint, for example:
app.use('socket.io/help', function(req, res) {
console.log(req.query);
io.sockets.emit('help_message', 'You should help me.');
res.send('help msg sent');
});
But I am looking for a way that allows a client (that doesn't have a socket connection) to hit an endpoint and pass a query param that tells NodeJS which of its connected sockets to send a message to.
Is this possible? Or am I trying to fight the framework? e.g., is there a different way of doing this with different JS WebSocket frameworks/technologies?

I have been stuck on same situation but resolved easily
you have created socket on app.js
server = require('http').createServer(app),
io = require('socket.io').listen(server);
server.listen(port);
global.socketIO = io;
Now you can call this io instance to your any controller like
app.use('socket.io/help', function(req, res) {
console.log(req.query);
var io = global.socketIO;
// UID IS THE VARIABLE
io.sockets.emit('help_message'+UID, 'You should help me.');
res.send('help msg sent');
});
CLIENT SIDE
<script src="/socket.io/socket.io.js"></script>
<script>
window.socket.on("help_message_<%-UID%>", function(msg){
//// ACTION
});

You can join a room with the specific sockets you want to recieve the messages on.
see Rooms & Namespaces in the socket.io documentation
join a chan on your helpdesk conenctions:
io.on('connection', function(socket){
socket.join('helpdesk');
});
and broadcast to them:
app.use('socket.io/help', function(req, res) {
console.log(req.query);
var io = global.socketIO;
io.sockets.emit('adduser', req.body.uid);
io.to('helpdesk').emit('some event'):
res.send('help msg sent');
});

Related

Trigger `socket.io` event from server(`node.js`), not from client

I am trying to make a game server with node.js, socket.io.
The basic idea likes below.
Initialize socket.io instance when the server starts
Store instance in global scope, so controllers can access it
When API calls, we trigger some socket.io event in the controller or some other points
Here is the implementation I made ...
First, in server.js - entry point
let GlobalVars = require('./state/GlobalVars');
const apiRouters = require('./router');
...
app.use('/api', apiRouters);
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/test/simpleClient.html`)
});
const httpServer = http.createServer(app);
let socketIOInstance = socketIO(httpServer);
socketIOInstance.on('connection', (socket) => {
console.log('SOCKET.IO A USER CONNECTED');
socket.on('create', (data) => {
console.log('SOCKET.IO create called', socket);
socket.join(data.room);
socketIOInstance.emit('message', 'New people joined');
});
socket.on('join', (data) => {
console.log('SOCKET.IO join called', data);
})
socket.emit('message', 'Hi');
});
GlobalVars.socketIO = socketIOInstance;
// Add to global, so the controllers can manage own actions like create, join ...
httpServer.listen(port, () => {
console.log(`Server Listening on the port ${port}`);
})
...
When I access from a client, I am able to see SOCKET.IO A USER CONNECTED and Hi in the browser console.
Second, In api controller.
let GlobalVars = require('../state/GlobalVars');
...
router.post('/create', (req, res) => {
console.log('GenerateGameSokect');
let game = new Game();
let gameId = game.gameId;
// console.log('Global vars ', GlobalVars.socketIO);
GlobalVars.socketIO.emit('create', {
room: gameId
});
res.json({
result : 'SUCCESS',
game : game
})
});
I imported GlobalVars which contains socketIO instance. So what I expected was, socket create event triggered from the statement GlobalVars.socketIO.emit('create', Object) but could not find message in the server logs.
I got no clue what I was missing.
The final form I pursue is something like...
When user call create API, I creates socket connection and room
API will called in HTTP protocol, but in the API, the server publishes some events. - pubsub like.
Thanks for reading my questions b. Here is full source code till now(bitbucket public)
================== EDIT ====================
I got understood (maybe...)
The user-flow I wanted was ...
The client call API
(In the server) Checking validation in API and if valid emit to socket.io
If event accepted send new status to all clients
However, creating socket.io connection in the server looks strange for me, the solution is up to the client.
New user-flow I will change
The client call a validation API
If return is valid, the client emit socket.io event. This time server only do validation, not emit socket.io
In socket event, send new status to all other users
================== EDIT #2 ====================
This is a kind of conclusion. It looks I just misunderstanding the concept of socket communication. Like answer and replies say, Socket and HTTP are totally different channels, there is no way to connect both. (At least, without open new connection from http server to socket)
If this is wrong, you could add reply, Thanks
Now I understand you. Or at least I think!
Let's put it this way: there are two (asymetric) sides on a socket, server and client. What I called, respectively, "global manager" and "socket" in my comment to your post.
const server = require('socket.io')(yourHttpServer);
// client is installed as well when `npm i socket.io`
const client = require('socket.io-client')('http://localhost:' + yourServerPort);
// `socket` is the server side of the socket
server.on('connection', (socket) => {
// this will be triggered by client sides emitting 'create'
socket.on('create', (data) => {
console.log('a client socket just fired a "create" event!');
});
});
// this will be triggered by server side emitting 'create'
client.on('create', (data) => {
server.emit('create', {content: 'this will result in an infinite loop of "create" events!'});
});
In your /create route, when you GlobalVars.socketIO.emit('create', ...), the server-side socket handler isn't triggered, however if you have clients connected through a browser (or, like I showed above, if you connect a client socket directly from the server) then these will trigger their 'create' listener, if any.
Hope this helps you get on the right tracks!

NodeJs Socket.io Rooms

This is a pretty simple question but i want to make sure that i am scaling our socket.io implementation correctly. We are using socket.io to respond back to the client after a lengthy process on the nodejs backend. So basically client makes call, then socket.io signals the client that the process has completed. Also socket.io ONLY responds to a temporary room that was established for the request.
In nodejs i created a global variable for the following so that i could emit back to the client room:
global.io = require('socket.io')(server);
But to create the room itself I am a little unsure how to create it globally such that only the socket that connected and made the request receives the response.
So if i have 500 client machines that initiate a connection through socket.io, each one will have its own socket. To ensure that the rooms are unique i use a guid across all 500. Of course i do not want all sockets to receive traffic if only one socket for a specific room is supposed to be evaluating the emit....
any ideas?
If I understood your question correctly, you're looking to send information to that 1 socket?
Perhaps something like this:
socket.broadcast.to(socketId).emit('someeventname', eventData);
If you have the connection open with that client, that means you have their socket id through socket.id . You can emit events to just that socket.
const app = express();
var http = require("http");
var server=http.createServer(app).listen(2525, (req, res) => {
console.log("Server running on", 2525);
});
var socketIO = require("socket.io");
var io = socketIO(server);
global.io = io
io.on("connection", async (socket) => {
socket.on("joinrooms", async (data) => {
socket.join(data.userId);
});
socket.on("sendMessage", async (data) => {
console.log("message", data);
io.to(data.touserId).emit("sendMessage", data);
});
});
/* Must Read section
Joinrrom data sample
data={
userId:123 //User's unique id.
}
sendMessage data sample
data={
userId:123, //sender User's unique id.
touserId:456, //reciver User's unique id.
}
Here I'm creating a room from the user's unique id(stored in DB) so whenever I
want to send data to a particular user I will emit an
event("io.to(data.touserId).emit") using the user's
a unique id that way only specific users will get messages.
*/

Sending socket message when post request is received

I would like to emit a socket.io message to all connected clients when any client sends a post request. How can I keep the socket open so that my http request methods can access the connected socket.
I was able to get the following to work but if a client is not connected, the post method doesn't work.
io.on('connection', function(socket){
console.log('socket connected');
app.post('/api/guests', function(req, res){
socket.emit('newguest', {hello: 'world'});
});
})
I also tried saving the socket to a higher scope and even a global but that didn't seem to work either.
Thanks in advance!
You don't have to call the "socket emit" inside the on.("connection"...)
Try something like this:
app.post('/api/news', user.can('access private page'), function(req, res, next) {
io.sockets.emit("nuova:news", data);
});
In my case i pass the "io" variable from the "server.js" file (or app.js) like this:
require('./app/myRoute')(app, user, io);
And receive it in the controller like this:
module.exports = function(app, user, io) {
...
}
"io" is declared like this:
var wsServer = require('http').createServer(app);
var io = require('socket.io').listen(wsServer);
I hope these additional info can be useful to you...

Can't connect to socket after emit

I am working on project that involves real time temperatures and have a device sending temps via get that is routed through the server and emitted to the socket. Then I want the server to connect to the original socket and emit the data to a new one that is being read by my client.
Here is my app.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
server.listen(8080);
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.route('/:temp')
.get(function (req, res){
var temp = req.params.temp;
res.end(temp);
io.sockets.on('connection', function(socket){
socket.emit('send temp', temp);
});
});
io.sockets.on('connection', function(socket){
socket.on('send temp', function(data){
console.log('connected to send temp');//this never shows in the console
io.sockets.emit('new temp', data);
});
});
The route code in app.js works fine. When I hit localhost:3000/test and change the client to connect to 'send temp' (rather then 'new temp') 'test' is output.
Here is the relevant section of my client
var socket = io.connect();
var $temp = $('#temp');
socket.on('new temp', function(data){
$temp.html("Temp: " + "<br/>"+data);
});
I am running node version 4.1.2, socket 1.3.7 and express 4.10.8.
I am wondering why I cannot connect to the original socket a second time. Or that may not even be my problem. I have studied the many 'chat' tutorials and searched for others using trying to do what I want to without any success.
Ultimately what I am trying to have happen is have a client hit /:temp over and over with a real-time reading and then have other clients get that data in real-time.
This is all still a little new to me so any help is appreciated.
Your code example registers a message handler on the server for the 'send temp' message. The client registers a message handler for the 'new temp' message.
The two (client and server) are then sitting there in a stalemate waiting for someone to first send a message, but (per the code you've disclosed) nobody ever does.
I don't really understand what the intent is for your code, but I see several issues.
First off, you do not want to install a listener for the connection event inside this code:
app.route('/:temp')
.get(function (req, res){
var temp = req.params.temp;
res.end(temp);
io.sockets.on('connection', function(socket){
socket.emit('send temp', temp);
});
});
Why would you only start listening for a connection event when you get a particular route handler. And, why add yet another event handler every time that route is hit. This code is just completely wrong. I don't know what you thought you were trying to achieve with it, but it's not the way to do things.
Second off, this code is waiting for the client to send a 'send temp' message and when it gets one, it attempts to broadcast that to all clients. But, the part of your client you disclose never sends a 'send temp' message.
io.sockets.on('connection', function(socket){
socket.on('send temp', function(data){
console.log('connected to send temp');//this never shows in the console
io.sockets.emit('new temp', data);
});
});
Third off, please describe exactly what you're trying to accomplish in words so we can better know what code to recommend in order to do that.
EDIT
Now that you've described the actual problem here:
Ultimately what I am trying to have happen is have a client hit /:temp
over and over with a real-time reading and then have other clients get
that data in real-time.
It is a little easier to recommend a solution:
On the server:
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
server.listen(8080);
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.get('/:temp', function (req, res) {
var temp = req.params.temp;
res.end(temp);
// send this temperature to all connected clients
io.emit('new temp', temp);
});
On the client:
var socket = io.connect();
var $temp = $('#temp');
socket.on('new temp', function(data){
$temp.html("Temp: " + "<br/>"+data);
});

How do I connect two Socket.io servers/apps?

I'm trying to build an application that has two components. There's a public-facing component and an administrative component. Each component will be hosted on a different server, but the two will access the same database. I need to set up the administrative component to be able to send a message to the public-facing component to query the database and send the information to all the public clients.
What I can't figure out is how to set up a connection between the two components. I'm using the standard HTTP server setup provided by Socket.io.
In each server:
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
And on each client:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
I've looked at this question but couldn't really follow the answers provided, and I think the situation is somewhat different. I just need one of the servers to be able to send a message to the other server, and still send/receive messages to/from its own set of clients.
I'm brand new to Node (and thus, Socket), so some explanation would be incredibly helpful.
The easiest thing I could find to do is simply create a client connection between the servers using socket.io-client. In my situation, the admin server connects to the client server:
var client = require("socket.io-client");
var socket = client.connect("other_server_hostname");
Actions on the admin side can then send messages to the admin server, and the admin server can use this client connection to forward information to the client server.
On the client server, I created an on 'adminMessage' function and check for some other information to verify where the message came from like so:
io.sockets.on('connection', function (socket) {
socket.on('adminMessage', function (data) {
if(data.someIdentifyingData == "data") {
// DO STUFF
}
});
});
I had the same problem, but instead to use socket.io-client I decided to use a more simple approach (at least for me) using redis pub/sub, the result is pretty simple. My main problem with socket.io-client is that you'll need to know server hosts around you and connect to each one to send messages.
You can take a look at my solution here: https://github.com/alissonperez/scalable-socket-io-server
With this solution you can have how much process/servers you want (using auto-scaling solution), you just use redis as a way to forward your messages between your servers.

Resources