Sending a message to the specific user with socket.io is very important feature If you are building private Chat application. When I published Private Chat application, I mentioned that We are not sending a message to the unique socket. Instead, we were broadcasting the messages and showing to the specific user that by AngularJs.
But this article is truly based on Sending message to the specific user with socket.io and will explain how we can do that in socket.io.
Also, read Typing notification in chatting application using nodejs and socket.io
1. Sending message to the specific user with the socket.io workflow
Here we are keeping track of all the users in the array of object. When users connect to NodeJs server, then Nodejs server sends an array of object to the client as a list of online users. This object comprises of unique socket.id as well as username which is provided by the client when a client connects to the server.
for example, [ {id:'some randon id',name:'Shashank'},{...},{...}....{n}];
The below Image shows how we are storing information of users connected to the server.
Here n
number of users connected to the server, we are storing their respective socket.id, name and throwing back the array of object to all clients connected to the server.
To send a message to the particular client, we are must provide socket.id of that client to the server and at the server side socket.io takes care of delivering that message by using,
socket.broadcast.to('ID').emit( 'send msg', {somedata : somedata_server} );
For example,user3 want to send a message to user1. Assuming user3 is contacted from Chrome browser and user1 is connected to firefox browser as shown below image.
Here, managing Array of users plays very important role in terms of showing online users in the chat room.For example, when one user goes offline other users should get updated list of online users.The figure below explains the same have a look,
Also, readShowing online users using Nodejs and Socket.io
2. Folder structure and package.json:
Before creating a project let’s take a look at our project folder structure.In this application, we have three main folders named as utils, views, and public_data in our application.
Folder structure:
+--- utils | +-- config.js | +---routes.js | +--- node_modules | \ body-parser | \ express | \ socket.io | +--- public_data | \ css | | | +-- bootstrap.min.css | +-- style.css | \ js | | | +-- angular.min.js | +-- script.js +--- views | +-- index.html | +---server.js +---package.json
Here I have 1.7.1 socket.io version installed by the time of writing this article.
Find yours, cd/dir_name/npm list socket.io
To create new nodejs, the best practice is to start with npm init
. This command will create a package.json file. Here is my package.json file,
{ "name": "sending-message-to-specific-user-with-socket-io", "version": "0.0.1", "description": "Sending message to specific user with socket.io", "author": "Shashank", "license": "MIT", "dependencies": { "express": "4.14.0", "body-parser": "1.15.2", "socket.io": "1.7.1", "ejs":"2.5.3" } }
3. Creating Nodejs project
So Let’s start off by creating config.js in utils
folder. This file takes care of express related configuration.
config.js :
class Config{ constructor(app){ // Setting .html as the default template extension app.set('view engine', 'html'); // Initializing the ejs template engine app.engine('html', require('ejs').renderFile); // Telling express where it can find the templates app.set('views', (__dirname + '/../pages')); //Files app.use(require('express').static(require('path').join('public_data'))); } } module.exports = Config;
Now let’s create a server.js file. In this file, we will add the configuration of our application.
Server.js :
'use strict'; const express = require("express"); const http = require('http'); const socketio = require('socket.io'); const bodyParser = require('body-parser'); const routes = require('./utils/routes'); const config = require('./utils/config'); class Server{ constructor(){ this.port = process.env.PORT || 81; this.host = `localhost`; this.app = express(); this.http = http.Server(this.app); this.socket = socketio(this.http); } appConfig(){ this.app.use( bodyParser.json() ); new config(this.app); } /* Including app Routes starts*/ includeRoutes(){ new routes(this.app,this.socket).routesConfig(); } /* Including app Routes ends*/ appExecute(){ this.appConfig(); this.includeRoutes(); this.http.listen(this.port, this.host, () => { console.log(`Listening on http://${this.host}:${this.port}`); }); } } const app = new Server(); app.appExecute();
Now the last piece of the puzzle on the server side is remaining and i.e. routes and socket event for our application. For that, we have to create one more file called as routes.js inside the utils folder.
Here,
In the appRoutes()
function, we can add express routes but for now, we are just rendering the index.html file.
In the socketEevents()
function, we have added the socket events.
With the help of these events,
- we will add the users
- send messages to them and
- delete the user from the array.
routes.js:
'use strict'; class Routes{ constructor(app,socket){ this.app = app; this.io = socket; /* Array to store the list of users along with there respective socket id. */ this.users = []; } appRoutes(){ this.app.get('/', (request,response) => { response.render('index'); }); } socketEvents(){ this.io.on('connection', (socket) => { socket.on('username', (userName) => { this.users.push({ id : socket.id, userName : userName }); let len = this.users.length; len--; this.io.emit('userList',this.users,this.users[len].id); }); socket.on('getMsg', (data) => { socket.broadcast.to(data.toid).emit('sendMsg',{ msg:data.msg, name:data.name }); }); socket.on('disconnect',()=>{ for(let i=0; i < this.users.length; i++){ if(this.users[i].id === socket.id){ this.users.splice(i,1); } } this.io.emit('exit',this.users); }); }); } routesConfig(){ this.appRoutes(); this.socketEvents(); } } module.exports = Routes;
4 FrontEnd implementation
To communicate with the server, we need to add Socket.io in the client side code. we can do that by importing the socket.io script as shown below,
<script src="/socket.io/socket.io.js"></script>
After importing this script, we can call socket by writing below code,const socket = io.connect();
Now we can communicate with the server on emitting events on top of socket constant. Let’s take a look at index.html
file.
index.html
<html ng-app="app" ng-controller="app"> <head> <title>Sending message to specific client</title> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 user-list"> <h2>List of Users</h2> <ul class="list-group"> <li class="list-group-item" ng-repeat="user in userList" ng-class="{ 'active' : user.id == selectedUser}" ng-click = seletedUser(user.id); ng-style="{ 'cursor': user.id === socketId ? 'not-allowed' :'pointer' }" > <span id='{{user.id}}' >{{ user.id === socketId ? 'You': user.userName }}</span> </li> </ul> </div> <div class="col-md-8 message-box"> <h2>Messages sent by users</h2> <div class="message-container"> <ul class="list-group"> <li class="list-group-item" ng-repeat="message in messages" > {{ message.name }} says: {{ message.msg }} </li> </ul> <div class="alert alert-warning" ng-show='messages.length == 0'> No messages for you. </div> </div> <div class="message-sender"> <textarea class="form-control" ng-model='message' ng-keypress="sendMsg($event)"></textarea> </div> </div> </div> </div> </body> <script src="/socket.io/socket.io.js"></script> <script src="js/angular.min.js"></script> <script src="js/script.js"></script> </html>
Now let’s write some Angular Js code make our application actually work. Create a script.js file inside the /public_data folder and write the below code.
script.js:
'use strict'; const app = angular.module('app',[]); /* Making factory method for socket */app.factory('socket', function ($rootScope) { const socket = io.connect(); return { on: function (eventName, callback) { socket.on(eventName, function () { var args = arguments; $rootScope.$apply(function () { callback.apply(socket, args); }); }); }, emit: function (eventName, data, callback) { socket.emit(eventName, data, function () { var args = arguments; $rootScope.$apply(function () { if (callback) { callback.apply(socket, args); } }); }) } }; }); app.controller('app', ($scope,socket) => { $scope.socketId = null; $scope.selectedUser = null; $scope.messages = []; $scope.msgData = null; $scope.userList = []; $scope.username = window.prompt('Enter Your Name'); if ($scope.username === '') { window.location.reload(); } $scope.seletedUser = (selectedUser) => { selectedUser === $scope.socketId ? alert("Can't message to yourself.") : $scope.selectedUser = selectedUser; }; $scope.sendMsg = ($event) => { const keyCode = $event.which || $event.keyCode; if (keyCode === 13 && $scope.message !== null) { socket.emit('getMsg',{ toid : $scope.selectedUser, msg : $scope.message, name : $scope.username }); $scope.message = null; } }; socket.emit('username',$scope.username); socket.on('userList', (userList,socketId) => { if($scope.socketId === null){ $scope.socketId = socketId; } $scope.userList = userList; }); socket.on('exit', (userList) => { $scope.userList = userList; }); socket.on('sendMsg', (data) => { $scope.messages.push(data); }); });
Now using this article you can Send a message to the specific user with socket.io and you can integrate this feature in the realtime private chat application.
Further reading and links:
Socket.io rooms and namespaces
Is there possible send message also to offline users?
No you can’t bcoz user is not connected to server so you you won’t get user’s socketid.
You can do this by storing user’s id(not socketid ) in db and store the messages too. when user comes online show those messages.
I have covered this topic in this article.
https://codershood.info/2015/12/10/real-time-chatting-app-using-nodejs-mysql-angularjs-and-socket-io-part-1/
i can’t see my messages
Download link is not working please provide me a link to download
Hi Rupesh,
Replied you on your email.
Please send me a download link
Nice article. Some typo in tutorial.
// Telling express where it can find the templates
app.set(‘views’, (__dirname + ‘/../views’));
should be
// Telling express where it can find the templates
app.set(‘views’, (__dirname + ‘/../pages’));
That’s Maneesh,
Corrected that.
i can’t see my messages
Okay so you can follow below steps:
1. Check if both the clients are connected to socket server.
2. Once connected, check if you are getting your data inside
and
method, by doing console.log();
If still does work for you please share your code here will try to resolve it here.
help me please
i have a problem with this method! if the app grow and then we have 100k clients using the app at the same time.
What about the efficiency ?
This does not entirely depend upon the Socket.io library, You might wanna consider all your option ranging from server to overall project architecture.
Let’s assume you have 100k connections, then yes socket.io might explode (I never tested this), so in this case, you can consider using websockets/ws it.
Hi – Great article…How would I go about building an offline messaging system like Airbnb. If I want to build a messaging system between guests and hosts can I use Socket.io for that? Or is that an overkill since it is more of a real time chat platform?
hi! The download link is not working. I followed the tutorial but my end product looks weird without the style.css :’)
strange!
Here is a Github Link.
Can you please tell me ,what is happening at your end why you can’t download? So I can fix the issue.
https://uploads.disquscdn.com/images/255196dc5e0618a9759bd9cd48e54b63981041c5e1ded9978d846434cde60d5d.png
Just says this when i try to open the file that i downloaded. Technically the link is working but i can’t open it. :/
Okay, Thanks.
Hi Shashank,
Nice article. I would like to ask. why did you suggest not to use an array in production environment?
“Note: In this application, I am using an array to store the users connected to the server, But In production please avoid this idea instead use your choice of database to store the user’s information.”
I notice everything a uses refresh or visit another page on the same site. socket id changes. This is result in sending too many requests to the DB. What do you advise?
Thanks
thanks a lot
HI! Its 2021!
Thanks for giving me great idea to create my project.
god bless you!
Thanks a lot for this.