I'm trying to implement private messaging in an app im creating using express 3 and socket.io
When a client connects a room with the clients userid is automatically created and joined. This is mainly for notifications and that sort of stuff. Now im trying to make this work for private chat too.When a user clicks the send button, the message gets sent along with the userid of the sender (from session userid) and the userid of the owner thats grabbed from a hidden field or attribute of element. And the sender joins the room with the owners userid namespace. The problem with this is that when the sender goes to another page or refresh the browser he is diconnected from the room, and doesnt recieve any further messages from the owner. He has to send a new message to rejoin the owners room. Now how do i percist the connection to the owners room? Or am i doing this all wrong? Is there a better or standard way to achieve this?
SERVER:
io.on('connection', function (socket) {
var sh = socket.handshake;
socket.join(sh.session.userid);
socket.on('chat', function (data) {
if (data.user) {
socket.join(data.owner);
}
io.sockets.in(data.owner).emit('chat',
{user: sh.session.user, message: data.message});
});
});
CLIENT:
var socket = io.connect('https://localhost/');
$('#send_message').click(function (e) {
socket.emit('chat',{message: $('.message').val(),
user:$('#user'), owner:$('#owner')} //Get this from hidden fields in chat form );
e.preventDefault();
});
socket.on('chat', function (data) {
$('ol').prepend('<li>' + data.user + '<br />' + data.message + '</li>');
});
Right. Because when you reload the page, the server gets a "client disconnected" message and unsubscribes the socket. The client will need to re-emit a 'chat' message (with same owner id) in order to get back onto the private feed.
One way is to have the client save the owner id in a cookie and recall it on every page load. Alternatively, you could have the server store and recall this info using a session cookie (http://www.senchalabs.org/connect/session.html), which, in essence, is much like the first option.
Related
I have used methods socket.on and io.emit, And i got response to all users. But, i want to get response for particular user.
But my application contains login functionality and i followed this post on stackoverflow, and they are saying we need unique userId and socketId in an object for a particular user to emit an event for a particular user.
But i am getting the userId after login, But we want it when user connect to app.
So can anyone please help me with the same?
In your node.js, create a global array 'aryUser', each element contains the socketid and loginid.
node.js onConnect (new connection), add a new element to the array with the socketid and set loginid = empty.
after the user login, emit an event from client to the server, e.g:
socket.emit('userloginok', loginid)
in node.js, define a function:
socket.on('userloginok', loginid)
and in this function, search the aryUser with the socketid and replace the empty loginid inside the array element with the parm loginid.
in node.js, define the function:
socket.on('disconnect')
and in this function, search the aryUser, use aryUser.splice(i,1) to remove the user just disconnected.
that means, aryUser contains all users connected, some of them logined, some of them not logined. And you can use the socketid of the array to send message to particular user, and/or all users.
Example Source Code:
server.js
http://www.zephan.top/server.js
server.html
http://www.zephan.top/server.html.txt
rename server.html.txt to server.html, put server.html and server.js in the same directory, and run:
node server.js
Yes, you definitely need socketId in order to send and receive messages between two specific users.
UserId is required just to keep track of socketId associated with the particular user or you can manage it with some other way as well that's up to you.
As per your question, you have userId of the user and you need socketId of that user! So, in this case, you can pass userId when that particular connects to a socket server from the client side as shown in below snippet,
const socket = io(this.SOCKET_SERVER_BASE_URL, { query: `userId=${userId}` });
And you can read this user on nodejs server like this,
const userId= socket.request._query['userId'],
const socketId= socket.id
Now store this socketId in somewhere, for example, Redis or some sort of caching mechanism again up to you, just make sure fetching and retrieval should be fast.
Now while sending a message just pull the socketId from your cache and emit the message on that socketId by using below code,
io.to(socket.id).emit(`message-response`, {
message: 'hello'
});
I have written a complete blog post on this topic on both Angular and AngularJs, you can refer those as well.
Edit 1:
Part 1 =>
When your user completes the login request, then make the connection to the socket server.
Assuming you are using React Or Angular After a successful login you will redirect your user to home component(page). On the Home component(page) make the socket server connect by passing the userId just like this,
const socket = io(SOCKET_SERVER_BASE_URL, { query: `userId=${userId}` });
P.S. you can get userID from URL or maybe using a cookie that is up to you.
Once you receive this socket connection request on the server, then you can read the userID query and you can get socketId associated with it and store it in cache like this,
io.use( async (socket, next) => {
try {
await addSocketIdInCache({
userId: socket.request._query['userId'],
socketId: socket.id
});
next();
} catch (error) {
// Error
console.error(error);
}
});
Part 2 =>
Now, let's say you have a list of the users on the client side, and you want to send a message to particular users.
socket.emit(`message`, {
message: 'hello',
userId: userId
});
On the server side, fetch the socketId from the cache using UserId. Once you get the socketId from cache send a specific message like this,
io.to(socketId).emit(`message-response`, {
message: 'hello'
});
Hope this helps.
I am creating customer service chat application that get data from client website to node.js server then send this data to agent and the agent reply to the client..
Server code:
var ws = require("nodejs-websocket");
var clients = [];
var server = ws.createServer(function(conn){
console.log("New Connection");
//on text function
conn.on("text", function(str){
var object = JSON.parse(str);
conn.sendText("Message send : " + object);
console.log("User ID: " + object.id);
clients.push(object.id);
var unique=clients.filter(function(itm,i,a){
return i==a.indexOf(itm);
});
/*
conn.on('message', function("test") {
console.log('message sent to userOne:', message);
unique[0].send("Message: " + message);
});
*/
console.log("Number of connected users : " + unique.length);
//closing the connection
conn.on("close", function(){
console.log("connection closed");
});
});
}).listen(process.env.PORT, process.env.IP);
Everything works perfectly and I have each client ID but the
I want to reply with a message to that client ID..
What I have tried:
I have tried to reply to the client using conn.send("message", callBackFunction) but it send to all not with a specified user ID.
Disclaimer: co-founder of Ably - simply better realtime
You have two problems there I suspect. Firstly, if you ever need to scale to more than one server you've got problems as you will need to figure out how to pass messages between servers. Secondly, you have no way of maintaining state between disconnections which will happen as part of normal behaviour for clients.
The industry typically approaches this type of problem using the concept of channels as it scales, it decouples the publisher from the subscriber, and it's quite a simple concept to work with. For example, if you had a channel called "client:1" and you published to that channel, and your subscriber was listening on that channel, then they would receive the message. You can find out more about how we have designed our realtime service around channels, I would suggest you do consider that pattern in your system.
Matt, co-founder, Ably
Environment:
Nodejs+Socketio
Problem:
client A and client B both connect to server;
client B is offline;
client A sends a message to client B(client B still offline);
client B connect to server again;
[Problem] client B can't receive the message from A;
Server Code
var clients = {};
io.sockets.on('connection', function (socket) {
socket.on('online', function (data) {
if (!clients[data.username]) {
clients[data.username] = socket;
}
io.sockets.emit('message', data.user + 'online now');
});
socket.on('say', function (data) {
if (data.to == 'all') {
io.sockets.emit('message', data.message);
} else { //to specific client
clients[data.to].emit('message', data.message);
}
});
});
Description
client B connected to server at one place first.During the period of client B's offline, client A sent messages to client B. Then client B connect to server at another place again, and client B needs to receive those message from client A. How to make it work?
The amount of code I would have to write would be fairly large to create a solution if I consider which db and it's config, and client. You basically have to persist your messages in a database. As messages come in you would have to write to your conversation object (or whatever is representing the chat messages between clients).
socket.on('say', function (data) {
// pseudo code to save conversation
// var conversation = db.getConversation();
// conversation.addMessage(data.message);
// conversation.save();
if (data.to == 'all') {
io.sockets.emit('message', data.message);
} else { //to specific client
clients[data.to].emit('message', data.message);
}
});
Then you would have to get all messages from the database when a client joins.
socket.on('online', function (data) {
if (!clients[data.username]) {
clients[data.username] = socket;
}
// pseudo code to get messages and display to user on first load
// var conversation = db.getConversation();
// var messages = conversation.getLast10Messages();
// messages.forEach(function(message) {
// clients[data.username].emit('message', message);
// });
io.sockets.emit('message', data.user + 'online now');
});
use message queue like RabbitMQ. whenever a message comes from socket write to receiver's queue and when the receiver joins he will pick it from the queue.
You can specify a unique id as username to every user , save it at the server side, or use username, you also have client id(socket id), then save them in an object, now for every user you have an object that contains (username or unique id ) and socket id, now its easy to save messages when user is offline and then send it to user.
Before emitting every event, you can search for socket id in the connected sockets object of socket.if socket id exists, you can emit, else , you still have username, and you can save messages in database by username.
Remember that, you must send receiver object info(username or unique id , and socket id) in every emitting from the client
I am learning NodeJS and Socket.IO and have been following the various tutorials dotted about, mainly focusing on making a basic chatroom.
My app works so far: being able to send messages with clients being able to move to different "rooms".
Now I am trying to tidy it up and make it look the part.
I am stumbling with the follow idea:
When user A moves form room A to B, an emit() method is sent to update an array with a user count, which in turn is sent back to the sender. A list of rooms in a side panel is then updated e.g. Room A (0), Room B (1).
Now, emitting the new data to the sender is easy and works, the room list gets updated, and the other rooms (that the sender is not in) have hyperlinks (this is how the user moves between rooms)
But I want to send data to the other clients, so their room lists also get updated. Sending the same data as before means the other clients's rooms list is incorrect, as the data is referencing the new room name the sender joined, not the room the other client is currently in.
Some code:
socket.on('switchRoom', function(data) {
var newroom = sanitize(data).escape().trim();
socket.leave(socket.room);
socket.join(newroom);
socket.room = newroom;
socket.emit('updaterooms', rooms, newroom);
// this is the problem area, socket.room should be the socket.room data for the non-sending client
//socket.broadcast.emit('updaterooms', rooms, socket.room);
});
How would I emit to all other clients (not the sender) with the data contained in their own socket "session" (i.e. socket.room)?
You will need to emit a generated message that's catered to each socket. What you will need to do is iterate through each socket, emitting the custom message based on each socket, for example:
io.sockets.on('connect', function(socket) {
socket.on('switchRoom', function(data) {
var newroom = sanitize(data).escape().trim();
socket.leave(socket.room);
socket.join(newroom);
socket.room = newroom;
socket.emit('updaterooms', rooms, newroom);
io.sockets.clients.forEach(function(client) {
if (socket != client) {
client.emit('updaterooms', rooms, client.room);
}
});
});
});
I have a phonegap app in which i connect to my node.js socket like so:
var socket = io.connect('http://54.213.92.113:8080');
It works fine but when I go to a different page, the socket gets disconnected.
I could just write the same code in the javascript on the next page but that's messier than I think it needs to be - as it would open up a new connection when it could have just stayed connected in the first place.
Is there any way to stay connected to the socket even if I switch pages?
suppose you have a multi-page application then ,here you can do a trick that when your socket gets connected first time when the page loads then you can assign the session id to that particular connection like this.and then bind that connection to that session.
io.on('connection', function(socket) {
socket.on('start-session', function(data) {
console.log("============start-session event================")
console.log(data)
if (data.sessionId == null) {
var session_id = uuidv4(); //generating the sessions_id and then binding that socket to that sessions
socket.room = session_id;
socket.join(socket.room, function(res) {
console.log("joined successfully ")
socket.emit("set-session-acknowledgement", { sessionId: session_id })
} else {
socket.room = data.sessionId; //this time using the same session
socket.join(socket.room, function(res) {
console.log("joined successfully ")
socket.emit("set-session-acknowledgement", { sessionId: data.sessionId })
})
}
});
Now you had binded the socket connection to a session now you are sending an acknowledgement too at the client side also .There what you can do is that store the session id to the web browsers session storage like this
At client side code
socket.on("set-session-acknowledgement", function(data) {
sessionStorage.setItem('sessionId', data.sessionId);
})
This will store the session id in the browsers session storage.Now when the page is navigated from page1 to page2 and so on. then send that session id to the server so that you will be connected to the same session logically like this
var session_id;
// Get saved data from sessionStorage
let data = sessionStorage.getItem('sessionId');
console.log(data)
if (data == null) {
session_id = null//when we connect first time
socket.emit('start-session', { sessionId: session_id })
} else {
session_id = data//when we connect n times
socket.emit('start-session', { sessionId: session_id })
}
So basically the logic behind is that we can use same session for multiple socket connections by doing this as every time the socket will be joined to that particular room only and emit the events which you can listen on server side and vice a versa.
Only if you build it as a single page application where the actual page doesn't reload load when navigating. However it would probably be better to design your socket.io code and your server side to be resilient to frequent socket connect / disconnect. This is especially true for code written to run on a cell phone.