Socket.io, emit to a single client - javascript

I have a chat app that allows multiple people to connect to a chatroom, When a new client connects to the chatroom i want to be able to show them the chat history, i've done his my emitting an chat history but obviously that updates everyone in the chat leading to duplicate messages.
How would i single out the new user and display the chat just to them?
this is the server code
socket.on('chat history', function(){
console.log("Chat History");
posts.find({chatId: chatId}).then(function(chatHistory){
console.log("Chat history", chatHistory);
socket.emit('chat history', chatHistory);
})
});
This is the client side code
socket.on('chat history', function(chatHistory){
for (var key in chatHistory) {
var obj = chatHistory[key];
addChatMessage(obj);
}
});
I am using the socketio example, the data is sending back to this method and the add chat message updates everyone.

In order to communicate to an individual, you'll require to capture their ID. You can do this by accessing the socket object, assuming that's the name of what you're creating on the connect action as illustrated in this block of server-side code:
io.on('connection', socket => {
socket.on('chat history', async function(){
console.log("Chat History");
const chatHistory = await posts.find({chatId: chatId})
console.log("Chat history", chatHistory)
socket.to(socket.id).emit('chat history', chatHistory)
})
})
In your example I've swapped the promise for async/await for the sake of readability, but you can accomplish the same using your preferred pattern. Just trust in this case that the ID which you seek to capture will always be kept to that context within the socket object that you generate on connection.

You can use below code line to send event/message to particular user/client:
socket.broadcast.to(socketid).emit('chatHistory', data);

Related

Socket.io + express rooms

I am working on an online website with real-time chat and I've been trying to set up socket.io rooms for a few days now, but one of my custom events just doesn't emit.
My website has 2 paths, / and /play. When someone fills out a form in the / path, the website redirects them to the /play path.
const io = require('socket.io')(listener);
io.on('connection', socket => {
socket.on('add user', data => { // This event gets emitted when the user submits the form at `/`, in a client-side js file.
socket.username = data.username;
socket.room = data.room;
socket.score = 0;
socket.join(data.room);
io.sockets.in(data.room).emit('user joined', { // This event gets 'handled', in another (not the same one) client-size js file.
username: data.username,
room: data.room
});
});
socket.on('disconnect', function () {
io.sockets.in(socket.room).emit('user left', {
username: socket.username,
room: socket.room
});
socket.leave(socket.room)
});
});
In the other client-side js file:
var socket = io.connect();
let joined = 0;
socket.on('user joined', data => {
joined++;
console.log(joined, data)
const elemental = document.createElement("LI");
const info = document.createTextNode(data.username);
elemental.appendChild(info);
document.getElementById('people').appendChild(info)
});
The add user event code executes just fine, but the user joined one doesn't..
I'm almost certain that it has to do with the paths. I read on socket.io namespaces but it seems like they are just like a different type of rooms.
Can someone tell me what's wrong?
EDIT: I should mention that no errors come up.
Without seeing your whole code, I'm going to assume that the socket on client-side.js code, is not joined to the data.room room, and that's why you don't get anything on the 'user join' event listener and that happens because you probably have multiple socket.io connections on the client side code.
You have multiple ways to solve this, depending on what you're trying to achieve.
Instead of only emitting to people on the room, emit to all sockets that an user joined a specific room. io.emit('user joined') instead of io.sockets.in(data.room)...
There should only be one single var socket = io.connect(); on the front end, otherwise the socket that's emitting: add user and therefore joined to data.room is not the same socket that's listening on: user joined, and that's why you never get the event on that socket, because you have 2 different sockets, one that joined the room, and one that is doing absolutely nothing except waiting for an event that will never occur.

Socket.io message after database update

Is it possible to send message (for example using alert) to all users when admin changed something in database?
situation: Users browsing car offers and while doing this admin changed price of few offers --> users gets notifications.
Just couple the event of the database update to an emit like this:
Backend
io.on('connection', (socket) => {
console.log('A user connected');
// handling event from the front-end:
socket.on('clientEvent', function(data) {
// Database update happens before this
socket.emit('databaseUpdate', { description: 'Database is updated'});
});
});
This way every time a database update happens a new event will be emitted to the frontend to all the users which are connected. Your frontend now can listen to it as follows (the frontend who is connected listened to emitten databaseUpdates from the backend):
Frontend
var socket = io();
// now we just log the updated data but in this callback you provide your own implementation.
socket.on('databaseUpdate', (data) => console.log(data.description));
Hopefully you find this answer usefull more info here
source1
Source2
You can use socket.blast() at the end of each db operation.
So, if any user is listening to the blasted message, you can make the API call so that it fetches the new record.
[http://node-machine.org/machinepack-sockets/blast][1]

return client socket.io to variable

On the docs page they say that i need to use like this.
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
there is any way to expose the client to a variable?
var socket = io.on('connection');
socket.emit('news', { hello: 'world' });
On the help page they say that Server#onconnection(socket:engine#Socket):Server expose a client, but i can't figure out how to use it. doc
this way i can use socket inside my other functions.
Right now on every function that i emiting stuff i do the io.on('connection', function (socket) all over again
Another question:
There is a way to different files emit event to each other
app.js
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
file1.html emit
<script src="socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost');
socket.emit('event15', function(x){
});
</script>
file2.html receive
<script src="socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost');
socket.on('event15', function(x){
});
</script>
On the server you have to deal with multiple sockets, one for each client. That's why on the server, you write an event handler that sets up the socket for each new client that connects. The basic idea is this:
Server:
io.on('connection', function(socket) {
socket.on('chat message', function(msg) {
io.emit('chat message', msg);
});
});
All of your message handling is supposed to be setup in the outer function. If you want to use the socket elsewhere however, you can pass it on like this:
io.on('connection', function(socket) {
socket.on('login', function(data) {
myServer.attemptLogin(socket, data.user, data.hash);
});
});
The attemptLogin(socket, user, hash) function can now process the parameters, then reply to the client by calling socket.emit()
If you want to store each user's socket in a variable, you need to create a data structure to do that. Here's an example:
var users = {}; // empty object
io.on('connection', function(socket) {
users[socket.id] = socket; // store socket for later use
// ...
});
This is only useful though if a connected user can somehow resume their previous session after a disconnect. Otherwise this is just a question of properly organizing your functions and variables.
As for the second question, you cannot send data from one HTML document to another. As soon as the user clicks a link leading to file2.html, a new socket will be opened and socket.io won't even realize that this is still the same user. As far as socket.io is concerned, some random dude just opened file2.html elsewhere in the world.
There are two ways around this: you can create a single-page app, where the user never actually navigates to another site. However they will still lose the connection if they accidentally close the tab, or navigate back, or refresh the page.
The other way is to use some kind of session information. If the user logs in, you can simply store their username and password hash in localStorage. On each page, check localStorage for the stored credentials, then send a "login" message to the server.

Socket.io, message to yourself

Socket.io doesn't display messages send on yourself ip.
For example
var id = 333;
socket.broadcast.to(id).emit('user', user);
It working good, but message is only in client #333, but user than sent message, do not have a copy in the message client.
I wanted to solve in this way, but it does not work
socket.broadcast.to(socket.id).emit('user', user);
Why?
Without more code its hard to say what you want but one thing is certain in order to send a message to a single user you must use that socket object and use socket.emit
As far as i know broadcast is only used to tell everyone except for yourself.
What i usually do when it comes to keeping track of users is i have the following:
var userList = [];
io.on('connection', function (socket) {
socket.on('userData', function (userDetails) {
userDetails.socket = socket;
userList[userDetails.id] = userDetails
});
});
Basicly when a user connects to my socket and the page for the user is fully loaded it sends its id (or a token if you wish) i then map the user's socket into the list so i can quickly pick it up again if i wish to send to that user.
An example could be:
user.id = 33 connects to our server
Once loaded the users emits to our server userData function
The socket is then taken and put into the list at row 33
When we need to we can this use the following code to get the users socket:
socket = userList[33];
or if we have the object:
socket = userList[user.id];
I hope this helps you.
For this, you can use socket.emit('message').
socket.emit: Emit for only one socket.
Hope this will help you. You can also check out this link: socket.io send packet to sender only

get tab's socket instance on server

I have to get socket instance in my ajax request on server in node.js module. Here is my code.
app.js
io.set('authorization', function (handshake, callback) {
if (handshake.headers.cookie) {
cookieParser(handshake, null, function(err) {
handshake.sessionID = handshake.signedCookies['express.sid'];
});
} else { return callback('No cookie transmitted.', false); }
});
io.sockets.on('connection', function(socket) {
var session = socket.handshake.session;
var userid = session.userid;
socket.join("room");
//make user offline
socket.on('disconnect', function() {
//my code goes here...
//make user offline
})
});
Now in one of my ajax request, I want socket instance
app.post('/logout', function (req, res) {
//here i want socket instance, so I can emit message to all socket, accept this.
});
As I know, each tab creates it's own new socket connection, but session is unique between all tabs of browser. So, How Do I store socket for each tab on server side, where I can find easily socket instance, and then broadcast message to all sockets, excluding that socket which is creating events. (means user's active tab's socket connection)
any guess.
thanks
In my app, I can do what you say because i use namespace and room and so in a room i can find every socket of someone.
io.of('/user').clients(idRoom);
So that i can remove every socket of the user. But if you cannot use this, i think in your app you will have to implement outside socket.io a class for someone (using session as a way to see if it's already have a socket open or if you have to create a new instance). And in this class, have a socket table so that you will be able to handle socket of someone.
In my case, i do the same except that i use the room of socket.io to do that.
And to broadcast to every socket, it depends what is your app. If your app send to anyone in the same namespace, it doesn't change anything because the socket of the same session will also receive the message. But if not, you will have to implement a function to emit to every socket of the table i suggested above.
In my case i use the 'exclude' to ensure the current socket doesn't receive the message but usually you can use broadcast.
io.of('/user').in(this.id).except(socket.id).emit('msg', { text: text,type:person});
To conclude, socket.io will not help you to handle session and several socket for one user/session but you can manage to deal with it using room feature (in my case it was the best way), or implement a user class where you will manage a table of your session sockets.

Categories

Resources