How to create namespaces/rooms from the client using node.js? - node.js

what I want to do is be able to create a room from the client but as if it was an object. For example:
I have a class called "room", this class has let's say 3 events implemented so when you create a new room you can trigger those events in each.
Is this possible? Here's my server code:
var express= require('express');
var app= express();
var server=require('http').createServer(app);
var io=require('socket.io')(server);
var channel= io.of('/arduino');
var bodyParser= require('body-parser');
server.listen(80, function(){
console.log("Server corriendo en puerto 80");
});
io.on('connection', function(socket){
console.log("Se conecto alguien por Socket");
socket.on('new-message', function(data) {
console.log(data);
console.log("Nuevo mensaje");
socket.emit('messages', data);
});
socket.on('JSON', function (data) {
var jsonStr = JSON.stringify(data);
var parsed = ParseJson(jsonStr);
console.log(parsed);
});
});
channel.on('connection', function(socket){
console.log("Se conectaron al canal 'arduino' ");
socket.on('new-message', function(data){
console.log("Sensor:");
console.log(data);
channel.emit("messages", data);
});
});
app.use(bodyParser.json());
app.get('/',function(req,res){
console.log('555555555');
res.status(200).send('GET TEST');
});
app.post('/',function(req,res){
console.log("post TEST");
datos=req.body;
console.log(datos);
res.end("fin");
});

Knowing the difference between rooms and namespaces is important for deciding what to go with :
namespaces are connected to by the client using io.connect(urlAndNsp)
(the client will be added to that namespace only if it already exists
on the server)
rooms can be joined only on the server side (although creating an API
on the server side to enable clients to join is straightforward)
You can define socket events specific for a namespace, but the client wont be able to connect if that namespace hasn't been defined previously in the server-side.
Read More : Rooms vs Namespaces
I think that the easiest solution for handling different channels, including dynamic channel names (created from the client) is to use rooms
Room Join/Leave from Client :
A socket.io-client can be subscribed to multiple rooms, I gave an answer with a method for :
Making server.side room subscribe/unsubscribe events that can be triggered from the client
It also explains how to get correctly the list of rooms a disconnecting client was subscribed to so a message to those rooms may be delivered to notify the client left.

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

How to create multiple Nodejs socket io server-client?

I am new to Nodejs and Socket.io, and this is first time when I am creating any chat application, so pardon me if I am asking some silly question.
In my web I have to kind of chat services, one is Live debate which is kind of chat room, and another one is private messaging like Facebook Messenger.
I have created both, in private messenger before showing the message I am checking the conversation Id, it's working quite appropriately. Live debate is also working appropriately.
But there is a issue, any message sent in private messenger displays in live debate window also. So I change the Server.js file for messenger and also changed the listening port, now the listening port for live debate is 3000 and for messenger is 8050, but still Live debate receiving the messenger messages.
Am I doing this in wrong way? Is there any other way to run two chat applications ?
I am using this server code
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
server.listen(3000);
io.on('connection', function (socket) {
console.log("client connected");
var redisClient = redis.createClient();
redisClient.subscribe('message');
redisClient.on("message", function(channel, data) {
console.log("mew message add in queue "+ data+ " channel");
socket.emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
With using Namespace
server.js
var nsp = io.of('/debate')
nsp.on('connection', function (socket) {
console.log("client connected"+socket.id);
var redisClient = redis.createClient();
redisClient.subscribe('message');
var redisClient1 = redis.createClient();
redisClient1.subscribe('debate');
redisClient.on("message", function(channel, data) {
console.log("mew message add in queue "+ data+ " channel");
nsp.emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
client code
var socket = io.connect('http://localhost:3000/debate');
socket.on('message', function (data) {
data = jQuery.parseJSON(data);
console.log(data.user);
$( "#messages" ).append( "<strong>"+data.user+":</strong><p>"+data.message+"</p>" );
$('#messages').animate({
scrollTop: $('#messages').get(0).scrollHeight}, 200);
});
socket.io supports the use of different namespaces. You should use that feature instead of creating two individual servers. After that you can use socket.emit to that specific namespace. For more information see the documentation: https://socket.io/docs/rooms-and-namespaces/
It's not too difficult, I wrote a chat app, as I think everyone does when they start nodejs, but nodeJS has rooms which are quite easy to use.
io.on('connection', function(client){
client.on('room_connection', function(id){
client.join(id); // User joins room.
})
client.on('message', function(data){
io.to(data.room).emit('message', {message:data.message, client:client.conn.id});
});
})
This is pretty much all you need. This works for PM's since u simply won't allow multiple users to join this chatroom, and normal chatrooms which multiple users can join.

Express, keep variables for each user

I'm using socket.io on top of Express/Node
Right now I'm using this method:
// get room name from URL
var requestRoom;
app.get('/editor/:room', function(req, res) {
requestRoom = req.params.room;
res.render('editor/editor', {
title: 'Editor'
});
});
// use that room name for private rooms within socket.io
io.on('connection', function(socket) {
socket.join(requestRoom);
// rest of socket.io code....
to grab the room-name from a URL that looks like sitename.io/editor/[room-name].
Then I'm using requestRoom as a variable for socket.to(requestRoom).broadcast.. for sending the socket packets to a specific room.
The problem
Each time a new user visits the app, requestRoom gets repopulated for all users, instead of just for the user that just joined.
Is there a way to start a new 'instance' with own variables for each 'session'?
What am I missing here?
You cannot create new instance for each session.
But, you can solve this issue by sending the room name from the client.
When you connect to WebSocket using socket.io-client, add the room name to query parameter.
Client:
var requestRoom = "roomName";
io('/', {query: "requestRoom=" + requestRoom})
Server:
io.on('connection', function(socket) {
var requestRoom = socket.request._query.requestRoom;
socket.join(requestRoom);
// rest of socket.io code....
EDIT:
Nicholas Kyriakides used simpler way to solve this.
Server:
io.on('connection', function(socket) {
var refererSplit = socket.request.headers.referer.split('/');
var requestRoom = refAr[refererSplit.length-1]; //Considering Room name is at the end of the path.
socket.join(requestRoom);
// rest of socket.io code....
No need to pass roomName from the client.

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

working chat example which use session to authenticate users socket.io express node.js

is there any working example which store sessions and use that sessions for all opened windows to let one user just connect to one room just once?
this app will get phpsessions as node.js sessions but i cant findout how to just let one person to access this chat application just once
//define what we need
var express = require('express'),
app = express(),
memcache = require("memcache"),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server),
co = require("./cookie.js"),
php = require('phpjs'),
codein = require("node-codein");
// answer all request from users and send them back index.html for root access
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
var cookieManager = new co.cookie(req.headers.cookie);
//using memcache as our session store
var client = new memcache.Client(11211, "localhost");
//connect to memcache client
client.connect();
//get our cookie sessions
user = client.get("sessions/"+cookieManager.get("sec_session_id"), function(error, result){
var session = JSON.parse(result);
//get just username from sessions(sessions store name and family and chat username in this case)
user = JSON.parse(session.name);
user = user.username;
//use this function to pass our chat username to our function
storeUsername(user);
});
});
function storeUsername(user){
// usernames which are currently connected to the chat
var usernames = {};
io.sockets.on('connection', function (socket) {
usernames[socket.id] = socket;
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.emit('updatechat', socket.username, data);
});
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
// we store the username in the socket session for this client
socket.username = user;
// add the client's username to the global list
// echo to client they've connected
if(php.in_array(socket.username,usernames)){
delete usernames[socket.username];
}else{
usernames[user] = user;
console.log('not exist');
socket.emit('updatechat', 'SERVER', 'you have connected');
// echo globally (all clients) that a person has connected
socket.broadcast.emit('updatechat', 'SERVER', username + ' has connected');
// update the list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
}
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
delete usernames[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
});
});
}
server.listen(3000);
everything works fine and user which send data is defined but when i access this site in another tab i will connect another time to socket.io server
Do you mean sharing a websocket between browser tabs?
Sharing websocket across browser tabs?
But why do you need to share sockets? I've developed a node.js chat for a forum and we don't care about how many sockets a user has. We just have a "User" object that has a list of sockets. We don't care if the sockets come from firefox, from an android app... it's not a problem. And when we need to send information to a user, we send it to each socket.
You can try this. It is using express and socket.io https://github.com/gravityonmars/Balloons.IO

Resources