I've beeen scouring the Net with no luck. I'm trying to figure out how to send a private message from one user to another. There are lots of snippets, but I'm not sure about the client/server interaction. If I have the ID of the socket I want to send to, how do I send it to the server, and how do I ensure the server only sends the message to that one receiver socket?
Is there a tutorial or walkthrough that anyone knows of?
No tutorial needed. The Socket.IO FAQ is pretty straightforward on this one:
socket.emit('news', { hello: 'world' });
EDIT: Folks are linking to this question when asking about how to get that socket object later. There is no need to. When a new client connects, save a reference to that socket on whatever object you're keeping your user information on. My comment from below:
In the top of your script somewhere, setup an object to hold your users' information.
var connectedUsers = {};
In your .on('connection') function, add that socket to your new object. connectedUsers[USER_NAME_HERE] = socket; Then you can easily retrieve it later. connectedUsers[USER_NAME_HERE].emit('something', 'something');
Here's a code snippet that should help:
Client-side (sending message)
socket.emit("private", { msg: chatMsg.val(), to: selected.text() });
where to refers to the id to send a private message to and msg is the content.
Client-side (receiving message)
socket.on("private", function(data) {
chatLog.append('<li class="private"><em><strong>'+ data.from +' -> '+ data.to +'</strong>: '+ data.msg +'</em></li>');
});
where chatLog is a div displaying the chat messages.
Server-side
client.on("private", function(data) {
io.sockets.sockets[data.to].emit("private", { from: client.id, to: data.to, msg: data.msg });
client.emit("private", { from: client.id, to: data.to, msg: data.msg });
});
Although we have nice answers here. However, I couldn't grasp the whole client server unique user identification pretty fast, so I'm posting this simple steps in order to help whoever is struggling as i did.....
At the client side, Get the user's ID, in my case I'm getting the username...
Client side user registration
//Connect socket.io
var systemUrl = 'http://localhost:4000';
var socket = io.connect(systemUrl);
//Collect User identity from the client side
var username = prompt('Enter your Username');
socket.emit('register',username);
The Listen to register on the server side to register user's socket to connected socket
Serve side code User registration
/*Craete an empty object to collect connected users*/
var connectedUsers = {};
io.on('connection',function(socket){
/*Register connected user*/
socket.on('register',function(username){
socket.username = username;
connectedUsers[username] = socket;
});
});
Send Message from the client side
$(document).on('click','.username',function(){
var username = $(this).text(),
message = prompt("type your message");
socket.emit('private_chat',{
to : username,
message : message
});
});
Receive message on server and emit it to private user
/*Private chat*/
socket.on('private_chat',function(data){
const to = data.to,
message = data.message;
if(connectedUsers.hasOwnProperty(to)){
connectedUsers[to].emit('private_chat',{
//The sender's username
username : socket.username,
//Message sent to receiver
message : message
});
}
});
Receive message on client and display it
/*Received private messages*/
socket.on('private_chat',function(data){
var username = data.username;
var message = data.message;
alert(username+': '+message);
});
This is not the best, however you can start from here....
The easiest way I can think of is to have an hash of all the users on using their id or name as the key and have their socket as part of the value then when you want to send a message to just them you pull that socket and emit on it... something like this:
users[toUser].emit('msg',"Hello, "+toUser+"!");
if you have a web site that has register users with uid then you can create a room for each user and name the room by uid of the user.
first connect client to the server using :
var socket = io('server_url');
on the server side create an event for detecting client connection:
io.on('connection', function (socket) {}
then you can emit to client inside it using socket.emit(); and ask uid of current user.
on the client side create an event for this request and then send uid of the user to server.
now on server side join the connected socket to room using :
socket.join(uid);
console.log('user ' + socket.user + ' connected \n');
now you can send private message to a user using following line:
io.to(uid).emit();
if you use the code above, it doesn't matter how many tab user has already open from your web site . each tab will connect to the same room.
in socket.io 1.0 use
io.sockets.connected[<socketid>]
you can store just socket id. like so:
var users = {};
users[USER_NAME] = socket.id;
then:
io.sockets.connected[users[USER_NAME]]
.emit('private', {
msg:'private message for user with name '+ USER_NAME
});
You can create an unique room for messaging between USER_A and USER_B and both users must join to this room. You may use an UUID as a ROOM_ID (the 'socketId' in the following example).
io.on('connection', socket => {
socket.on('message', message => {
socket.to(message.socketId).emit('message', message);
});
socket.on('join', socketId => {
socket.join(socketId);
});
});
See Joining Rooms and Emit Cheatsheet
The way I got it to work is by introducing one more event "registered user". This basically triggers on the client side as soon as there is a registered user and then emits this even and passes "currentUser._id"
Client Side:
var socket = io();
<% if (currentUser) { %>
socket.emit('registered user', "<%= currentUser._id %>");
<% } %>
Server Side: Once the "registered user" even triggers, it joins the current user socket to the new room with the name which is that user id. So basically each registered user will be in a room which is his/her uid.
socket.on('registered user', function (uid) {
socket.join(uid)
});
Server Side: One there is a message sent, I pass the id of the user the message is sent to in the msg object and then emit to that specific room.
// Private chat
socket.on('chat message', function (msg) {
const uidTo = msg.uidTo
socket.in(uidTo).emit('chat message', msg )
}); `
});
Related
I've just started working with Socket.io and Redis for pub/sub messaging and it's pretty great. One important feature of my application is that the server needs to be able to broadcast messages to all subscribers of a room, and also choose 1 subscriber in that room and narrowcast a message just to them. For now, that subscriber is chosen at random. Based on reading socket.io's documentation, I think I can accomplish this.
However, I've come across something I don't understand. In Socket.io's Default Room documentation (https://socket.io/docs/rooms-and-namespaces/#default-room), they say that each socket automatically joins a room named after its socket ID. This looks like it would solve my narrowcast requirement -- look at the list of client IDs connected to my "big" room, choose one at random, and then send a message to the room with the same name as the chosen ID.
However, it doesn't work because for some reason all clients are joining each others' default rooms. I'm not seeing any exceptions in my code, but my "narrowcast" messages are going to all clients.
Here's my server-side code:
var io = require('socket.io');
var redisAdapter = require('socket.io-redis');
var server = io();
server.adapter(redisAdapter({ host: 'localhost', port: 6379 }))
server.on('connect', (socket) => {
console.log(`${socket.id} connected!`);
socket.join('new');
server.emit('welcome', `Please give a warm welcome to ${socket.id}!`);
server.to(socket.id).emit('private', 'Just between you and me, I think you are going to like it here');
});
server.listen(3000);
setInterval(whisperAtRandom, 2000);
function whisperAtRandom() {
server.in('new').adapter.clients((err, clients) => {
if (err) throw err;
console.log('Clients on channel "new": ', clients);
chosenOne = clients[Math.floor(Math.random()*clients.length)];
console.log(`Whispering to ${chosenOne}`);
server.to(chosenOne).emit('private', { message: `Psssst... hey there ${chosenOne}`});
server.in(chosenOne).adapter.clients((err, clients) => {
console.log(`Clients in ${chosenOne}: ${clients}`)
})
});
}
It stands up the server, listens on port 3000, and then every 2 seconds sends a message to a random client in the "new" room. It also logs out the list of clients who are connected to the "new" room and the "" room.
Here's the client-side code:
var sock = require('socket.io-client')('http://localhost:3000');
sock.connect();
sock.on('update', (data) => {
console.log('Updating...');
console.log(data);
});
sock.on('new', (data) => {
console.log(`${sock.id} accepting request for new`);
console.log(data.message);
});
sock.on('welcome', (data) => {
console.log('A new challenger enters the ring');
console.log(data);
});
sock.on('private', (data) => {
console.log(`A private message for me, ${sock.id}???`);
console.log(data);
});
My problem is that all my clients are connected to each others' "" room. Here's a sample from my logs:
0|socket-s | Clients on channel "new": [ 'dJaoZd6amTfdQy5NAAAA', 'bwG1yTT46dr5R_G6AAAB' ]
0|socket-s | Whispering to bwG1yTT46dr5R_G6AAAB
2|socket-c | A private message for me, dJaoZd6amTfdQy5NAAAA???
2|socket-c | Psssst... hey there bwG1yTT46dr5R_G6AAAB
1|socket-c | A private message for me, bwG1yTT46dr5R_G6AAAB???
1|socket-c | Psssst... hey there bwG1yTT46dr5R_G6AAAB
0|socket-s | Clients in bwG1yTT46dr5R_G6AAAB: dJaoZd6amTfdQy5NAAAA,bwG1yTT46dr5R_G6AAAB
You can see that the "private" message is received by both clients, dJaoZ... and bwG1y..., and that both clients are connected to the default room for bwG1y....
Why is this? Does it have something to do with the fact that both of my clients are running on the same machine (with different Node processes)? Am I missing something in Socket.io's documentation? Any help is appreciated!
PS -- to add even more confusion, the private messaging that occurs in server.on('connect', ... works! Each clients receives the "Just between you and me ..." message exactly once, right after they connect to the server.
Your main problem could caused by server.to in whisperAtRandom function, use socket instead of server for the private message:
socket.to(chosenOne).emit('private', { message: `Psssst... hey there ${chosenOne}`});
To answer your other problems, try to change these:
server.emit('welcome', `Please give a warm welcome to ${socket.id}!`);
server.to(socket.id).emit('private', 'Just between you and me, I think you are going to like it here');
To:
socket.broadcast.emit('welcome', `Please give a warm welcome to ${socket.id}!`);
socket.emit('private', 'Just between you and me, I think you are going to like it here');
Check out my Socket.IO Cheatsheet:
// Socket.IO Cheatsheet
// Add socket to room
socket.join('some room');
// Remove socket from room
socket.leave('some room');
// Send to current client
socket.emit('message', 'this is a test');
// Send to all clients include sender
io.sockets.emit('message', 'this is a test');
// Send to all clients except sender
socket.broadcast.emit('message', 'this is a test');
// Send to all clients in 'game' room(channel) except sender
socket.broadcast.to('game').emit('message', 'this is a test');
// Send to all clients in 'game' room(channel) include sender
io.sockets.in('game').emit('message', 'this is a test');
// sending to individual socket id
socket.to(socketId).emit('hey', 'I just met you');
So I have chat app in which only two users can be in a room at a time.
Please note that clientInfo is a global variable and anytime a new user joins a room, an object variable is passed to the server with username and room name. Then this code is executed
socket.on('joinRoom', function(userDetails){
clientInfo[socket.id] = userDetails; //variable contains name and room joined.
socket.join(userDetails.room);
}
I added a feature where a user can type 'kick' as the chat message which will call this function:
function kickUser(socket){
socket.leave(clientInfo[socket.id].room);
delete clientInfo[socket.id];
}
the above code works fine but how can i change the function to kick the other user in the room? I tried the code below but I still ended up kicking myself out. I take it that socket.leave() doesn't care about the socket id but it just uses the room name and the person who called the function.
function kickUser(socket){
var info = clientInfo[socket.id];
if (typeof info == 'undefined'){
return;
}
Object.keys(clientInfo).forEach(function(socketId){
if(info.room == clientInfo[socketId].room) {
if (info.name != clientInfo[socketId].name){
socket.leave(clientInfo[socketId].room);
delete clientInfo[socketId];
}
}
});
}
Try implement next idea
In socket.on('connection', function(socket){ ... }); you must identify user name and room for this connection/socket: client can send username/room on connection itself
socket.on('connect', function {
socket.send(JSON.stringify{do: "introduce", name: "User1", room: "dasRoom"})
})
or server can read it in client cookie. Also you can check here user count in room. This info can be stored in global vars.
User can do kick by send specified message by socket, e.g. {do: "kick
, user: "User1"}, or request url. e.g. /kick/User1.
Function Kick must implement logic to find user by name, send goodbay message to victim and disconnect socket. Also server must send User1 leave room to alive user by socket.
In docs it is said: "Each Socket in Socket.IO is identified by a random, unguessable, unique identifier Socket#id. For your convenience, each socket automatically joins a room identified by this id."
I am wondering if there is an option to disable such feature.
My solution was:
io.on('connection', function (socket) {
leaveDefRoom(socket);
[...]
}
function leaveDefRoom(socket){
var room = socket.adapter.rooms;
for (var key in room){
if (key.charAt(0) == '/') {
socket.leave(key);
return;
}
}
}
In socket.io. Every time you emit event. socket.io send the event to the client in this room. If you remove user from the room, you cannot send this user messages. Even broadcast will not work.
Anyway, if you really want, you can leave this room, like any other room:
You can change socket.js file and disable this option:
https://github.com/socketio/socket.io/blob/master/lib/socket.js#L289
Socket.prototype.onconnect = function(){
debug('socket connected - writing packet');
this.nsp.connected[this.id] = this;
// You have to remove this line below:
this.join(this.id);
this.packet({ type: parser.CONNECT });
};
Following on the question asked by me:
socket io changes socket.id repeatedly
Registering only happens when the client comes on the chatpage:
On the client:
socketConnect: function() {
socket = io.connect('https://socket.mydomain.nl');
socket.on('connect', function() {
socket.emit('register', userID, 'Lobby');
});
};
Server
var UserSockIdStorage = {},
Allsocketids = [];
//Clients connect and register here.
io.sockets.on('connection', function(socket) {
socket.on("register", function(userID, currentchannel, userinfostr) {
console.log('USER WITH ID ' + userID + ' REGISTERS WITH THE SOCKETIDOF: ' + socket.id);
UserSockIdStorage[userID] = socket.id; //userid is associated with the socket ID.
socket.room = currentchannel;
socket.join(currentchannel);
});
As you can see i store the username in an array with the socket ID as its value.
When a message is to be send the username is looked up to retrieve the socket ID to send the message to.
Now come the part that i don't understand:
Clients keep reconnecting (Not registering so the socket id is not saved in the array!!) from time to time and i can see that they get another socket ID.
My guess would then be, that when i look up by username (Array UserSockIdStorage) a socket ID to send a message to, and the owner of that ID made several reconnects thus acquiring a new socket ID over and over again,
that a message send to the lookedUp socket ID wouldn't go anywhere since the client obtained several new socket ID's in the meanwhile.
But from what i'm observing the message is routed to the correct client so the first socket ID is still it's socket ID?!
Are those ID's cached or somewhat?
I'm using Node.js with ws(a socket library) and I'm having a little trouble with finding the socket of a specific user.
when the user connects to my socket server, I'm supposed to keep his socket objct in a big massive object by his userID or socketID like this:
var bigMassiveObject = {};
ws.on('connection', function(socket){
var userID //somehow get the userID
bigMassiveObject[userID] = socket;
// to find the socket
socket = bigMassiveObject[userID];
socket.send("hi");
})
so that later I can send him message by that ID, but isn't it better to just keep his socketID? this way we are not storing all the socket, but only the ID and then somehow(which I don;t know) send him message by that ID
this is what i'm looking for:
var smallObject = {};
ws.on('connection', function(socket){
var userID //somehow get the userID
smallObject[userID] = socketID;
// to find the socket
var socket = generateSocketObjectBySocketID(socketID);
socket.send("tada!");
})
so the question is, is there anyway to generate the socket, by the socketID(or useID)? so that we wouldn't be storing all these big socket objects in memory and there wouldn't be any memory leakage too :)
Simply,
This is what you need :
io.to(socket.id).emit("event", data);
whenever a user joined to the server,socket details will be generated including ID.This is the ID really helps to send a message to particular people.
first we need to store all the socket.ids in array,
var people={};
people[name] = socket.id;
here name is the reciever name. Example:
people["trojan"]=2387423cjhgfwerwer23;
So, now we can get that socket.id with the reciever name whenever we are sending message:
for this we need to know the recievername.You need to emit reciever name to the server.
final thing is:
socket.on('chat message', function(data){
io.to(people[data.reciever]).emit('chat message', data.msg);
});
Hope this works well for you.!!Good Luck