I am currently working on a simple socketio application in which i am sending some parameters to envents and i am storing them into each socket.
socket.on('add user', function (data) {
if (addedUser) return;
// we store the username in the socket session for this client.
socket.nickname = data.nickname;
socket.userId = data.userId;
...
Then i am getting all socketio clients using var io.engine.clients and i am trying to obtain that parameters in other event like this:
socket.on('new message', function (data) {
var clientsRoom = io.engine.clients;
for(var c in clientsRoom){
console.log(" Client: " + c.userId); //Error
console.log(" Client: " + c); //outputs the socket ID
console.log(" Client: " + c['userId']); //Error
}
...
but i am unable to get my userID previously stored for all sockets. ¿What am i doing wrong?
Thanks.
Instead of io.engine.clients, you should use io.sockets.sockets (they aren't the same, socket.io adds an extra layer on top of engine.io). Also make sure that you treat it as an object, not an array:
var clientsRoom = io.sockets.sockets;
for (var id in clientsRoom) {
var c = clientsRoom[id];
console.log(" Client: " + c.userId);
console.log(" Client: " + id);
}
Related
socket.on('join room', function (data) {
var room = data.room;
var msg = "<span style='color:darkgreen;'>" + data.user + " has joined chat.</span>";
socket.join(room, function () {
console.log(socket.id + " now in rooms ", socket.rooms);
});
io.in(room).emit('system message', msg);
});
If I change io.in(room) to socket.broadcast the system message goes through. For whatever reason though, it will not send messages to specific rooms, it just does nothing, no errors either. Any ideas what's going wrong here?
It seems the code above is on back-end, correct?
Try the following, adapt it with your needs if necessary:
io.on('connection', function (socket) {
// You should expect ONLY one user connected,
// > otherwise, you're creating multiple sessions :)
console.log('a user connected');
socket.on('join room', function (data) {
var room = data.room;
console.log('Room: ', room);
var msg = "<span style='color:darkgreen;'>" + data.user + " has joined chat.</span>";
socket.join(room); // No callback needed
console.log(socket.id + " now in rooms ", socket.rooms);
io.to(room).emit('system message', msg);
// Change it to io.to(room)
});
});
I noticed you're asking to join on front-end using:
$(".channelChange").click( function(evt) {
var socket = io();
$("#messages").html("");
evt.stopPropagation();
if($.inArray(evt.currentTarget, $(this).children())){
console.log("user moved to " + evt.currentTarget.id);
}
setCookie("currentRoom", evt.currentTarget.id, 1);
$(".roomName").html("<span class='3Dtext'>" + evt.currentTarget.id + "</span>");
$("#enter-sound").get(0).play();
getMessages(getCookie("currentRoom"));
// Here you ask to join room
socket.emit('join room', { user: getCookie("username"), room: getCookie("currentRoom") });
})
And receiving the system message on:
$(function(){
var socket = io();
socket.on('system message', function(msg) {
alert("test");
$('#messages').append($('<div class="systemMessage">').html(msg));
var height = $("#messages")[0].scrollHeight;
$.mobile.silentScroll(height);
});
The main problem as you'll see, is that you're calling io() everytime, this created different sessions,
1. Session one joins a room
2. A different session will await for system message
Hence, you need a single io() session, a quick work arround is to make a self invoking function:
const setIo = (function (){
const _io = io();
return () => _io;
})()
And then Replace all io() declarations to: var socket = setIo();
such as:
$(".channelChange").click( function(evt) {
var socket = setIo();
$("#messages").html("");
evt.stopPropagation();
This will create a single io() session instead of different ones, and re-use it everytime you call setIo()
You'll eventually see the alert pop up:
socket.join() is asynchronous. It has not completed yet when you're trying to send to the room, so no message goes to that socket because it isn't yet in the room. So, change this:
socket.join(room, function () {
console.log(socket.id + " now in rooms ", socket.rooms);
});
io.in(room).emit('system message', msg);
to this:
socket.join(room, function () {
console.log(socket.id + " now in rooms ", socket.rooms);
io.in(room).emit('system message', msg);
});
So, in this new version you're not sending to the room until AFTER the .join() has completed rather than before.
You can see a similar example right in the socket.io doc here.
Dear friends I have a small issue while trying to keep track of a logged in users in chat. The chat is based on two separate channels that work with the help of namespaces:
chatInfra - to handle logged in users and send welcome messages.
chatCom - to handle messageing between users.
I have searched a lot but I found only theoretical explanations that the best solutions is to store users into array. Therefore I tried to keep the track of logged in users by storing them in array and then iterating through them but still the result is not good.
The problem is that after entering to the chat, only the first logged in user's name appears on screen, whereas the second user's name is not visible.
This is my server side code, I am trying to store users into clients array:
var clients = [];
var chatInfra = io.of("/chat_infra").on("connection", function(socket){
socket.on("set_name", function (data) {
clients.push(data.name);
socket.emit('name_set', data);
socket.send(JSON.stringify({
type:'serverMessage',
message:'Welcome!'
}));
socket.broadcast.emit('user_entered', data);
});
});
var chatCom = io.of("/chat_com").on("connection", function (socket) {
socket.on('message', function (message) {
message = JSON.parse(message);
for(var key in clients){
if(message.type == "userMessage"){
message.username = clients[key];
console.log('message : ', message);
socket.broadcast.send(JSON.stringify(message));
message.type = "myMessage";
socket.send(JSON.stringify(message));
}
}
});
});
Here is how it looks in browser: http://screencast.com/t/lshnfcGZ8E8
Here is the full code: https://gist.github.com/johannesMatevosyan/0b9f7e588338dbb6b7f5
I think you're creating an unnecessary overkill by using different namespaces. Here's a clearer working example achieving the same functionality:
server.js
var app = require("express")();
var server = require("http").Server(app);
var io = require("socket.io")(server);
var chat = io.of("/chat").on("connection", function(socket){
socket.on("set_name", function (data) {
socket.username = data.name;
socket.emit("name_set", data);
socket.emit("message", {
type :"serverMessage",
message :"Welcome!"
});
chat.emit("message", {
type :"serverMessage",
message : data.name + " has joined the room.!"
});
});
socket.on("message", function (message) {
message.username = socket.username;
chat.emit("message", message);
});
});
app.get("/", function (req, res) {
res.sendfile(__dirname + "/index.html");
});
server.listen(3000);
client.js
var socket = io.connect('http://localhost:3000/chat');
socket.on('name_set', function (data) {
$('#nameform').hide();
$('#messages').append('<div class="systemMessage">Hello ' + data.name + '</div>');
});
socket.on('message', function (message) {
var userNameHtml = message.username ? '<span class="name">' + message.username + ':</span>' : '';
$('#messages').append('<div class="' + message.type + '">' + userNameHtml + message.message + '</div>');
});
$(function () {
$('#setname').click(function () {
socket.emit("set_name", { name: $('#nickname').val() });
});
$('#send').click(function () {
socket.emit("message", {
message : $('#message').val(),
type : 'userMessage'
});
$('#message').val('');
});
});
I don't think you need a separate event handler for user_entered, since you are treating it as a regular message and not doing anything else with the event. Also a couple of things:
You don't need to first connect to the server and then to the namespace address, connecting to the later is just fine.
Don't set event listeners within callbacks, that will result in setting them multiple times.
I have set up a node application which receives packets from a Port at an IP address and serves the contents to a web-page. This IP address has traffic going through two ports, 9999 and 10000. My application has the following code:
var PORT_ONE = 9999;
var PORT_TWO = 10000;
var SENSOR = '239.0.0.1';
var client = require('dgram').createSocket('udp4');
var dns = require('dns');
/* Client starts listening on IP */
client.on('listening', function () {
console.log('UDP Client listening on ' + SENSOR + ":" + PORT_TWO);
client.setMulticastTTL(1);
client.addMembership(SENSOR);
});
/* Client receives a message */
client.on('message', function (message, remote) {
var tempMessage = message.toString(); //cast Buffer var to String
var delimiter = "\n";
var tempData = tempMessage.split(delimiter);
console.log('From: ' + remote.address + ':' + remote.port +' - \n' + message);
var data = {
ip: [SENSOR],
info: [tempData]
};
sendMessage(data);
});
client.bind(PORT_ONE);
client.bind(PORT_TWO); //error here
When I run my node application in the terminal, I receive the error
dgram.js:163
throw new Error('Socket is already bound');
Where dgram.js is part of the Nodejs libraries. The error comes when binding to PORT_TWO. Is there a way to bind the socket to more than one port?
I want to add my 2 cents here, what I normally do is create array of ports and Ips and put in for loop binding on them one by one
udpclients = [5550,5551];
udpsockets=[]
var dgram = require('dgram');
udpclients.forEach(function(port){
var udpServer = dgram.createSocket('udp4');
udpServer.bind(port,'127.0.0.1')
udpServer.on('listening', function() {
var address = udpServer.address()
console.log("listening"+address.address+" port::"+address.port)
});
udpServer.on('message', function(msg, rinfo) {
console.log("message received :: "+ msg +" address::"+rinfo.address+ "port = "+ rinfo.port )
});
});
Or you can bind multiple sockets, wrap them into streams and then combine streams to get feeling of one single socket.
I need some help about my node.js+socket.io implementation.
This service expose a server that connects to an ActiveMQ broker over the STOMP protocol, using the stomp-js node.js module to receive events; that then are displayed in a web front end through websockets using socket.io.
So, everything was fine until I started use the Filters feature of ActiveMQ, but this was not the failure point because of my and my team researching, we found the way to ensure the implementation was fine, the problem comes with the connections: So here's the thing, I receive the filters to subscribe, I successfully subscribe to but when I receive a new set of filters is when comes the duplicated, triplicated and more and more messages depending the number of times that I subscribe-unsubscribe to.
So making some debug, I cannot see what's the problem but I'm almost sure that is some bad implementation of the callbacks or the program flow, I'll attach my code to read your comments about it.
Thanks a lot!
var sys = require('util');
var stomp = require('stomp');
var io = require('socket.io').listen(3000);
var socket = io.sockets.on('connection', function (socket) {
var stomp_args = {
port: 61616,
host: 'IP.ADDRESS',
debug: true,
};
var headers;
var client = new stomp.Stomp(stomp_args);
var setFilters = false;
socket.on('filtros', function (message) {
console.log('DEBUG: Getting filters');
if(setFilters){
client.unsubscribe(headers);
}
else{
client.connect();
}
var selector = '';
headers = '';
for(var attributename in message){
console.log(attributename+" : " + message[attributename]);
if(message[attributename] != ''){
selector += ' ' + attributename + '=\'' + message[attributename] + '\' AND ';
}
}
selector = selector.substring(0, selector.length - 4)
console.log('DEBUG: Selector String: ' + selector);
headers = {
destination: '/topic/virtualtopic',
ack: 'client',
selector: selector
};
if(setFilters)
client.subscribe(headers);
client.on('connected', function() {
client.subscribe(headers);
console.log('DEBUG: Client Connected');
setFilters = true;
});
});
var bufferMessage;
client.on('message', function(message) {
console.log("Got message: " + message.headers['message-id']);
var jsonMessage = JSON.parse(message.body);
if(bufferMessage === jsonMessage){
console.log('DEBUG: recibo un mensaje repetido');
return 0;
}
else{
console.log('DEBUG: Cool');
socket.emit('eventoCajero', jsonMessage);
}
client.ack(message.headers['message-id']);
bufferMessage = jsonMessage;
});
socket.on('disconnect', function(){
console.log('DEBUG: Client disconnected');
if(setFilters){
console.log('DEBUG: Consumer disconnected');
client.disconnect();
}
});
client.on('error', function(error_frame) {
console.log(error_frame.body);
});
});
Looking in the Socket.IO documentation, I've found that this is a known issue (I think critical known issue) and they have not fixed it yet. So, to correct this is necessary to reconnect to the socket in the client side to avoid duplicate messages, using:
socket.socket.reconnect();
function to force reconnection explicitly.
I need to send a broadcast datagram to all machine (servers) connected to my network.
I'm using NodeJS Multicast
Client
var dgram = require('dgram');
var message = new Buffer("Some bytes");
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, 41234, "localhost");
// If I'm in the same machine 'localhost' works
// I need to do something 192.168.0.255 or 255.255.255
client.close();
Servers
var dgram = require("dgram");
var server = dgram.createSocket("udp4");
server.on("message", function (msg, rinfo) {
console.log("server got: " + msg + " from " +
rinfo.address + ":" + rinfo.port);
});
server.on("listening", function () {
var address = server.address();
console.log("server listening " + address.address + ":" + address.port);
});
server.bind(41234);
Thanks.
I spent a lot of time trying to be able to do UDP broadcasting and multicasting between computers. Hopefully this makes it easier for others since this topic is quite difficult to find answers for on the web. These solutions work in Node versions 6.x-12.x:
UDP Broadcasting
Calculate the broadcast address
Broadcast address = (~subnet mask) | (host's IP address) - see Wikipedia. Use ipconfig(Windows) or ifconfig(Linux), or checkout the netmask module.
Server (remember to change BROADCAST_ADDR to the correct broadcast address)
var PORT = 6024;
var BROADCAST_ADDR = "58.65.67.255";
var dgram = require('dgram');
var server = dgram.createSocket("udp4");
server.bind(function() {
server.setBroadcast(true);
setInterval(broadcastNew, 3000);
});
function broadcastNew() {
var message = Buffer.from("Broadcast message!");
server.send(message, 0, message.length, PORT, BROADCAST_ADDR, function() {
console.log("Sent '" + message + "'");
});
}
Client
var PORT = 6024;
var dgram = require('dgram');
var client = dgram.createSocket('udp4');
client.on('listening', function () {
var address = client.address();
console.log('UDP Client listening on ' + address.address + ":" + address.port);
client.setBroadcast(true);
});
client.on('message', function (message, rinfo) {
console.log('Message from: ' + rinfo.address + ':' + rinfo.port +' - ' + message);
});
client.bind(PORT);
UDP Multicasting
Multicast addresses
Looking at the IPv4 Multicast Address Space Registry and more in-depth clarification in the RFC 2365 manual section 6, we find the appropriate local scope multicast addresses are 239.255.0.0/16 and 239.192.0.0/14 (that is, unless you obtain permission to use other ones).
The multicast code below works just fine on Linux (and many other platforms) with these addresses.
Most operating systems send and listen for multicasts via specific interfaces, and by default they will often choose the wrong interface if multiple interfaces are available, so you never receive multicasts on another machine (you only receive them on localhost). Read more in the Node.js docs. For the code to work reliably, change the code so you specify the host's IP address for the interface you wish to use, as follows:
Server - server.bind(SRC_PORT, HOST_IP_ADDRESS, function() ...
Client - client.addMembership(MULTICAST_ADDR, HOST_IP_ADDRESS);
Take a look at these supporting sources: NodeJS, Java, C#, and a helpful command to see which multicast addresses you are subscribed to - netsh interface ipv4 show joins.
Server
var SRC_PORT = 6025;
var PORT = 6024;
var MULTICAST_ADDR = '239.255.255.250';
var dgram = require('dgram');
var server = dgram.createSocket("udp4");
server.bind(SRC_PORT, function () { // Add the HOST_IP_ADDRESS for reliability
setInterval(multicastNew, 4000);
});
function multicastNew() {
var message = Buffer.from("Multicast message!");
server.send(message, 0, message.length, PORT, MULTICAST_ADDR, function () {
console.log("Sent '" + message + "'");
});
}
Client
var PORT = 6024;
var MULTICAST_ADDR = '239.255.255.250';
var dgram = require('dgram');
var client = dgram.createSocket('udp4');
client.on('listening', function () {
var address = client.address();
console.log('UDP Client listening on ' + address.address + ":" + address.port);
});
client.on('message', function (message, rinfo) {
console.log('Message from: ' + rinfo.address + ':' + rinfo.port + ' - ' + message);
});
client.bind(PORT, function () {
client.addMembership(MULTICAST_ADDR); // Add the HOST_IP_ADDRESS for reliability
});
UPDATE: There are additional options for server.send (named socket.send in the docs). You can use a string for the msg instead of a Buffer, and depending on your version, several parameters are optional. You can also check whether an error has occurred in the callback function.
UPDATE: Since Node.js v6, new Buffer(str) is deprecated in favor of Buffer.from(str). The code above has been updated to reflect this change. If you are using an earlier version of Node, use the former syntax.
I never used Node.js, but I do recall that with Berkely sockets (which seem to be the most widely used implementation of sockets) you need to enable the SO_BROADCAST socket option to be able to send datagrams to the broadcast address. Looking up the dgram documentation, there seems to be a function for it.
var client = dgram.createSocket("udp4");
client.setBroadcast(true);
client.send(message, 0, message.length, 41234, "192.168.0.255");
You might want to find out the broadcast address programmatically, but I can't help you with that.
I think since node 0.10.0 some things has changed this works for me now:
//var broadcastAddress = "127.255.255.255";
var broadcastAddress = "192.168.0.255";
var message = new Buffer("Some bytes");
var client = dgram.createSocket("udp4");
client.bind();
client.on("listening", function () {
client.setBroadcast(true);
client.send(message, 0, message.length, 6623, broadcastAddress, function(err, bytes) {
client.close();
});
});
Hope this helps somebody ;)
If you want a AUTOMATIC BROADCAST ADDRESS yo can do:
const broadcastAddress = require('broadcast-address');
const os = require("os")
var PORT = 1234;
var dgram = require('dgram');
var server = dgram.createSocket("udp4");
server.bind(function() {
server.setBroadcast(true);
setInterval(broadcastNew, 5000);
});
function broadcastNew() {
var message = Buffer.from("Broadcast message!");
Object.keys(os.networkInterfaces()).forEach(it=>{
console.log(broadcastAddress(it));
server.send(message, 0, message.length, PORT, broadcastAddress(it), function() {
console.log("Sent '" + message + "'");
});
})
}
This code will get a broadcast address for each interface on you server and send a message.
;) reguards
NOTE: dont forget install broadcast-address = "npm i broadcast-address"