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);
});
Related
I am having a issue where I am pulling data from a DB via node mysql & Express and passing it via socket.io.... but there's an issue am running into.
All users are updating with the same data rather than unique data per user.
For example:
If user A has just logged in he can see all his account details. But when user B logs in right after he can then see all his details....but it then updates user A details to show user B details as well.
I am trying to ensure user A can can only see his own and same for user B.
I have tried numerous things to stop this happening via JQuery but cant seem to find a resolution.
Below I have trimmed down a the code to a basic example:
HTML
<span id="id-val">User A</span>
<span id="user-val"></span>
Server side
server = http.createServer(app),
io = require('socket.io').listen(server);
function SQLuserData(userval) {
connection.getConnection(function (err, connection) {
connection.query('SELECT val FROM test WHERE name= ?;',
[userval],
function (err, rows) {
var accountval = rows[0]['val'];
if (accountval) {
console.log("Val : " + accountval);
UserVal(accountval);
} else {
console.log("Error | Val: " + err);
}
});
connection.release();
});
}
//Socket.io connection socket
io.sockets.on('connection', function (socket) {
socket.on('sqluser', function (userval) {
SQLuserData(userval);
});
});
//Pass val client side.
function UserVal(accountval) {
io.sockets.emit("valsocket", accountval);
}
Client side
var socket = io.connect();
//Used to grab information for that user from serverside.
$(document).ready(function () {
var userval = $('#id-val').text();
socket.emit('sqluser', userval);
});
//Grabs user value being passed from serverside and updates HTML.
socket.on("valsocket", function (accountval) {
$("#user_val").val(accountval);
});
Does anyone have any advice or potential solutions?
you need to grab and store the socket.id for each connected user
var users = {};
//Socket.io connection socket
io.sockets.on('connection', function (socket) {
socket.on('sqluser', function (userval) {
// 'userval' must be unique for each user
users[userval] = socket.id;
SQLuserData(userval);
});
});
and then use the same id to emit data ti single socket
//Pass val client side.
function UserVal(accountval, userval) {
io.sockets.socket(users[userval]).emit("valsocket", accountval);
}
for socket.io version 1.0 and above
io.to(users[userval]).emit("valsocket", accountval);
I think you want to avoid emitting the account data to all connected users, which is what Socket.IO's emit method does. It might be better have the client send a GET request to the server and respond with the account details to the individual client.
Here are some resources if you choose to use an HTTP request over Socket.IO:
jQuery GET
Express Respond
So basically the problem with your code is that you are not distinguishing between users . Since you are sending data through socket you need to be careful to whom you are sending data.
You can use socketio-auth to create a type of authentication . And then send the data as socket.emit(event, data); Where socket is an individual object per user . You can also use a cookie based session to help you with this .
I have a problem that i don't seems to be able to solve it. I'm doing some kind of integration with remote system and my code is in iframe but that can't be important for this one i hope :).
I'm trying to send a message from server to specific room/client to begin session. First thing I do is when user log in, I emit message from client side with username.
CLIENT.JS
conn.on('connect', function () {
conn.emit('session', { username: 'some_username' });
}, false);
And on server side i get message and join socket to the room.
SERVER.JS
socket.on('session', function(session) {
socket.join(session.username);
});
I have another module that communicates with this server.js script through redis. So i have two more events in server.js
SERVER.JS
var userCreate = redis.createClient();
userCreate.subscribe("userCreate", "userCreate");
var userDestroy = redis.createClient();
userDestroy.subscribe("userDestroy", "userDestroy");
userCreate.on("message", function(channel, data) {
socket.to(JSON.parse(data).username).emit('beginSession', data);
});
userDestroy.on("message", function(channel, data) {
socket.to(JSON.parse(data).username).emit('endSession', data);
socket.leave(JSON.parse(data).username);
});
But when ever i try to emit message from server to client i broadcast message to everyone. What am I doing wrong?
Well, from the syntax point of view you are doing everything correct.
Didn't you forget to specify the userId property in the endSession?
userDestroy.on("message", function(channel, data) {
socket.to(JSON.parse(data).userId).emit('endSession', data);
socket.leave(JSON.parse(data).userId);
});
If that doesn't work - you should provide the contents of a data object
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 trying to proxy emails via NodeJS in order to do very custom processing on outgoing emails on our test server.
This is what I have:
var net = require('net');
var server = net.createServer({allowHalfOpen: true}, function(socket) {
console.log('New connection established.');
socket.setEncoding('utf8');
socket.on('data', function(data) {
console.log(data);
});
socket.on('end', function() {
console.log('Connection closing.');
});
socket.resume();
});
server.listen(25);
It does not yet process the emails, because it simply doesn't even work. I get the connection established message in console every time I send an email, but the data event never gets fired. I'm not sure if it's that the data already came before I bound the event listener, or whether I'm supposed to talk to the client first (HELO?).
I'm trying to access the email contents, basically.
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.