Forward all messages to socket.io room - javascript

If the client emits a message to a room, how can I get that message sent to all other clients in the room?
Currently on the server I have to do:
io.on('connection', function (socket) {
socket.on('join', function (room) {
socket.join(room);
socket.on('food.create', function (foods) {
socket.broadcast.to(room).emit('food.create', foods);
});
socket.on('food.update', function (foods) {
socket.broadcast.to(room).emit('food.update', foods);
});
socket.on('food.remove', function (foods) {
socket.broadcast.to(room).emit('food.remove', foods);
});
});
});
io.listen(3000);
Which is fine now there's only 3 messages, but when I add more it's going to get long. Does socket.io provide a way of automatically forward all messages form one client to all the other clients in that room?

This is the example off the socket.io docs page that shows how to use events and key:value sets for data:
var io = require('socket.io')();
io.on('connection', function(socket){
socket.emit('an event', { some: 'data' });
});
Why not use this construct to pass the messages around? Put your data in the data object along with what you want the respective client to do with that data as a key:value set. Something like this:
io.to('your_room').emit('food', { todo: 'delete', name: 'macaroni' });
});
The server can then simply broadcast that event to all other clients in the room. That way you don't have to filter messages on the server at all.
The other clients then receive the message and do whatever you want based on the data.todo and data.name values.

Related

Vue & Sockets: Handling sending messages back and forth

I have a simple implementation, or an attempt at one, of a messaging system just to show an alert of a message to different users. I'm using https://www.npmjs.com/package/vue-socket.io Vue Socket Io but it's just a socket.io. I am attempting to have the client subscribe to an event in mounted. The name is based on their userID. The problem is that my implementation below doesn't work to show the alerts. I can see the event being subscribed to in mount and I can see sending message console log in the server so I know that is getting fired but I don't see the alert being triggered by the emit(Chat_....
server:
io.on('connection', function(socket) {
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
socket.emit(`CHAT_${data.user}`, data.msg)
});
});
client:
On the client, the userIDSelf is a user ID that is passed in as a prop. It's the User's logged in ID and in mounted, they automatically subscribe to the a chat channel with their userid appended to it, like a custom channel.
mounted() {
this.sockets.subscribe(`CHAT_${this.userIDSelf}`, (data) => {
alert(data)
});
},
there is a function sendMessage that takes the values from 2 inputs (not seen) in the template. You have to enter a user ID on who you want the message sent to and then another input with the actual message. These are sent over to the backend server listed above.
methods: {
sendMessage() {
this.$socket.emit('sendMessage', {user: this.userIDReceiver, msg: this.message})
},
}
I see a logical problem here. In your server code
io.on('connection', function(socket) {
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
socket.emit(`CHAT_${data.user}`, data.msg)
});
});
the socket (User 123) which emitted sendMessage event to the server, will eventually also be the socket which will receive the emitted CHAT_456 event from the server. But User 123 socket only listens to CHAT_123 events. The socket is sending itself a message but doesn't receive it.
Possible Solutions
A - the room approach
Once a socket connects on the server, throw it in a room based on it's user id. This requires sending the userid to the server during connection, e.g. with a query parameter. In the client just add a token query parameter to the connection string like
const io = require('socket.io-client');
const socket = io("https://server-domain.com/?token=" + userid);
and through the connecting socket in a room (join) on the server side like
io.on('connection', function(socket) {
const userid = socket.handshake.query.token;
socket.join(userid);
});
then your socket sendMessage would work like this
/* ... server side */
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
io.to(data.user).emit('chatMessage', data.msg);
});
B - the keep reference to the socket approach
Internally every socket has a socket id. Normally these are not exposed the user/clients. However you do have a unique user id. So let's keep a user id / socket instance - relation on the server side, so you can get a reference to the socket object for each corresponding user, using the user id.
The most basic example for it would be a in-memory store on the server side
let socketRefs = {};
io.on('connection', function(socket) {
const userid = socket.handshake.query.token;
socketRefs[userid] = socket;
});
then your socket sendMessage would work like this
/* ... server side */
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
let targetSocket = socketRefs[data.user];
targetSocket.emit('chatMessage', data.msg);
});

How do i make my chat message private?

Now my chat is working but it works like a group message as a third party will be able to see the messages. I want to make it private between two users so that when a third user log in, they will not be able to see the other users talking.
var io = socket(server);
io.on("connection", function(socket){
console.log("made socket connection", socket.id);
socket.on("chat", function(data){
var messages = {
session: data.session,
message: data.message,
sender: data.handle,
};
MsgModel.create(messages);
io.sockets.emit("chat", data);
});
socket.on("typing", function(data){
socket.broadcast.emit("typing", data);
});
});
Emit a join event from client whenever a new client joins with their userid as data. Then on server side, you can use this join event to add user to a room with its userid.
io.on('connection', function(socket){
socket.on("join", function(data){
socket.join(data.userId)
})
})
Now you have rooms with every userid and if you want to send some specific user something, you can emit to that room,
io.to(userid).emit(data)

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: Avoiding server to client to sever communication

This may seem odd but I have a situation where I'm keeping an array of uses in io.on('connection', function(socket) { ... }); scope for each user currently connected to a room. So when a user disconnects, I need to update that array on the server. The issue is, since this array is unique to each socket/user I can't just update the array directly.
So what I'm doing is broadcasting an update to all users then immediately having users emit a message back. Is there a cleaner method of doing this?
Quick and dirty example:
// server.js
io.on('connection', function(socket) {
var usersArray = getUsersArray();
socket.on('user array update', function() {
usersArray = removeUser(user);
});
socket.on('disconnect', function() {
socket.broadcast.to(room).emit('user disconnected', user);
});
});
//client.js
socket.on('user disconnect', function(user) {
socket.emit('user array update', user);
});
I'm new to node.js but this ping pong type communication feels a bit unnecessary, is this pretty standard or is there a better way I should be doing this?

Socket.io event listening on a specific room

I'm not sure it is possible, but I really need that feature.
Can I do something like:
client side
sio = io.connect(url);
sio.to('room1').on('update', function(roomName) {
console.log(roomName);
});
Which will be triggered whenever an update event is emitted to 'room1'?
server side:
sockets.to('room1').emit('update', 'room1');
That, unfortunately, doesn't work just on the client side.
But you can do it on the server side.
Server Setup:
sockets.on('connection', function (socket) {
socket.on('join', function (room) {
socket.join(room);
});
});
//...
sockets.to('room1').emit('update', 'room1');
Client:
sio.emit('join', 'room1');
sio.on('update', function (room) {
console.log(room);
});
The "easy" way is to make sure that your emits from the server include the room in the payload.
sockets.to(room).emit('update', {room: room, message: 'hello world!'})
Most probably you want to do the same on the client-side of things.
So that the messages sent from the client to the server includes a room identifier so that the message can be routed correctly.
Or you implement name spaces.

Categories

Resources