33

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?

Trevor Newhook
  • 889
  • 1
  • 11
  • 29
  • I've created an array with all users, and then used io.sockets.socket(userID).emit, but it's still not sending. The updated server file is [here](http://pastebin.com/k770CEaM), and the client file is [here](http://pastebin.com/V6Cdg113) If you could look at it, I'd appreciate it! – Trevor Newhook Jul 10 '12 at 08:38

8 Answers8

38

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');

Community
  • 1
  • 1
Brad
  • 159,648
  • 54
  • 349
  • 530
  • 17
    that's to send to the user with the 'news' id? what are the functions of "news", "hello", "world"?? There's no MEANING in that snippet. How do I get the message and the target user from the client?... "socket.emit('news', { hello: 'world' });" isn't exactly straightforward.. – Trevor Newhook Jul 06 '12 at 04:55
  • 1
    No, that will raise the `news` event for the client connected to `socket`. The first parameter for the function handling that event will be the object, `{hello: 'world'}`. You need to keep a reference to that socket object upon connect for later use. – Brad Jul 06 '12 at 04:57
  • Also note that there *is* a way to get that socket later on, but it's a bit messy. See: https://github.com/LearnBoost/socket.io/issues/503 See also: http://stackoverflow.com/questions/6913801/sending-message-to-specific-client-with-socket-io-and-empty-message-queue – Brad Jul 06 '12 at 04:59
  • Not sure if you meant to post twice, or just didn't read anything I posted. If you are still confused, can you clarify what your confusion is? Are you not sure how to use callback functions with Socket.IO? – Brad Jul 06 '12 at 05:02
  • no..I posted twice because I didn't know if StackOVerflow would send a notification for an edited post (as compared to a new post). Let me see if I understand this correctly. Please tell me if I understand the logic.. I'll respond once I've thought it out – Trevor Newhook Jul 06 '12 at 05:07
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13496/discussion-between-brad-and-trevor-newhook) – Brad Jul 06 '12 at 05:08
  • 1
    I understand the socket.emit('news', { hello: 'world' });, but I still can't wrap my head around HOW to send a particular message to a particular user. = news is event, hello:world is data, but where's the target user? Right now, I'm trying io.sockets.socket(userID).emit('news', { hello: 'world' });, but nothing seems to get sent. – Trevor Newhook Jul 10 '12 at 03:40
  • 1
    I don't know how else to explain this to you.... I've said this at least 3 times now... you need to hang on to the socket when the user connects. How is Socket.IO supposed to magically know that you intend to associate sockets with users? **It doesn't!** You have to write that code. The socket is what is associated with a particular user. You need to keep an object, array, or something around that allows you to associate your users with sockets. – Brad Jul 10 '12 at 04:23
  • some kind of an associative array that says this socket goes with this user. I think I'm getting it...slowly. Thanks for your patience. – Trevor Newhook Jul 10 '12 at 04:30
  • 1
    Yes, that's exactly what I am saying, except that associative arrays don't exist in JavaScript... only objects. They look the similar, and can often be used for the same purpose, but have a few key differences. – Brad Jul 10 '12 at 04:32
  • so...I create an object that has attributes of the socket.id and some kind of identifier. From what I can understand, I should use something like io.socket.sockets(object.id).emit{object, variable, whatever else) Is that correct, or am I still confused? – Trevor Newhook Jul 10 '12 at 05:00
  • Brad...thanks for your patience - I think my problem was that I thought the socket IDs were the IDs that I selected in my case, usernames. I didn't realize that socket.io had its own IDs that I had to use. Thanks for your help. – Trevor Newhook Jul 10 '12 at 09:42
  • 1
    @TrevorNewhook, You shouldn't use Socket.IO's IDs to identify sockets... you should keep a reference around of the actual socket itself. Otherwise, you are creating new socket objects each time you use them, and you are relying on Socket.IO functionality that may change at some point in the future. – Brad Jul 10 '12 at 14:22
  • how would I do that?? ... a reference of the actual socket? A socket is a data connection with identification information, kind of like an address identifies a physical location, right? I thought the socket ID had the same function... What's the difference? – Trevor Newhook Jul 10 '12 at 23:56
  • When a user is connected, you get the new socket object as the first parameter to your connection event handler. Save a copy of it for use later, in an object containing users or something. At this point, if you still don't understand, I'd strongly suggest you go back to the basic example on the Socket.IO home page and work with it until you fully understand the basics, prior to building a full application around this concept. – Brad Jul 10 '12 at 23:59
  • thanks a ton. I know the Socket.IO home page has examples, they're not explained. From what I can understand, on socket connection, I would use 'io.sockets.on('connection', function (socket) { socket.set('nickname', nick, function () { socket.emit('ready'); }); });' To get the socket, I would use: ' socket.on('private message', function(data) { socket.get('nickname', function (err, name) { }); updateLog('private message', socket.nickname, data.message); message=data.message; io.sockets.socket(nickname).emit('private message', tstamp(), socket.nickname, message); });' – Trevor Newhook Jul 11 '12 at 00:26
  • 11
    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');` – Brad Jul 11 '12 at 14:01
  • IT took me a while, but I now understand what that line of code means :p - thanks for the help – Trevor Newhook Aug 10 '12 at 04:42
  • @TrevorNewhook, Glad to hear it! No problem. – Brad Aug 10 '12 at 13:30
  • Trevor wants to send a message to a specific user. Your original solution would send it to an entire room. It doesn't solves his problem. – Pedro Alves Jul 20 '14 at 16:03
  • @PedroAlves That isn't true. You simply emit on the socket instance for the socket you want to send to, for that individual person. – Brad Jul 20 '14 at 16:08
  • Yes, sorry. It doesn't emit to a room. My mistake. But the problem's core is how to recover the destination's socket instance. You can do that using connectedUsers dict. – Pedro Alves Jul 20 '14 at 16:13
  • @PedroAlves You should keep a reference to that when they connect. – Brad Jul 20 '14 at 16:26
  • @Brad You should edit your answer and combine it with your comment (the one with var connectedUsers = {};). The best way is to keep the socket associate it with whatever you want to use it later (username, userid, etc.). Your comment is the answer, your answer is the right way to send one message to the current socket but not someone who connected 10 minutes ago. tnx for the solution. – Maziyar Sep 18 '14 at 20:23
  • @Brad So, we will store all the user data like nickname, channel in the socket object correct? – Sobiaholic Nov 25 '14 at 22:30
  • @Sobiaholic No, absolutely not. Store that data elsewhere. Otherwise, you might conflict with something internal to Socket.IO. Think of a structure like this: `var users = [ {nickname:'Sobiaholic', socket: socketiosocketgoeshere} ]` Also remember that a user can connect multiple times at once (not only with multiple browsers/tabs, but due to reloading a page and the old connection not being removed immediately). You may want to keep an array of sockets for a user. – Brad Nov 25 '14 at 22:32
  • Thanks @Brad I tried to detect if a user joined with multiple nicknames, but the socket (in first tab) is different in the socket (in the second tab) even the socket ids are different. I wrote an if statement like the following `if (userSessions[auser].socket === currentSocket)` but always return false. Note: I'm looping through each user so auser is in `for(var auser in userSessions)` – Sobiaholic Dec 02 '14 at 21:22
  • @Sobiaholic Yes, each socket has its own ID. – Brad Dec 02 '14 at 21:23
  • @Brad how to detect multiple sockets from same user then? shall I open a new question or asking here is fine? – Sobiaholic Dec 02 '14 at 21:24
  • @Sobiaholic How you identify them is specific to your application. Probably they connect with the same username, yes? Then use that. – Brad Dec 02 '14 at 21:25
  • @Brad What if not the same user name? – Sobiaholic Dec 02 '14 at 21:26
  • @Sobiaholic How should I know? I don't know what your application does, nor how you identify your users. You should post a new question. – Brad Dec 02 '14 at 21:27
  • @Brad, True that :). Sorry. Your comments above helped much! Thanks again! – Sobiaholic Dec 02 '14 at 21:28
  • Helped a lot!.. Thanks! :-) – David R Dec 14 '19 at 15:02
  • @Brad Thank a lot. However, what if we want to keep a history of the discussion if people want to reread their exchange a week later for example, in my simplest idea would be to keep a copy in the database, listening to the event of disconnecting the socket? maybe there is a smarter solution? – Hugo-dev Feb 04 '21 at 06:22
  • 1
    @user3074603 Yes, how you do that is completely unrelated to your Socket.IO usage. You'll want to store that content in a database, as you suggest. – Brad Feb 04 '21 at 06:47
  • Yes indeed it is decorrelated but I was especially asking that at the level of the event socket.IO listener which seems the most suitable for capturing the history of the conversation at the right time. Maybe try to detect when the two people are disconnected, or before the socket destruction if this event is catchable. – Hugo-dev Feb 04 '21 at 06:56
  • 1
    @user3074603 You'll want to log data in your database constantly, as the data flows through. Otherwise, how will you handle the situation of your Node.js instances crashing and losing data before write? Additionally, there may be multiple devices in use by a single user. You may want multiple server instances at some point as well. – Brad Feb 04 '21 at 07:02
24

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 });
});
Pingolin
  • 3,161
  • 6
  • 25
  • 40
almypal
  • 6,612
  • 4
  • 25
  • 25
  • In your server side code, what is client? Why not socket.on? The chat_server.js code is [here](http://pastebin.com/nxHdzHDg), and the client code is [example_chat.tpl.php](http://pastebin.com/t0h7rSFv) – Trevor Newhook Jul 10 '12 at 04:55
  • 1
    ok...I figured out that client is just the name that you used for io.sockets.on('connection'). – Trevor Newhook Jul 10 '12 at 05:21
  • to: selected.text() gets a username of the recipient? so, his socket.id needs to be stored in the object, but I don't see that in your snippet? or I'm missing something? – Sobiaholic Nov 25 '14 at 21:54
  • This line is confusing : io.sockets.sockets[data.to].emit("private", { from: client.id, to: data.to, msg: data.msg }); getting error: TypeError: Cannot read property 'emit' of undefined – Sheeraz Jul 03 '19 at 06:59
  • This is only the simulation of private message on the same public window,real private chat is when we extract conversation to new tab for example,and,for that task i failed to find working solution in few months. – Goran_Ilic_Ilke Sep 10 '20 at 09:37
13

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....

Peter Moses
  • 1,997
  • 1
  • 19
  • 22
7

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+"!");
Matthew Clark
  • 571
  • 1
  • 9
  • 33
7

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.

Ali_Hr
  • 4,017
  • 3
  • 27
  • 34
1

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
   });
user3506100
  • 121
  • 1
  • 2
0

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

J. Jerez
  • 704
  • 8
  • 6
0

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 )
}); `
});
Stan
  • 11
  • 3