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.
Related
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..
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.
I'm trying to build a secure private facebook like messaging system using laravel, redis pub/sub and socket io. All the console.log functions log the messages to say everything went through on the node server and everything works fine untill I try and emit the message to the client. When I try to emit the message to the client nothing at all happens.
Although I don't think my controller method on laravel is of significance here it is anyway. You can skip the Laravel controller if it's not necessary
Laravel Message Controller:
public function store($id, Request $request)
{
//Stores message in $message
//Connects redis
$redis = Redis::connection();
$data = ['message' => $request->input('message'), 'user' => Auth::user()->name,
'room' => $message->conversation_id];
$redis->publish('message', json_encode($data));
return Redis::get('message');
response()->json([]);
}
Redis stuff being published in JSON output:
{message: 'some message', user: 'john doe', room: 1}
Node Server
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) {
data = JSON.parse(data);
socket.join(data.room, function() {
console.log('Joined room '+data.room);
});
console.log("new message add in queue "+ data.room + " channel");
socket.to(data.room).emit(channel, data);
});
socket.on('disconnect', function() {
redisClient.quit();
});
});
Client Side JS
var socket = io.connect('http://localhost:3000');
socket.on('message', function (data) {
//Just console the message and user right now.
console.log(data.message+" " + data.user);
});
I don't know if my problem is that I'm not specifying the room on the client. If so is there any way of emitting the data to the client without specifying the room in the client side code? Why can't I emit the message to the client?
I'm new to both node.js and socket.io, but I'm trying to build a simple service that listens to Redis notifications (fed by PHP app), and broadcasts them to any users currently logged in, connected to a socket.io room e.g. 'site_name:user:user_id'.
I have it working, but the memory footprint of the Node app quickly gets larger and larger, going from 100mb to 200+mb pretty quickly with about 100 users online and actively browsing, and I'm wondering if I have something set up wrong here.
PM2 is handling the node app, and nginx is set up as reverse proxy.
Server side:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
var redisClient = redis.createClient();
var allClients = [];
server.listen(8890);
io.sockets.on('connection', function (socket) {
allClients.push(socket);
socket.on('subscribe', function(data) {
console.log('Joining room', data.room);
socket.join(data.room);
redisClient.subscribe(data.room);
})
socket.on('disconnect', function() {
console.log('Disconnect');
var i = allClients.indexOf(socket);
delete allClients[i];
});
});
// Watch for connection errors and log
redisClient.on('error', function (err) {
console.log('Error event - ' + redisClient.host + ':' + redisClient.port + ' - ' + err);
});
redisClient.on('message', function(room, message) {
console.log('New message: ' + message + '. In room: ' + room);
io.sockets.in(room).emit('message', message);
});
Client side:
// connect to socket
socket = io.connect('http://localhost:8890');
// subscribe to user's room once connected
socket.on('connect', function(data){
socket.emit('subscribe', { room: site_name + ':user:' + user_id });
});
// show messages from user's "room" via redis notifications
socket.on('message', function (response) {
var json = new Hash(JSON.decode(response, true) || {});
roar.alert(json.title, json.message, { link: json.link });
});
Seems like this should be a very lean app, no? What's a normal memory footprint for a simple node.js app?
The server starts up at 41mb, but even without anybody connecting to it, memory creeps up slowly, about 1mb a minute. Once I start connecting users, it bloats up quickly to 200+mb until I kill it.
I'm not clear on how best to handle the redisClient and socket connections as users connect & disconnect, and I thought that might be the issue. But seeing it creep up while idle is disconcerting.
PM2 v0.15.7
node v0.12.7
socket.io v1.3.7
express v4.13.3
nginx v1.6.2
Any help much appreciated.
I have a similar setup although it is not released and have done no stress testing yet... but here is an idea for you:
Use the redis module for socketio (whether it is any better then the redisClient would be interesting to know). It uses a different client for pub'ing and sub'ing. The subClient uses detect_buffers.
var redisModule = require('socket.io-redis');
var redisAdapter= redisModule({
host: redisClient.options.host
, port: redisClient.options.port
, pubClient: redisClient
//, subClient: ... separate client that uses detect_buffers
});
io.adapter(redisAdapter);
then subscribe/disconnect looks like this:
socket.on('subscribe', function(room) {
socket.join(room);
});
socket.on('disconnect', function() {
console.log('user disconnected');
});
I've also read multiple times that at one point socketio was not the best and to instead use sockjs. No idea if that is still the case.
And... since I just realized it's been more than 2 months. Did you find anything to reduce your memory footprint?
I'm trying to get Redis pub/sub working in a little Node app. Currently I'm using node-redis and I don't understand the API for pub/sub. It seems like I can subscribe to a channel by name and then I receive all messages for all channels in a generic 'message' event, like so:
var express = require('express')
, app = express()
, server = app.listen(4000)
, sio = require('socket.io')
, io = sio.listen(server)
, db = require('redis').createClient();
io.sockets.on('connection', function (socket) {
db.subscribe("foo");
db.subscribe("bar");
db.on("message", function(channel, message) {
socket.emit('message', message);
});
});
This forces me to either create a new client for each subscription or to use something like:
db.on("message", function(channel, message) {
socket.emit(channel, message);
});
and then listen to the right socket.io channel client side. Neither is ideal for me.
What I would like is to register a callback at the time of subscribing so, I receive messages for that channel only, like in this pseudo code snippet:
db.subscribe("foo", function(message) {
socket.emit("foo_message", message);
});
db.subscribe("bar", function(message) {
socket.emit("bar_message", message);
});
Does anybody know a clean solution for my problem or maybe another redis API that supports my wishes?