Socket.io broadcasting not working - node.js

I'm really new to node.js and I'm starting to learn it. Actually I'm trying to develop a simple chat application just for learning purpose.
I'm also using socket.io package.
That's what I've done:
1) index.js
var express = require('express');
var app = express();
var path = require('path');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var users = {};
var socket = io.listen(3001);
var dateFormat = require('dateformat');
app.use('/public',express.static(path.join(__dirname, '/public')));
app.get('/',function(req,res){
res.sendFile(__dirname+'/index.html')
});
//CHAT
socket.on('connection',function(client){
client.on('join',function(name){
var date = dateFormat(new Date(),"HH:MM:ss dd-mm-yyyy");
var hour = dateFormat(new Date(),"HH:MM:ss");
users[client.id] = {"name":name,"id":client.id,"status":1,"joinDate":date};
client.emit("update", "You have connected to the server.");
var msg ={'msg':"Client: "+name+" joined the channel", 'user':name,'hour':hour,'fromServer':1};
io.sockets.emit('update', msg);
});
client.on('request-list',function(){
client.emit('user-list',users);
});
client.on('chat',function(msg){
//socket.broadcast.emit('update',msg);
//THIS DOESN'T WORK
socket.emit('update', msg);
});
client.on('disconnect',function(){
delete users[client.id];
if(Object.keys(users).length>0){
console.log(Object.keys(users).length);
} else{
console.log("NESSUNO");
}
});
});
//END CHAT
http.listen(3000,function(){
console.log('listening on 3000');
});
2) main.js
$(document).ready(function(){
var socket = io.connect('127.0.0.1:3001');
var chatStarted = false;
$('[data-type="loginMask"] [data-component="login-button"]').off().on('click',function(){
socket.emit('join',$('[data-component="login-nickname"]').val());
localStorage.setItem('user',$('[data-component="login-nickname"]').val());
});
socket.on('update',function(msg){
if(!chatStarted){
$('[data-type="loginMask"]').hide();
$('[data-type="chatMask"]').show();
socket.emit('request-list');
chatStarted=true;
}else{
$('[data-component="chat"] [data-component="chat-messages"]').append(formatMessage(msg));
}
});
socket.on('user-list',function(list){
var html ='';
$.each(list,function(k,item){
html += '<div data-component="userlist" data-id="'+item.id+'">';
html += '<span data-component="userlist-status">'+getStatus(item.status)+'</span>';
html += '<span data-component="userlist-user">'+item.name+'</span>';
html += '<span data-component="userlist-caret"><i class="fa fa-caret-right" aria-hidden="true"></i></span>';
html += '</div>';
});
$('[data-type="userlist"]').append(html).promise().done(function(){
});
});
$('[data-component="chat-text"]').off().on('keydown',function(e){
if(e.which==13){
$('[data-component="chat-send"]').trigger('click');
}
});
$('[data-component="chat-send"]').off().on('click',function(){
var msgToSend = {'msg':$('[data-component="chat-text"]').val(),'user':localStorage.getItem('user'),'fromServer':0};
$('[data-component="chat"] [data-component="chat-messages"]').append(formatMessage(msgToSend));
//socket.broadcast.emit('chat',msg);
//THIS DOESN'T WORK
socket.emit('chat',msgToSend);
$('[data-component="chat-text"]').empty();
});
});
I tried to use the broadcast function in two parts of the code (where there's the comment THIS DOESN'T WORK :), but I always receive undefined in both parts. If I leave the code as it's now the message will be sent to all clients included the sender, I want to send the message to everyone except the client.
As I said I'm new to node and socket.io as well, so I can't really understad what is the problem.
In the socket.io chat example here they made use of broadcast, so I guess it's a method of the socket.
Thank you in advance for your help

To broadcast to all connected clients from the server, you use:
io.emit(...)
To broadcast to all connected clients except one use:
socket.broadcast.emit(...)
where socket is the one you do not want to broadcast to.

Related

Image with socket io node js

I am trying to do something that sounds quite simple but unfortunately I can't manage to get right.
I just need to stream an image from a file with socket.io.
I have read several links that were either outdated or incomplete and here is where I am right now (not working) :
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var bodyParser = require('body-parser');
var fs = require('fs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}));
app.get('/',function (req,res){res.sendStatus(200)});
app.get('/image',function (req,res){res.sendFile(__dirname+'/index.html')});
http.listen(3000, function () {
console.log("server listening");
});
io.on('connection',function(socket){
console.log("socket connection");
var interval = setInterval(function(){
fs.readFile('./image.jpeg',function(err,buff){
if(err){
console.error(err);
}
else{
socket.emit('image',{image:true,buffer:buff.toString('base64')});
}
});
},1000);
socket.on('disconnect', function(){
console.log("socket deconnection");
clearInterval(interval);
});
});
index.html
<html>
<head>
<meta charset="UTF-8">
<title>IMAGE</title>
</head>
<body>
<script src="/socket.io/socket.io.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(function(){
var socket = io();
var ctx = document.getElementById('canvas').getContext('2d');
socket.on('image',function(info){
if(info.image){
var img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src = 'data:image/jpeg;base64,' + info.buffer;
console.log("Image received");
}
})
});
</script>
</body>
</html>
I have also tried several other configurations close to this one for the client file but none of them worked.
I read the file at interval because in the real project the image.jpeg file will change at times.
Thank you very much if you can help !
Sending images over a socket is not very efficient. I stumbled upon something similar when I had to send big buffers of game world data to every client. Sending those packets over a socket connection was obviously not an option for me.
I solved it by storing a key that referenced to a specific file on the server, and only sending that key to the client. Then the client would send a request containing the key to a http server, which eventually would respond with the file.
Here is some shortened code just to illustrate:
On the server:
...
const files = {};
...
...
function (request, response) { // http request function
const key = request.url.parse(key);
const file = fs.readFile(files[key]);
response.send(file);
}
...
...
function sendFile (socket, file) { // function that sends and stores the key
const key = generateKey();
files[key] = file;
socket.send('key', key);
}
...
...
sendFile(socket, '/path/to/file'); // send key to socket
...
This way the big files are being downloaded over http instead of the socket.
On the client:
...
socket.on('key', function (key) {
fetch('/file/' + key, function (response) {
const file = response.blob(); // blob or whatever
});
});
...
Hope this helps you :D

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 use the client ID in HTML5 websocket and node.js chat?

I'm trying to send private message between users in a simple chat that I created using HTML5 websocket and node.js.
When the users connect, I create a simple ID for them (connection ID) and add that to this CLIENTS=[];
When I do a console.log(CLIENTS);, I see all the IDs like this:
[0, 1, 2]
Now I need to use the IDs to send Private messages to the users.
So i went ahead and this (For the testing purposes, I just need to send the message to the user with ID 2):
var express = require('express'),
app = express(),
http = require('http').Server(app),
WebSocketServer = require('ws').Server,
wss = new WebSocketServer({
port: 8080
});
CLIENTS = [];
connectionIDCounter = 0;
app.use(express.static('public'));
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
client.send(data);
});
};
wss.on('connection', function(ws) {
/////////SEE THE IDs HERE////////////////
ws.id = connectionIDCounter++;
CLIENTS.push(ws.id);
console.log(CLIENTS);
ws.on('message', function(msg) {
data = JSON.parse(msg);
/////SEND A PRIVATE MESSAGE TO USER WITH THE ID 2//////
CLIENTS['2'].send(data.message);
});
});
http.listen(3000, function() {
console.log('listening on *:3000');
});
When I run my code, I get the following error:
TypeError: CLIENTS.2.send is not a function
Could someone please advice on this issue?
Any help would be appreciated.
Thanks in advance.
Track clients manually, replace:
CLIENTS = [] with CLIENTS = {}, and CLIENTS.push(ws.id); with CLIENTS[ws.id] = ws;
According to docks https://github.com/websockets/ws/blob/master/doc/ws.md should be something like this:
new WebSocket.Server(options[, callback])
clientTracking {Boolean} Specifies whether or not to track clients.
server.clients - A set that stores all connected clients. Please note
that this property is only added when the clientTracking is truthy.
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({
port: 8080,
clientTracking: true
});
.....
wss.on('connection', function(ws) {
ws.on('message', function(msg) {
data = JSON.parse(msg);
wss.clients[ID].send(data.message);
});
});
I don't know what data format wss.clients is, so you should try it by yourself. If this is really {Set} as docks said, then try wss.clients.get(ID).send(data.message)

socket.IO can only connect one client (browser) at a time

I am having a problem of using socket IO to connect my server to the client(http website).
On the client, I have a button that when pressed, sends data to the server. However, this only works with one client.
If I have two clients, the first person to open the http website gets the socket IO connection, while the second person can open the page, but can't send any data to the server.
On the client side:
var socket = new io.connect('ServerIP:8090');
socket.on('message', function(obj){
if ('buffer' in obj){
//ignore this
} else message(obj);
});
On server side:
var io = io.listen(server)
, buffer = [];
io.on('connection', function(client)
{
client.send({ buffer: buffer });
client.broadcast.send({ announcement: client.sessionId + ' connected' });
chatGuests.push(client);
client.on('message', function(message){
var msg = { message: [client.sessionId, message] };
buffer.push(msg);
if (buffer.length > 15) buffer.shift();
client.broadcast.send(msg);
});
client.on('disconnect', function(){
client.broadcast.send({ announcement: client.sessionId + ' disconnected' });
});
Instead of using client.broadcast.send(something) and client.send(something) use io.emit('eventName', something). Also, for setting up the server with the variable io use
var socket = require('socket.io');
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = socket.listen(server);
And then after your code:
server.listen(8090);
This allows you to use the node.js module express, which allows additional communication between the client and server (but doesn't require you to rewrite your socket.io code).
For your client code, instead of using:
socket.on('message', function(obj){
//Do something
});
Use:
socket.on('eventName', function(something){
//Do something
});
This works for multiple kinds of data passing, not just messages. You can multiple event listeners to each do different things

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