I am creating a rest api on nodejs. I have email id and user id in database. I want to share a file (present on same server) from one user to a particular user. Can anyone tell me how this can be done in a best way ?
Here is the code i have tried yet.
const server = require('./../server/server.js')
const socketIO = require('socket.io');
const io = socketIO(server);
const mongoose = require('mongoose');
const User = require('../models/user.js');
let sockets = [];
io.on('connection', socket=>{
console.log("User connected");
socket.on('online', (data)=>{
socket.name = data._id;
sockets[data._id] = socket.id;
console.log("user is online")
})
socket.on('send_file', (data)=>{
User.find({emailId: data},{emailId:0, password:0})
.exec()
.then(userid => {
if(userid.length<1){
console.log("No such user");
}
else{
console.log(userid[0].id);
socket.to(sockets[userid[0].id]).emit('hello', "HELLO");
}
})
.catch(err =>{
console.log(err);
});
});
socket.on('disconnect', ()=>{
console.log("User disconnected");
})
})
module.exports = io;
server.listen('8080', (err)=>{
if(err) throw err;
console.log("running on port 8080");
});
Assuming that you have already configured the socketio and express sever properly with he mechanism to save the file path and file name in you database.
Try something like this (with socketio)
let sockets = [];
io.on("connection", function(socket) {
socket.on("online", data => {
socket.name = data.username;
sockets[data.username] = socket.id;
});
socket.on("send_file", function(data) {
// your logic to retrieve the file path from you database in to the variable **filedata**
// let filedata = ................
socket.to(sockets[data.user]).emit("file",filedata);
});
socket.on("disconnect", reason => {
sockets.splice(sockets.findIndex(id => id === socket.id), 1);
});
});
in send_file event you will have receive the username from the sender inside the data object. The following code will be one which will help you to send file to selected user.
socket.to(sockets[data.user]).emit("file",filedata);
Replying to your 1st comment.
history.push() will not refresh the client since its a single page application.
But when you refresh(from user A side) a new socket session will be created then the other user(user B) will still be referring the old socket session(which is already being disconnected by the refresh). So to handle this use the following lines
socket.on("online", data => {
socket.name = data.username;
sockets[data.username] = socket.id;
});
where you will be keeping a pool(an array) of sockets with the usernames so when ever a user refresh their client the newly created socket will be referring to the same user. Since you will be updating the the socket.id to the same person.
For example assume that you the user who refresh the client and im the other user. so when you refresh a new socket session will be created an it will be sent to the back end along with the user name. When the data comes to the server it will get your session object from the array(sockets[data.username]) and update it with the new socketio sent from your front-end sockets[data.username] = socket.id;.
for this to happen you will have to send the user name along with the socket message. like this
socket.emit("online", {
username: "username"
});
Replying to your 2nd comment
To send data in real time the users should be online. if not you can just create an array of notifications with the following information (sender and receiver). So when the receiver logs in or clicks on the notification panel the list of shared files notification can be shown. This is just a suggestion you can come up with you own idea. Hope this helps.
In case of Non-real time data:
Server Side-:
use res.sendFile() is a way to solve this problem. In addition to file send the receiver and sender id in headers
Client Side-:
Increase the notification count if the receiver id matches the logged in user.
res.sendFile(path.join("path to file"),
{headers:{
receiverid:result[0]._id,
senderid:result[1]._id
}
});
In case of real time data:
follow the answer posted by TRomesh
Related
I'm trying to create a user to user chat application - no group chat or anything.
I'm using NodeJS and Socket.io on the backend and React Native on the frontend.
I ended up having a Map that stores a user id and it's corresponding socket id, my problem is that only when a user connects to the server, he will get a socket id.
But what if User A is connect and is trying to send a message to User B, and User B is not connected, so it does not have a socket id, I don't really know what to do then.
This is what I got so far:
io.on("connection", (socket: Socket) => {
//Whenever a user will connect, the user will emit 'initiate' to register itself and it's socket id to the server.
//We will be using this userSocketMap to send messages between users.
socket.on(SocketEvents.INITIATE, (data) => {
const uid = data.uid;
const socketID = socket.id;
userSocketMap.set(uid, socketID);
});
//This function will get called when a user is sending message to another user.
socket.on(SocketEvents.SEND, (data) => {
const to = data.to;
const from = data.from;
const content = data.content;
const payload: MessagePayload = {
to: to,
from: from,
content: content,
};
const dstUser = userSocketMap.get(to); // getting the socket id of the receiver.
// '/#' is the prefix for the socketID, if the socketID already has this prefix, this should be deleted - need to check this.
//MessageEvent.RECEIVE is used to signal the dstUser to receive a message.
io.to("/#" + dstUser).emit(SocketEvents.RECEIVE, { payload: payload });
});
socket.on(SocketEvents.DISCONNECT, (socket: Socket) => {
const userID = getByValue(socket.id);
if (!userID) return;
userSocketMap.delete(userID);
});
});
You should do two things when working with react-native and socket.io in case user lost internet connection. Use socket.io heartbeat mechanism inorder to get the users that lost connection and are not responding and user NetInfo package to inform the mobile user that he has lost internet connection.
Socket.io
var server = app.listen(80);
var io = socketio(server,{'pingInterval': 2000});
io.on("connection", (socket: Socket) => {
socket.on(SocketEvents.INITIATE, (data) => {
const uid = data.uid;
const socketID = socket.id;
userSocketMap.set(uid, socketID);
})
socket.on('heartbeat', (socket: Socket) => {
const userID = getByValue(socket.id)
userSocketMap.MARK_USER_AS_INACTIVE(userID)
})
});
React-Native - use NetInfo - it used to be part of the core but got separated to a community module
import NetInfo from "#react-native-community/netinfo";
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
});
const unsubscribe = NetInfo.addEventListener(state => {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
});
// Unsubscribe
unsubscribe();
Below is my Server side and client side file:
Server side file:
'use strict';
var model = require('../model/model.js');
class Socket{
constructor(socket){
this.io = socket;
this.users = [];
}
socketEvents(){
this.io.on('connection', (socket) => {
socket.on('username', (data) => {
this.users.push({
id : socket.id,
userName : data.username
});
let len = this.users.length;
len--;
model.addSocketId( data.username,this.users[len].id);
this.io.emit('userList',this.users,this.users[len].id);
});
socket.on('getMsg', (data) => {
model.insertMessages({
fromUserId: data.fromUserId,
toUserId: data.toUserId,
message: data.msg
});
console.log("socket id is:");
console.log(data.toid);
socket.broadcast.to(data.toid).emit('sendMsg',{
msg:data.msg,
name:data.name
});
});
socket.on('disconnect',()=>{
for(let i=0; i < this.users.length; i++){
if(this.users[i].id === socket.id){
this.users.splice(i,1);
}
}
this.io.emit('exit',this.users);
});
});
}
socketConfig(){
this.socketEvents();
}
}
module.exports = Socket;
Below is my Client Side File:
<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect("http://192.168.1.12:3000");
socket.on('sendMsg', (data) => {
console.log("send message-list");
$('#message-list').append("<li class='friend-user'>"+data.msg+"</li>");
});
socket.on('userList', (completeUserList,userSocketId) => {
console.log('userlist function');
var userList = completeUserList;
});
$('document').ready(function(){
$(document).on('click', "#msg-btn", function (event) {
var messagePacket = {
toid: $("#friendSocketId").text(), //stored in hidden format from db
msg: $('#message').val(),
name: $("#myUserName").text(),
fromUserId: $("#loginUserId").text(),
toUserId: $("#friendUserId").text(),
};
socket.emit('getMsg',messagePacket);
});
</script>
In this, When user clicks on send button from front-end then "getMsg" event emitting successfully. getMsg function took receiver socket id as message and emit "sendMsg" event to particular socket id. But sendMsg event is not sending msg to particular socket id. please help.
It looks like you don't have a key called toid on data object. You might have mistyped it. Replace toid with toUserId and it should work.
Since you are sending a message to another socket, instead of a room, you can use socket.to().emit() instead of socket.broadcast.to().
From Socket.IO docs:
// a private message to another socket
socket.to(/* another socket id */).emit('hey');
So, replace socket.broadcast.to() with socket.to().emit(), like:
socket.to(data.toid).emit('sendMsg',{
msg:data.msg,
name:data.name
});
EDIT:
You asked the following question in the comments below this answer:
Does socket id changed automatically after
login when user send message or it remains same when client logged in?
Socket id is created for each client when io.connect() is executed.
In your code, this is the first line in your script file. So, a new socket id is created every time this script loads.
So, every time the page containing this script is loaded, a new socket id is created. If you refresh the page, this script loads again and a new socket id is created.
To answer your question, socket id remains same as long as user is on the same page. So, when a user sends a message it doesn't change, unless the user navigates to another page or refreshes this page after sending the message. So, if a user logs out and logs in, this page loads again, so socket id changes.
I think you are storing the socket id in database after user logs in, but are not updating it when user navigates to another page or logs out and logs in again. So, when you are emitting a message from server, you are sending it to user's previous socket id, not the current one.
I am trying to set up a chat interface on a site using nodejs.
I need to update the logged_in_user list after a user is authenticated.
I am using this code as a reference
https://github.com/amirrajan/nodejs-chat
io.sockets.on('connection', (socket) => {
socket.on('sendchat', (data) => {
io.sockets.emit('updatechat', socket.username, data);
});
As you can see on the 'connection' event the updatechat event is triggered.
In the client side (public/app.js),the chatlist is updated on getting the updatechat event.
I need to do the same but after a client is authenticated.
Can I somehow tweak the socket object to trigger the event on a 'login' event
This is my code
app.get('/authenticate',function(request,response)
{
var db = couch.db.use('users');
var email = request.query['email'];
var pass = request.query['password'];
db.get(email,{email:email},function(err,body){
if(err)
console.log(err);
else
{
var user_pass = body['password'];
var name = body['name'];
console.log(user_pass);
if(pass == user_pass)
{
eventEmitter.emit('login',name);
response.render('pages/dashboard',{user:body['name']});
}
}
});
// And on 'login' event
eventEmitter.on('login', function(name)
{
// ?????? Broadcast the new user to all logged in users to update the chatlist
});
I have tried to redirect the logged in users to another port and use the socket 'connection' event there.It sort of works but I want to know if there is another approach I could use.
You should use socket.io middleware for authentication.
io.use(function(socket,next){
//auth here
});
Take a look at the docs http://socket.io/docs/server-api/
You can get the req and res objects from the socket object being passed to the middleware.
Note, it is not a great idea to pass a password on the query since the url is not pass ssl.
How do I get other user's socket.id?
Since socket.id is keep changing, whenever the browser refresh or disconnect then connect again.
For example this line of code will solve my problem
socket.broadcast.to(id).emit('my message', msg);
But the question is How do i get that id?
Let say Jack wants to send a message to Jonah
Jack would use the above code to send the message, but how to get Jonah's socket id?
Just for the record, I already implemented socket.io and passport library so that I could use session in socket.io , the library is call passport-socket.io. So to get the user id in socket.io would be
socket.request.user._id
What i did for this was to maintain a database model(i was using mongoose) containing userId and socketId of connected users. You could even do this with a global array. From client side on socket connect, emit an event along with userId
socket.on('connect', function() {
socket.emit('connected', userName); //userName is unique
})
On server side,
var Connect = require('mongoose').model('connect');; /* connect is mongoose model i used to store currently connected users info*/
socket.on('connected', function(user) { // add user data on connection
var c=new Connect({
socketId : socket.id,
client : user
})
c.save(function (err, data) {
if (err) console.log(err);
});
})
socket.on('disconnect', function() { //remove user data from model when a socket disconnects
Connect.findOne({socketId : socket.id}).remove().exec();
})
This way always have connected user info(currently used socketId) stored. Whenever you need to get a users current socketId fetch it as
Connect.findOne({client : userNameOfUserToFind}).exec(function(err,res) {
if(res!=null)
io.to(res.socketId).emit('my message', msg);
})
I used mongoose here but you could instead even use an array here and use filters to fetch socketId of a user from the array.
The documentation suggests that `socket.on(SOME_MESSAGE's callback is provided an id as the fist param.
http://socket.io/docs/rooms-and-namespaces/#default-room
javascript
io.on('connection', function(socket){
socket.on('say to someone', function(id, msg){
socket.broadcast.to(id).emit('my message', msg);
});
});
i'm beginning with node.js, in my first example i'm creating a chat. This char have two page, the first (index.jade) the user insert your nickname and second page (chat.jade) the user can chat with other users.
i have two problems.
the first: when user insert your name, the system change the page (chat.jade) and return the your name wrote but the code ( io.sockets.emit('nicknames', nicknames); ) no execute and i think that's because when change of page no load de funcion en el client (socket.on('nicknames',function(data){ .....) . Why??
second problem:
when a user send a message, the server send a json with user and message but the client get the user null and message correct. why the user is null??
code:
index.jade
$('#form-login').submit(function(){
var nickname = $('#nickname').val();
if(nickname !== ''){
socket.emit('login',nickname, function(data){
if(!data){
return false;
}
});
}
});
chat.jade
$('#btnSendMessage').on('click',function(){
var message = $("#message").val();
if(message != ''){
socket.emit('sendMessage',message);
$('#message').val('');
$('#message').focus();
}
});
socket.on('nicknames',function(data){
var $p = $('<p>'+data+'</p>');
$('.users').append($p);
});
//app.js
var nicknames = [];
io.sockets.on('connection', function(socket) {
socket.on('login', function(data, callback){
if (nicknames.indexOf(data) != -1){
callback(false);
}else{
callback(true);
nicknames.push(data);
socket.set('nickname', data);
io.sockets.emit('nicknames', nicknames);
}
});
socket.on('sendMessage', function(data){
socket.get('nickname', function(err, name){
console.log('nickname: '+name);
});
var message = { "nickname":socket.nickname, "data":data };
io.sockets.emit('userMessage', message);
});
});
Submitting the form navigates away from this page, thus closing a socket connection to the server.
Because of the above when client arrives at your chat.jade, it establishes new socket connection, which doesn't have socket.nickname set - and also you should be using socket.get('nickname') - which is an asynchronous functions.
have a look at my implementation of chat using socket.io and agular
Use sessions to tie socket connection to a user session, link
Use AngularJs or similar frameworks to build Single Page App, to prevent refreshing, and loosing connection