How to emit event in socket.io based on client? - node.js

I am working on realtime data visualization application using node.js, express and socket.io.
Requirement:
Have to emit the events based on the client request.
For example: If user enter the url as http://localhost:8080/pages socket.io should emit the topic pages to client and another user request for http://localhost:8080/locations socket should emit location to that particular user.
Code
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
io.on('connection', function (socket) {
config.socket = io.sockets.socket(socket.id);
socket.on('disconnect', function () {
console.log('socket.io is disconnected');
});
});
app.get('/*', function(req, res) {
var url = req.url;
var eventName = url.substring('/'.length);
//pages and locations
config.socket.volatile.emit(eventName, result);
});
Client Code:
//No problem in client code.Its working correctly.
Sample code as follows
socket.on('pages', function (result) {
console.log(result);
});
Problem:
It is emitting pages and locations to both the clients.
Any suggestion to overcome this problem.

I couldn't understand your approach on this, but because you said you're rendering different pages, It means you can serve different code, so what about doing it like this:
Server Side:
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
app.get('/pages', function(req, res) {
res.render('pages.html');
});
app.get('/locations', function(req, res) {
res.render('locations.html');
});
io.on('connection', function (socket) {
socket.on('pagesEvent', function(data){
socket.volatile.emit('pages', {your: 'data'});
});
socket.on('locationsEvent', function(data){
socket.volatile.emit('locations', {your: 'data'});
});
});
On Client side:
pages.html:
socket.on('connect', function(){
socket.emit('pagesEvent', {});
});
socket.on('pages', function(data){
// do stuff here
});
locations.html:
socket.on('connect', function(){
socket.emit('locationsEvent', {});
});
socket.on('locations', function(data){
// do stuff here
});

You are doing it wrong, WebSockets supposed to work same in both directions. Client emit event to Server, server emit back to Client/Subscribers.
The way you are doing things, seems like a way of implementing API, but for some reason you are trying to implement it with WebSockets, instead of XHR.

Related

Socket.IO duplicated message

I'm trying to build a chat application using Laravel, with node js server, socketio and redis. What I have is this:
Client JS:
var socket = io('http://localhost:3005');
var room = '17';
$("#send").click(function(){
content = $("textarea").val();
id =$("#id").val();
$.ajax({
url: "{{route('send.message')}}",
method: "POST",
data: {content, id, room},
success: function(){
}
});
});
socket.on('cacad', function(message){
console.log(message); //multiple copies here
});
socket.on('connect', function(){
console.log("Connected!");
socket.emit('room', room);
});
Laravel Controller:
public function sendMessage(Request $request){
event(new EventName($request->all()));
$message = new Message;
$message->message = $request->content;
$redis = LRedis::connection();
$redis->publish('chat-channel', json_encode($request->all()));
$message->save();
}
Node Server:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var redis = require('ioredis');
var myMessage;
var redis_client = redis.createClient();
redis_client.subscribe('chat-channel');
io.on('connection', function(socket) {
redis_client.on('message', function(channel, message) {
var myData = JSON.parse(message);
socket.broadcast.to('17').emit('cacad', 'u i u a a');
});
socket.on('room', function(room){
socket.join(room);
});
socket.on('disconnect', function(){
console.log("disconnected!");
});
});
http.listen(3005, function() {
console.log('Listening on Port 3005');
});
I am trying to broadcast a message u i u a a in room 17. But when I receive it in the console, it shows multiple copies of it, 2x-4x. In the Laravel controller I publish a message using redis and I subscribe to it in node server. It is received successfully, but the problem lies with the multiple copies of the message (client side).
Please where is it wrong? Thank you :)
I'm pretty convinced I found the issue although I had to search a bit about those simple API usage because I'm not using the library lately.
Looking at the docs it's explain your issue pretty clearly.
Here you are listening to a new connection,
io.on('connection', function(socket) {
If the client asked to join to a specific room, you join him:
socket.on('room', function(room){
socket.join(room);
So far it's like the docs:
io.on('connection', function(socket){
socket.join('some room');
});
But your issue is with your emit, on each client connection, you listen to a message from your redis. Then you broadcast it to the room with an emit of the connected client.
Instead of that, you can do this:
io.on('connection', function(socket) {
socket.on('room', function(room){
socket.join(room);
});
socket.on('disconnect', function(){
console.log("disconnected!");
});
});
redis_client.on('message', function(channel, message) {
var myData = JSON.parse(message);
io.to('17').emit('cacad', 'u i u a a');
});
I think this happens on the socket-io-client side. not on the server-side. when I was using react-js for the client-side. I received the same message multiple times.
with the same server, I imported socket-io-client 4.4.1 in the vanilla js front-end project. then I didn't get multiple messages... :)
try use latest socket io client versions. i think they have fixed the issue in the latest versions..

Why does not sending data io.sockets.emit and socket.broadcast.emit

Tried different methods, but the data is sent to a maximum of one or two clients. How to send data to all the clients connected to the server ? What am I doing wrong?
Server.js:
var PORT = 3000;
var options = {
// 'log level': 0
};
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server, options);
server.listen(PORT);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/attantions/templates/.default/template.php');
});
io.sockets.on('connection', function (client) {
client.on('attantion', function (data) {
try {
// Tried so
io.sockets.volatile.emit('attantion', data);
// And tried so
io.sockets.emit('attantion', data);
client.emit('attantion', data);
client.broadcast.emit('attantion', data );
} catch (e) {
console.log(e);
client.disconnect();
}
});
});
Client.js:
socket.emit("attantion", data);
socket.on('attantion', function (data) {
pushData(data);
});
See this post for different options for socket.io messages
Send response to all clients except sender (Socket.io)
io.sockets.on('connection', function (client) {
client.on('attantion', function (data) {
//client.emit('attantion', data ); // This will send it to only the client
//client.broadcast.emit('attantion', data); // This will send it to everyone but this client
io.emit('attantion', data); // This will send it to all attached sockets.
});
});
Edit
I wonder if this post can help you?
Socket.io - Cannot load file
I was curious how sending the php file to the client through node.js works? are you using another framework?
Could you show more of what your client code looks like? loading the lib and the instantiation of the socket.

Server-side push using NodeJS

I want to implement a server-side push functionality using NodeJS.
For now, all I want is a server that listens to requests to a specific URL (e.g. http://localhost:8000/inputData?data=hello)
Once this request is made, I want all clients viewing client.html to run alert(hello);. What I did is the following, on the server side:
var app = require('http').createServer(handler),
io = require('socket.io').listen(app),
parser = new require('xml2json'),
fs = require('fs'),
url = require('url');
var qs = require('querystring');
app.listen(8000);
console.log('server listening on localhost:8000');
function handler(req, res) {
var url_parts = url.parse(req.url, true);
var pathname = url_parts.pathname;
switch(pathname){
case '/inputData':
var data = url_parts.query.data;
socket.emit('notifyData', data); //socket is undefined
break;
default:
fs.readFile(__dirname + '/client.html', function(err, data) {
if (err) {
console.log(err);
res.writeHead(500);
return res.end('Error loading client.html');
}
res.writeHead(200);
res.end(data);
});
}
;
}
At the client side, in client.html:
<script>
var socket = io.connect('http://localhost:8000');
socket.on('notifyData', function (data) {
alert(data);
});
</script>
I get an error saying that socket is not defined. I know that I should define it earlier but I am not sure how to do that..
Is this the right way to approach such problem? or should the emit be in a different file e.g. inputData.html? I'd rather complement the code I already have, because I might need to make a set of operations right before var data= url_parts.query.data
You have a design issue on your server as the socket variable is not defined at all. If what you're trying to do is to broadcast to all clients when any one hits that particular URL, then you can do that with:
io.sockets.emit('notifyData', data);
If you're trying to emit to a single client socket, then you will have to find a way to figure out which socket in the io.sockets namespace it is that you're trying to send to.
From the docs, it looks like you haven't initialized the server correctly.
var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
app.listen(8000);
/*...*/
io.on('connection', function (socket) {
socket.emit('notifyData', { hello: 'world' });
});
and on the client you should have
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost:8000');
socket.on('notifyData', function (data) {
alert(data);
});
</script>

Working with Routes in express js and socket.io and maybe node in general

I am trying to write a multi channel application in socket.io. The channel you are in should be defined by the url you are on. If I do the joining part in the app.js with permanent values everything works. As soon as I change it so that the route for route.page does the joining I get the error, that sockets is not available in the context. What would be the correct way so that I can dynamically join the channel?
/app.js
var io = socketio.listen(app);
require('./io')(io);
io.sockets.on('connection', function (socket) {
socket.on('debug', function (message) {
socket.get('channel', function (err, name) {
socket.in(name).broadcast.emit('debug', message);
});
});
});
/io.js
var socketio = function (io) {
if (!io) return socketio._io;
socketio._io = io;
}
module.exports = socketio;
/routes/index.js
var io = require('../io')();
exports.page = function(req, res){
var channel = req.params.id;
res.render('page', { title: 'PAGE', channel: channel });
io.sockets.on('connection', function (socket) {
socket.join(channel);
socket.set('channel', channel );
});
};
The easiest way I've found to do multiple channels is off of different URLs.
For example I have the client do the following:
io.connect('/game/1')
io.connect('/system')
and on the server I have
io.of('/game/1').on('connect' function(socket) {...})
io.of('/system').on('connect' function(socket) {...})
It looks like I'm connecting twice here, but socket.io is smart enough to use a single websocket for this connection (at least it says so in the how-to-use).

Socket.io Error (socket.send is not a function)

I'm trying out Websockets/Node.js/Socket.io/Express for the first time and I'm trying to create a simple chat program. Everything runs fine and I see both clients in my node termial.
But when I try to execute my socket.send(), I get an error in Firefox (socket.send is not a function). It doesn't complain about socket.connect() so I know the socket.io.js is loaded.
Here is my server code:
var sys = require('util');
var express = require('express');
var io = require('socket.io');
var app = express.createServer();
app.listen(8080);
app.use(express.static(__dirname));
app.get('/', function (req, res) {
res.render('index.html', {
title: 'Chat'
});
});
var socket = io.listen(app);
socket.on('connection', function (client) {
client.on('message', function (message) {
console.log("Message: " + JSON.stringify(data));
socket.broadcast(message);
});
client.on('disconnect', function () {});
});
My client code:
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
var socket = new io.Socket("http://localhost:8080");
socket.connect();
Then I do some code to get the chat message and send it.
socket.send(JSON.stringify(values));
Explanations
You haven't initialized Socket.io correctly on the server-side and client-side.
Client Side
new io.Socket("http://localhost:8080"); doesn't give you the object that you want, you need new io.connect("http://localhost:8080");.
You need to wait until the client is connected to the server before sending a message.
Server side
socket is the object send back by Socket.IO, you need to use socket.sockets to have access to on.
To broadcast a message, you need to use the client object like this: client.broadcast.send()
The variable data doesn't exist on your broadcast. You probably mean message.
Solution
Server
var sys = require('util'),
express = require('express'),
io = require('socket.io'),
app = express.createServer();
app.listen(8080);
app.use(express.static(__dirname));
app.get('/', function (req, res) {
res.render('index.html', {
title: 'Chat'
});
});
var io = io.listen(app);
io.sockets.on('connection', function (client) {
client.on('message', function (message) {
console.log("Message: " + JSON.stringify(message));
client.broadcast.send(message);
});
client.on('disconnect', function () {});
});
Client
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
<script>
var socket = new io.connect("http://localhost:8080"),
connected = false;
socket.on('connect', function () {
connected = true;
});
// Use this in your chat function.
if (connected) {
socket.send(JSON.stringify(values));
}
</script>
socket.broadcast(message); should be io.sockets.emit('key', message);
when you use the socket object passed in threw the connect event your only emitting information to that client, to emit to all clients you have to use io.sockets.emit().
also with socket.send(JSON.stringify(values)); I think you want to do socket.emit(namespace, data);
see my connection file from one of my projects here: https://github.com/AdminSpot/HangoutCanopy/blob/master/javascripts/connection.js
You have to wait for socket.io to connect on the client side
var socket = new io.Socket("http://localhost:8080");
socket.connect();
socket.on('connect', function() {
socket.emit('event', data);
});

Resources