I am developing a website which uses a private messaging system using php + socket.io.
From the beginning i passed the sender_id, recipient_id and text to socket.io using socket.emit but later realized that this could be easily tampered with and wanted to use my php sessions in some way to be sure that the sender_id is indeed the sender_id.
I have the following setup right now but i dont really understand how to pass the session from index.php to app.js and then connect to redis-server in app.js to get the PHPSESSID which holds the user_id.
Server 1 running nginx + php-fpm (index.php)
Server 2 running node.js with socket.io (app.js)
Server 3 running redis for session management
My code right now looks like the following but is obviously missing the redis part right now which i would really appriciate some help with.
Thanks!
index.php
<?php
session_start();
if ($_SESSION['user_id'] == false){
header("Location:login.php");die;
}
?>
<script>
var socket = io('https://app01.dev.domain.com:8895');
socket.on('connect', function(){
console.log("Connected to websockets");
});
socket.on('event', function(data){});
socket.on('disconnect', function(){});
$('.chat-message').keypress(function (e) {
if (e.which == 13) {
console.log("send message");
var friend_id = $(this).attr('id');
friend_id = friend_id.split("-");
friend_id = friend_id[3];
var obj = {
recipient_id: friend_id,
text: $(this).val()
};
socket.emit('chat_message', obj);
$(this).val('');
return false;
}
});
</script>
app.js
var https = require("https"), fs = require("fs");
var options = {
key: fs.readFileSync('/etc/letsencrypt/live/domain/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/domain/cert.pem'),
ca: fs.readFileSync('/etc/letsencrypt/live/domain/chain.pem')
};
var app = https.createServer(options);
var io = require("socket.io")(app);
var redis = require("redis");
// This i want to fill with for example PHPSESSION:user_id that i get from redis and later use it as sender
// var all_clients = {};
io.set("transports", ["websocket", "polling"]);
io.on("connection", function(client){
console.log("Client connected");
// Here i would like to connect to redis in some way and get the user_id but dont really understand how
//all_clients[USER_ID_FROM_REDIS] = client.id;
//var user_id = USER_ID_FROM_REDIS;
client.on("chat_message", function(data){
var obj = {
to: data.recipient_id,
text: data.text
};
console.log("Message inbound from socket: "+client.id+" from: "+data.user_id+" to: "+data.recipient_id+" with text: "+data.text);
});
client.on("disconnect", function(){
console.log("Client disconnected ");
//delete all_clients[USER_ID_FROM_REDIS];
});
});
app.listen(8895, function(){
console.log("listening on *:8895");
});
var recursive = function () {
//console.log("Connected clients: "+Object.keys(all_clients).length);
//console.log(JSON.stringify(all_clients));
setTimeout(recursive,2000);
}
recursive();
HTTP in itself does not protect against MITM attacks, to protect against MITM the server certificate needs to be pined.
To protect against a user being spoofed you need authentication such as logging-in or a secret token like Dropbox.
Add certificate pinning, that is just jargon for validating that you are connecting to the correct server and not a MITM by verifying the certificate that is sent by the server. MITM used to be harder but WiFi has made it easy to connect to the wrong end-point at Hot Sports, even at home I have seen this.
I am making a chat application for my web job portal.I used to save user's name from php session to nodejs socket io library..I am confused..it is ok to store 10k users in socket object.if yes then how to maintain that users list? Another problem is when user navigates from one page to another..socket disconnect and again connect..Does it effect my application performance or nodejs server perform?
Please guide me through tutorials or blog..I have not find relevant docs yet about socket connection management ..Thanks in advance!!
You can store your data into socket.
For example,
On Server side, use like this,
var socketIo = require('/socket.io').listen(8080);
var usernames=[];
socketIo.sockets.on('connection', function (socket) {
socket.on('storeUserData', function (data) {
var userInfo = new Object();
userInfo.userName = data.userName;
userInfo.SocketId = socket.id;
usernames.push(userInfo);
});
socket.on('disconnect', function (data) {
var len = usernames.length;
for(var i=0; i<len; i++){
var user = usernames[i];
if(user.socketId == socket.id){
usernames.splice(i,1);
break;
}
}
});
});
and on client side, you need to add this
<script>
var userName = <?php echo $_SESSION['userName'] ?>;
var socket = io.connect('http://localhost', {port: 8080});
socket.on('connect', function (data) {
socket.emit('storeUserData', { 'userName' : userName });
});
</script>
Socket connection disconnects on page refresh.Why?
It is default behaviour of socket.io.
My application stack:
On my server runs a Redis server. The PHP backend communicates with Predis library with the Redis server. It will publish messages. These messages will be fetched by my Redis client (node.js) and pushed to the connected websocket clients (with SockJS).
My problem:
It runs well. At least for broadcast messages. Now I came to the point I need to send a unicast message and I'm stuck... How to connect the user on the backend side (sender of messages) with the connected client of the websocket?
Code snippets:
PHP
$redis = new Client();
$redis->publish('updates', Random::getUniqueString());
Redis client on node.js server
redis.subscribe('updates');
redis.on('message', function(channel, data) {
for (var id in sockets) {
if (sockets.hasOwnProperty(id)) {
sockets[id].write(data);
}
}
});
SockJS client
mySocketFactory.setHandler('message', function(event) {
console.log(event.data);
});
Like I said. Working well but the id used for the socket connection is not known by the PHP backend.
Edit: One idea I got in mind is to use cookies.
I found a way to solve my problem. When the socket connection is established I sent a request to my PHP backend and ask for the user id. This is stored on the node.js server. When messages are incoming there is a check if they are for specific user and handle them only for them.
So, what do I store exactly on my node server?
var sockets = {}; // {connection_id: socket_connection}
var connIdToUser = {}; // {connection_id: user_id}
var connIdsForUser = {}; // {user_id: [connection_id_1, connection_id_2 ,...]}
socketServer.on('connection', function(conn) {
sockets[conn.id] = conn;
var options = {
host: os.hostname(),
port: 80,
path: '/user/id',
method: 'GET'
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
var userId = JSON.parse(chunk).id;
connIdToUser[conn.id] = userId;
if (!connIdsForUser.hasOwnProperty(userId)) {
connIdsForUser[userId] = [];
}
connIdsForUser[userId].push(conn.id);
console.log('connection id ' + conn.id + ' related to user id ' + userId);
});
});
req.end();
conn.on('close', function() {
console.log('connection lost ' + conn.id);
// remove connection id from stack for user
var connections = connIdsForUser[connIdToUser[conn.id]];
var index = connections.indexOf(conn.id);
if (index > -1) {
connections.splice(index, 1);
}
// remove connection at all
delete sockets[conn.id];
// remove relation between connection id and user
delete connIdToUser[conn.id];
});
});
The reason for storing the relation between user id an connection id twice is the different use case I need either for sending a message or deleting the connection for the close event. Otherwise I would have to use a nested loop.
As you can see deleting a socket is fairly easy. Although deleting the connection from the connection stack of an user is a little bit complicated.
Let's continue with the sending of a message. Here I defined a structure of the message I get from the Redis server:
{
targets: [], // array of unit ids (can be empty)
data: <mixed> // the real data
}
Sending the data to the sockets looks like:
redis.on('message', function(channel, message) {
message = JSON.parse(message);
// unicast/multicast
if (message.targets.length > 0) {
message.targets.forEach(function(userId) {
if (connIdsForUser[userId] !== undefined) {
connIdsForUser[userId].forEach(function(connId) {
sockets[connId].write(message.data);
});
}
});
// broadcast
} else {
for (var id in sockets) {
if (sockets.hasOwnProperty(id)) {
sockets[id].write(message.data);
}
}
}
});
Since I store the connection stack per user it is quite easy to send the data to all sockets related to a specific user. So what I can do now is unicast (array with one user id), multicast (array with more than one user id) and broadcast (empty array).
It's working well for my use case.
I have recently been looking around, to find a good way to communicate between nodeJS and PHP. Here is the idea : nodeJS is still quite new, and it can be kind of tricky to develop a full application only with it. Moreover, you may need it only for one module of your project, like realtime notifications, chat, ... And you want to manage all the other stuff with PHP, because it is probably more easy for you (and you can take advantage of the existing frameworks, like CodeIgniter or Symfony).
I would like to have an easy solution ; I don't want to use cURL, or a third server to communicate between Apache and Node servers. What I want is to be able to catch events from node in simple Javascript, client-side.
I didn't find any answers that where complete, most of the time client-side was running by the node server and so not applicable in my case. So I crawled all the possible topics, and finally find my answer ; I'll try to share this, and to have a point where it's all clear.
Hope this can help some people ! ;)
So, to begin with, I put my project on github, if you want access to the full code: https://github.com/jdutheil/nodePHP
It is a very simple example project: a web chat. You just have an author and message, and when you press send it is saved in a mysql database. The idea is to send real time updates, and have a real conversation. ;) We'll use nodeJS for that.
I won't talk about PHP code, it is really simple and not interesting here; what I want to show you is how to integrate your nodeJS code.
I use express and Socket.IO, so be sure to install those modules with npm. Then, we create a simple nodeJS server:
var socket = require( 'socket.io' );
var express = require( 'express' );
var http = require( 'http' );
var app = express();
var server = http.createServer( app );
var io = socket.listen( server );
io.sockets.on( 'connection', function( client ) {
console.log( "New client !" );
client.on( 'message', function( data ) {
console.log( 'Message received ' + data.name + ":" + data.message );
io.sockets.emit( 'message', { name: data.name, message: data.message } );
});
});
server.listen( 8080 );
We registered our events callback when a new user is connected ; every time we receive a message (represents a chat message), we broadcast it to every users connected. Now, the tricky part: client-side! That the part that took me most of the time, because I didn't know which script include to be able to run Socket.IO code without the nodeServer (because client page will be served by Apache).
But everything is already done; when you install Socket.IO module with npm, a script is available in /node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js; that the script we will include in our PHP page, in my case:
<script src="js/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js"></script>
<script src="js/nodeClient.js"></script>
And to finish, my nodeClient.js, where we simply connect to the node server and wait for event to update our page. ;)
var socket = io.connect( 'http://localhost:8080' );
$( "#messageForm" ).submit( function() {
var nameVal = $( "#nameInput" ).val();
var msg = $( "#messageInput" ).val();
socket.emit( 'message', { name: nameVal, message: msg } );
// Ajax call for saving datas
$.ajax({
url: "./ajax/insertNewMessage.php",
type: "POST",
data: { name: nameVal, message: msg },
success: function(data) {
}
});
return false;
});
socket.on( 'message', function( data ) {
var actualContent = $( "#messages" ).html();
var newMsgContent = '<li> <strong>' + data.name + '</strong> : ' + data.message + '</li>';
var content = newMsgContent + actualContent;
$( "#messages" ).html( content );
});
I'll try to update and improve my code as soon as possible, but I think it already open to all of cool things! I am really open for advice and reviews on this stuff, is it the good way to do it, .. ?
Hope this can help some people!
I have another solution that works quite well for me, but I would like someone to comment about how effective it is, as I have not (yet) had the opportunity/time to test it on the real server.
Here goes the node-js code. I put this code in a file called nodeserver.js:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var knall = new Object();
knall.totten = "4 tomtar";
knall.theArr = new Array();
knall.theArr.push("hoppla")
knall.theArr.push("hej")
var strKnall = JSON.stringify(knall);
res.end(strKnall);
}).listen(process.env.PORT);
And here is the simple piece of code in php, calling the node-js server with the help of file_get_contents():
$json = file_get_contents('http://localhost:3002/knall.json');
$obj = json_decode($json);
Works great, when I load the php-page, it in turn calls the nodeserver.js page, which jsonify the knall-object.
I have two localhost-installations running on iis on windows 10, one standard php-server, and the nodejs-server works with the neat iisnode package.
The 'real' server is run on ubuntu.
I think this is a neat and easy solution for communication between two servers, but maybe someone has any comments about it?
Try similar or you can check my blog for complete sample code on nodejs
On your page side:
Load Socket JS
https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js
Make object of the socket
var socket = io();
Use the emit function to send data to nodeserver.
socket.emit('new_notification', {
message: 'message',
title: 'title',
icon: 'icon',
});
So now your code will be look like
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
var socket = io();
$(document).ready(function($) {
$('.rules-table').on('click', '.runRule', function(event) {
event.preventDefault();
/* Act on the event */
var ruleID = $(this).parents('tr').attr('id');
// send notification before going to post
socket.emit('new_notification', {
message: 'Messge is ready to sent',
title: title,
icon: icon,
});
$.ajax({
url: '/ajax/run-rule.php',
type: 'POST',
dataType: 'json',
data: {
ruleID: ruleID
},
})
.done(function(data) {
console.log(data);
// send notification when post success
socket.emit('new_notification', {
message: 'Messge was sent',
title: title,
icon: icon,
});
})
.fail(function() {
console.log("error");
// send notification when post failed
socket.emit('new_notification', {
message: 'Messge was failed',
title: title,
icon: icon,
});
})
.always(function() {
console.log("complete");
});
});
});
Now on Node server side make handler for your request to get your request and send a message to all connected devices/browsers(server.js)
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res) {
res.sendfile('index.html');
});
io.on('connection', function (socket) {
socket.on( 'new_notification', function( data ) {
console.log(data.title,data.message);
// Now Emit this message to all connected devices
io.sockets.emit( 'show_notification', {
title: data.title,
message: data.message,
icon: data.icon,
});
});
});
http.listen(3000, function() {
console.log('listening on localhost:3000');
});
Now the client/browser/client side make a receiver to receive socket message from node server
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
var socket = io();
/**
* Set Default Socket For Show Notification
* #param {type} data
* #returns {undefined}
*/
socket.on('show_notification', function (data) {
showDesktopNotification(data.title, data.message, data.icon);
});
/**
* Set Notification Request
* #type type
*/
function setNotification() {
showDesktopNotification('Lokesh', 'Desktop Notification..!', '/index.jpeg');
sendNodeNotification('Lokesh', 'Browser Notification..!', '/index.jpeg');
}
/**
* Check Browser Notification Permission
* #type window.Notification|Window.Notification|window.webkitNotification|Window.webkitNotification|Window.mozNotification|window.mozNotification
*/
var Notification = window.Notification || window.mozNotification || window.webkitNotification;
Notification.requestPermission(function (permission) {
});
/**
* Request Browser Notification Permission
* #type Arguments
*/
function requestNotificationPermissions() {
if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
});
}
}
/**
* Show Desktop Notification If Notification Allow
* #param {type} title
* #param {type} message
* #param {type} icon
* #returns {undefined}
*/
function showDesktopNotification(message, body, icon, sound, timeout) {
if (!timeout) {
timeout = 4000;
}
requestNotificationPermissions();
var instance = new Notification(
message, {
body: body,
icon: icon,
sound: sound
}
);
instance.onclick = function () {
// Something to do
};
instance.onerror = function () {
// Something to do
};
instance.onshow = function () {
// Something to do
};
instance.onclose = function () {
// Something to do
};
if (sound)
{
instance.sound;
}
setTimeout(instance.close.bind(instance), timeout);
return false;
}
Im trying to build an notification system with php and socket.io. The idea is, the clients connect to socket.io and are waiting for notification. A PHP script connects to socket.io via curl on another port and posts the update even, which gets passed to the connected clients. The clients are identified via an id they send in a message after the connection event. I Store the socket variable associated to the user_id. Everything works fine, but after some time the script stops working. It seems that after some time the socket variable which is stored in an array. However my server-code is posted below
var notification_port = 8001;
var oak_port = 8002;
var io = require('socket.io').listen(notification_port);
var clients = new Array();
io.sockets.on("connection", function(socket){
socket.on("__identification", function(message){
if (message.id){
console.log("user with session id " + message.id + " connected!");
var sockets = clients[message.id];
if (!sockets){
sockets = new Array();
}
sockets.push(socket);
clients[message.id] = sockets;
}
});
});
var url = require('url');
var oakListener = require('http').createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var url_parts = url.parse(req.url, true);
var query = url_parts.query;
var sockets = clients[query.id];
if (sockets){
for (var i = 0; i < sockets.length; i++){
sockets[i].emit("notification", query);
}
res.end('ok');
} else {
res.end('failed');
}
}).listen(oak_port);
you have to add a handler for disconnect like explained below:
socket.on('disconnect', function () {
//delete socket from sockets;
});
The problem was, that the connection gets lost about every minute. You have to gargabe collect in the "disconnect" function and re initializing the connection in the "connection" function