I'm working on chat, where facebook friends can talk only with each other. I'm using redis so save relation: fb_user_id - user_socket_id. This is how my implementation looks like:
getting friends from facebook;
selecting socket ids of my friends from redis, creating local friends-sockets list in my node client;
connecting to node server. Server saving my socket id to redis and notifying all my friends about new friend login (about me);
all my friends updating local friends-sockets list;
when someone sending chat message to server, this message comes with friends-sockets list, so server knows where need to send message (only for my friends).
Question: it's better solution to send friends-sockets every time to server, or it's better to get this relation on server from redis (or create array of sockets in server). How to adapt my task for high availability?
Any comments and suggestions are welcomed, thanks.
Here is my code (socket.io 1.2.0)
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var redis = require("redis"), client = redis.createClient();
var parts;
client.select(2, function() {});
client.on("error", function (err) {
console.log("Error " + err);
});
process.on('uncaughtException', function (err) {
console.log(err);
});
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function (socket) {
// on connect
socket.on("join", function (data)
{
if (data) {
// notify all friedns about new friend login
if (data.to) {
if (data.to.length > 0) {
for (x in data.to) {
io.to(data.to[x]['socket_id']).emit('new friend response', {uid: data.uid, sid: socket.id});
}
}
}
// save or update user socket id to redis
parts = split_id(data.uid);
client.hset(parts[1], parts[0], socket.id);
}
});
// disconnect
socket.on('disconnect', function () {
console.log("user disconnected");
});
// send message by friends-sockets list
socket.on('chat message', function (data) {
if (data.to.length > 0) {
for (x in data.to) {
var message = data.msg;
io.to(data.to[x]['socket_id']).emit('chat message response', {msg: message, uid: data.uid});
}
}
});
});
http.listen(3000, function () {
console.log('listening on *:3000');
});
// split facebook uid in 2 parts (for redis saving)
function split_id(str)
{
var n = str.length;
var res1 = str.substr(n - 2, 2);
var res2 = str.substr(0, n - 2);
return [res1, res2];
}
client.js
// friends socket list
var friends_sockets = [];
// my data from facebook
var my_data;
// my facebook uid
var my_uid;
function client() {
socket = io('http://server.com:3000');
// connect
socket.on('connect', function () {
// notify server about login
socket.emit('join', {uid: my_uid, to: friends_sockets, from: my_data, type: 'web'});
});
// send chat message to my friends
$('.enter_form button').click(function () {
if (friends_sockets.length > 0) {
socket.emit('chat message', {msg: $('#m').val(), to: friends_sockets, from: my_data, uid: my_uid});
}
// add message to my chat
$('#messages').append($('<li>').text(my_data.first_name + ' ' + my_data.last_name + ': ' + $('#m').val()));
$('#m').val('');
return false;
});
// new message listner (waiting for chat messages)
socket.on('chat message response', function (data) {
$('#messages').append($('<li>').text(data.msg));
});
// new friends lister (update list on friends login)
socket.on('new friend response', function (data) {
var found = false;
if (friends_sockets.length > 0) {
for (x in friends_sockets) {
if (friends_sockets[x]['uid'] == data.uid) {
friends_sockets[x]['socket_id'] = data.sid;
found = true;
}
}
}
if (found === false) {
friends_sockets.push(data);
}
});
}
Concerning your question regarding high availablity, have a look at
http://socket.io/docs/using-multiple-nodes/
for a configuration example using nginx, multiple Node processes and Redis as session store.
Related
Hello I'm trying to create a chat application, I googled around and I got some issues on this step. Would appreciate some help...
Server.js
var express = require("express");
var app = express();
var http = require("http").createServer(app);
var io = require("socket.io")(http);
var users = [];
io.on("connection", function (socket) {
console.log("User connected", socket.id);
socket.on("user_connected", function (username) {
users[username] = socket.id;
io.emit("user_connected", username);
});
socket.on("send_message", function (data) {
var socketId = users[data];
io.to(socketId).emit("new_message", data);
console.log(data);
});
});
http.listen(3000, function () {
console.log("Server Started");
});
chat.php
function sendMessage(){
var message = document.getElementById("message").value;
io.emit("send_message", {
sender: sender,
message: message
});
return false;
}
io.on("new_message", function (data) {
console.log(data);
//var html = "";
//html += "<li>" + data.sender + " says: " + data.message + "</li>";
//document.getElementById("messages").innerHTML += html;
});
So my problem is happening in chat.php where my console.log(data) isn't shown, however the data is shown in server.js. Why is this currently not working?
From what you said earlier it's possible that you make it more complicated than it actually is. No need to change anything in chat.php, however instead of creating the variable socketId you could just emit the data immediately like this:
io.on("connection", function (socket) {
console.log("User connected", socket.id);
socket.on("user_connected", function (username) {
users[username] = socket.id;
io.emit("user_connected", username);
});
socket.on("send_message", function (data) {
io.emit("new_message", data);
});
});
I'm having problem finding the right answer for my problem. I want to had private messaging for every user like Telegram if I may say. Or like notifications on StackOverflow the socket event are only send to specific user.
io.on('connection', function (socket) {
// this will be only sent to a user that just connected
socket.emit('news', { hello: 'world' });
});
But how to send specific message with custom id?
Here my socket.js
const socketio = require('socket.io')
const { redis, saveUser } = require('./redis')
module.exports.listen = function(app){
io = socketio.listen(app)
io.on('connection', socket => {
console.log('connected user', socket.id)
socket.on('join', (payload) => {
redis.get(payload.email (err, socket_id) => {
socket.broadcast.to(socket_id).emit('join', `Hello ${payload.whoChat.name} Chat you`)
})
})
})
return io
}
but this doesn't send anything to my user, if using rooms it will send directly to the rooms and when i look for docs io.to() and io.broadcast.to() can be use with sockets rooms.
so i decided to take it simple but wrong here my server.js rigth now.
io.on('connection', function (socket) {
socket.on('join', payload => {
socket.emit('join', payload)
})
and on client-side
socket.on('join', (payload) => {
// Compare data from socket with local data if match then push the message
// to users who recieve
if ( payload.userId === localData.userId ) {
this.message.push(payload.message)
}
})
but with that actually I send it to all users... how to do it properly and right? and how to save the users socket.id? since socket.id are generated randomly how do you save it for the future request when user disconnect?
You need to maintain a map of _socket_id/user_id_
Server
const sessionsMap = {};
io.on('connection', (socket) => {
socket.emit('askForUserId');
socket.on('userIdReceived', (userId) => {
sessionsMap[userId] = socket.id;
});
socket.on('send', (message) => {
const receiverId = sessionsMap[message.receiverId];
const messageData = message.data;
socket.broadcast.to(receiverId).emit('my message', messageData);
});
});
Client
const userId = 'FOO';
io.on('askForUserId', () => {
io.emit(userId);
});
io.on('send', (message) => {
console.log('You received a message');
console.log(message.data);
});
Note
Make sure to check out the Socket.IO cheatsheet it covers a lot of commands and use cases.
I'm new in node js and socket io, I'm trying to create interactive quizzes based on pin code given in the url for ex: http://domain/start/5555 should create a quiz page with name 5555 and when clients have the pin code they can connect to this quiz. So it means i have to have a quiz page which shows all connected clients for example but i have some prblems with io.sockets.in(pincode).emit()
because i don't know if i'm creating rooms correctly or not!
here is my code
Client.js
var socket = io();
socket.on('connect', function () {
var params = jQuery.deparam(window.location.search);// To get the name and the room
socket.emit('join', params, function (err) {
if (err) {
alert(err);
window.location.href = '/join.html';
} else {
console.log('No error');
}
});
});
Server.js
app.get('/start/:pincode', (req, res) => {
io.on('connection', (socket) => { // to rigester connection listener
console.log('New user is connected');
// When somebody connects it verifies if the name and the room are valid strings
socket.on('join', (params, callback) => {
if (!isRealSrting(params.name) || !isRealSrting(params.pincode)) {
return callback('Name and pincode are required');
}
socket.join(params.pincode);
users.removeUser(socket.id);//Remove the user from other places
users.addUser(socket.id, params.name, params.pincode);
var x = users.getUserList(params.pincode);
console.log(x);
io.sockets.in(params.pincode).emit('updateUserList', { //here it doesn't work i don't know why!
users: users.getUserList(params.pincode),
pincode: params.pincode
});
socket.emit('newMessage', {
user: params.name,
pincode: params.pincode,
readyMessage: 'You are connected'
});// when the user connect to the server he gets this message
callback();
});
socket.on('disconnect', () => {
var user = users.removeUser(socket.id);
if (user) {
io.emit('updateUserList', users.getUserList(user.pincode));
//io.to(user.room).emit('newMessage', generateMessage('Admin', `${user.name} has left`));
}
});
});
res.render('start.hbs', {
pincode: req.params.pincode
});
});
and the main page
Start.js
var socket = io();
var pincode;
socket.on('updateUserList', function (users) {
console.log('here');
var ol = jQuery('<ol></ol>');
var usercount = users.users.length; // Players number
pincode = users.pincode;
});
Any help or suggestion please?
I have two programs, a sender and a receiver. The sender sends some message to device on IOT Hub and receiver in turn receives those messages as long as sender sends them. I am using socket.io to broadcast those messages to connected clients. However when the sender is stopped, the receiver also stops but the last message sent by sender is going to broadcasted infinitely until i close the receiver or the sender starts again and sends new messages. The last message will get duplicated and broadcasted infinitely. How to check if the sender program has stopped?
this is the sender prrogram:
var clientFromConnectionString = require('azure-iot-device-mqtt').clientFromConnectionString;
var Message = require('azure-iot-device').Message;
var connectionString = 'conn_string'
var client = clientFromConnectionString(connectionString);
var avgTemperature = 20;
var printResult = function (err, res) {
if (err) {
console.log('send error: ' + err.toString());
return;
}
console.log('send status: ' + res.constructor.name);
};
setInterval(function () {
var currentTemperature = avgTemperature + (Math.random() * 10) - 2;
var data = JSON.stringify({
deviceId: 'test',
temperature: currentTemperature,
latitude: 50.286264,
longitude: 19.104079,
time: Date.now()
});
var message = new Message(data);
console.log("Sending message: " + message.getData());
client.sendEvent(message, printResult);
}, 5000);
this is the receiver and the socket.io which broadcasts to the client:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var EventHubClient = require('azure-event-hubs').Client;
var connectionString = 'conn_string'
var printError = function (err) {
console.log(err.message);
};
var result;
var printMessage = function (message) {
console.log('Message received: ');
result = JSON.stringify(message.body);
console.log('message: ' + result);
/* io.on('connection', function(socket){
socket.on('chat message', function(msg){
io.emit('chat message', result);
});
}); */
console.log('');
};
count =0;
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket){
console.log('user connected');
socket.on('chat message', function(msg){
io.emit('chat message', result);
});
socket.on('disconnect', function(){
console.log('user disconnected');
socket.removeAllListeners('disconnect');
io.removeAllListeners('connection');
});
});
var client = EventHubClient.fromConnectionString(connectionString);
client.open()
.then(client.getPartitionIds.bind(client))
.then(function (partitionIds) {
return partitionIds.map(function (partitionId) {
return client.createReceiver('$Default', partitionId, { 'startAfterTime' : Date.now()}).then(function(receiver) {
console.log('Created partition receiver: ' + partitionId)
receiver.on('errorReceived', printError);
receiver.on('message', printMessage);
});
});
})
.catch(printError);
http.listen(3000, function(){
console.log('listening on *:3000');
});
According to your code. Whenever the sender stops sending, the receiver will not receive the messages and will wait for the sender to send new messages. However, if you still want to check, you can use sequence numbers with sender messages or associate Id with them to check duplicates.
Following is the code on front end, where storeSelUserId contains user_id to send the message-
FYI - Node Version 1.1.0
// Socket Notification
var socket = io('http://localhost:6868');
socket.on('connection', function (data) {
socket.emit('send notification', { sent_to: storeSelUserId });
});
Following is the server code in routes file -
var clients = {};
io.on('connection', function (socket) {
socket.emit('connection', "Connection Created.");
socket.on('send notification', function (sent_to) {
console.log(sent_to);
});
});
In console sent_to is showing the array of user_id.
Now being a starter in socket.io I stuck with the solution that how do I send the message to these particular userids.
I search and found that I need to push each user with its sockets so I reformed it to -
var users = [];
io.on('connection', function (socket) {
users.push({socket_id: socket.id});
socket.emit('connection', "Connection Created.");
socket.on('send notification', function (sent_to) {
console.log(sent_to);
});
});
But I am in dilemma that else do I need to do to store which user_id refers to which socket_id and then update the div of users with that particular ids?
EDIT -
Add Controller - (Front End)
Front end Interface where memo is created and send to particular users
var socket = io('http://localhost:6868');
socket.on('connection', function (data) {
socket.emit('send memo notification', {creator_id: creator_id, sent_to: [Array of user_ids to whom memo to send]});
});
Dashboard controller - (Front End)
Front end Interface where notification count to show "notificationCount"
if (SessionService.currentUser._id) {
var socket = io('http://localhost:6868');
socket.on('connection', function (data) {
socket.emit('get notifications', {user_id: SessionService.currentUser._id});
});
socket.on('notification data', function(data){
console.log("-- Not Data Test -");
$scope.notificationCount = data.length;
});
}
Code at server end -
io.on('connection', function (socket) {
socket.emit('connection', "Connection Created.");
socket.on('send memo notification', function(data) {
notifications.createNotification(data);
});
socket.on('get notifications', function(data){
notifications.getNotifications(data, function(response){
socket.emit('notification data', response.data);
});
});
});
Backend controller code -
exports.getNotifications = function(data, callback) {
var userId = data.user_id;
Notification.find({receiver_id: userId}, function(err, response){
if (err)
callback({"message": "error", "data": err, "status_code": "500"});
else
callback({"message": "success", "data": response, "status_code": "200"});
});
};
exports.createNotification = function(data) {
var notificationData = data;
var x = 0;
for(var i=0; i< notificationData.length; i++) {
// Code
Notification(notificationData[i]).save(function(err,response){
if (err)
return false;
});
if (x === notificationData.length - 1) {
return true;
}
x++;
}
};
If you want to use your own user ids then there is no way around mapping the socket id to the user id. I assume a client knows its user id from somewhere, so it could send its user id to the server after connection.
Client
socket.on('connection', function (data) {
socket.emit('setUserId', myUserId);
});
The server saves the socket for each user id.
socket.on('setUserId', function (userId) {
users[userId]=socket;
});
If you have such a mapping in the server you can send a message just to this client using the user id.
socket.on('send notification', function (userId) {
users[userId].emit('notification', "important notification message");
});
Edit: Saving the corresponding socket directly is even better.
According to what i understand, you need private notification send to only some users. For that, save your users name to whom you want to send and their corresponding socket in different hashes.
username [socket.name] = username to be added;
usersocket [ socket.name ] =socket;
Then to emit the messages to that user only, use
usersocket[ socket.name ].emit('event for send message', ' what you want to send ');