Comment intégrer nodeJS + Socket.IO et PHP?

98

J'ai récemment cherché un bon moyen de communiquer entre nodeJS et PHP. Voici l'idée: nodeJS est encore assez récent, et il peut être assez difficile de développer une application complète uniquement avec elle. De plus, vous n'en aurez peut-être besoin que pour un module de votre projet, comme les notifications en temps réel, le chat, ... Et vous voulez gérer tous les autres trucs avec PHP, car c'est probablement plus facile pour vous (et vous pouvez en profiter les frameworks existants, comme CodeIgniter ou Symfony).

J'aimerais avoir une solution simple; Je ne veux pas utiliser cURL ou un troisième serveur pour communiquer entre les serveurs Apache et Node. Ce que je veux, c'est pouvoir capturer les événements du nœud en JavaScript simple, côté client.

Je n'ai trouvé aucune réponse qui était complète, la plupart du temps, le côté client était exécuté par le serveur de nœud et donc non applicable dans mon cas. J'ai donc exploré tous les sujets possibles, et enfin trouvé ma réponse; J'essaierai de partager ceci, et d'avoir un point où tout est clair.

J'espère que cela peut aider certaines personnes! ;)

Jérémy Dutheil
la source

Réponses:

131

Donc, pour commencer, je mets mon projet sur github, si vous voulez accéder au code complet: https://github.com/jdutheil/nodePHP

C'est un exemple de projet très simple: un chat Web. Vous avez juste un auteur et un message, et lorsque vous appuyez sur envoyer, il est enregistré dans une base de données mysql. L'idée est d'envoyer des mises à jour en temps réel et d'avoir une vraie conversation. ;) Nous utiliserons nodeJS pour cela.

Je ne parlerai pas de code PHP, c'est vraiment simple et pas intéressant ici; ce que je veux vous montrer, c'est comment intégrer votre code nodeJS.

J'utilise express et Socket.IO, alors assurez-vous d'installer ces modules avec npm. Ensuite, nous créons un serveur nodeJS simple:

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

Nous avons enregistré notre rappel d'événements lorsqu'un nouvel utilisateur est connecté; chaque fois que nous recevons un message (représente un message de chat), nous le diffusons à tous les utilisateurs connectés. Maintenant, la partie délicate: côté client! C'est la partie qui m'a pris la plupart du temps, car je ne savais pas quel script incluait pour pouvoir exécuter du code Socket.IO sans le nodeServer (car la page client sera servie par Apache).

Mais tout est déjà fait; lorsque vous installez le module Socket.IO avec npm, un script est disponible dans /node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js; que le script que nous allons inclure dans notre page PHP, dans mon cas:

    <script src="js/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js"></script>
    <script src="js/nodeClient.js"></script>

Et pour finir, mon nodeClient.js, où nous nous connectons simplement au serveur de nœuds et attendons l'événement pour mettre à jour notre 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 );
});

Je vais essayer de mettre à jour et d'améliorer mon code dès que possible, mais je pense qu'il est déjà ouvert à toutes les choses sympas! Je suis vraiment ouvert aux avis et critiques sur ce sujet, est-ce la bonne façon de le faire, ..?

J'espère que cela peut aider certaines personnes!

Jérémy Dutheil
la source
18
Eh bien, lorsque vous écrivez une question, il y a une option "répondre à votre propre question, partager le style de questions-réponses", alors j'ai pensé que nous pouvons partager comme ça, désolé si je me trompe :)
Jérémy Dutheil
4
À titre de suggestion, je pense qu'incorporer la réponse à cette question ici stackoverflow.com/questions/5818312/mysql-with-node-js est une méthode supérieure. en évitant tout appel ajax et en rendant le code plus en ligne avec l'utilisation de node. Maintenant, PHP peut simplement sélectionner les informations de la base de données.
blackmambo
1
Est-il possible de se connecter à l'application de nœud en utilisant io.connect si elle se trouve sur une machine différente de votre application principale plutôt que d'avoir l'application de nœud sur le même serveur mais en utilisant un port différent?
maembe
1
nécessitent la signature hmac comme authentification de message. cela garantit que seul php peut diffuser des messages sur le socket. le socket inspectera le jeton signé, et s'il réussit, ti diffusera alors le message. c'est bon pour empêcher le spam et garantir l'intégrité des données. donc ne postez jamais directement sur la socket du nœud depuis le client. à la place, postez sur l'application php avec ajax, puis transmettez-le au serveur socket. il est assez non trivial d'ouvrir une connexion socket à un serveur websocket avec fopen + fwrite ou stream select à partir de php.
r3wt
1
D'accord avec @Bangash, vous pouvez utiliser Node.js pour stocker les données dans la base de données mysql au lieu de PHP, ce qui le rendrait beaucoup plus rapide
Parthapratim Neog
2

J'ai une autre solution qui fonctionne assez bien pour moi, mais j'aimerais que quelqu'un commente son efficacité, car je n'ai pas (encore) eu l'occasion / le temps de la tester sur le vrai serveur.

Voici le code node-js. J'ai mis ce code dans un fichier appelé 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);  

Et voici le simple morceau de code en php, appelant le serveur node-js à l'aide de file_get_contents ():

$json = file_get_contents('http://localhost:3002/knall.json');
$obj = json_decode($json);

Fonctionne très bien, lorsque je charge la page php, elle appelle à son tour la page nodeserver.js, qui jsonify le knall-object.

J'ai deux installations localhost fonctionnant sur iis sur Windows 10, un serveur php standard et le serveur nodejs fonctionne avec le package iisnode soigné .

Le «vrai» serveur est exécuté sur ubuntu.

Je pense que c'est une solution simple et soignée pour la communication entre deux serveurs, mais peut-être que quelqu'un a des commentaires à ce sujet?

Snorvarg
la source
Cela n'a aucun sens pour moi, car vous lancez le serveur de nœuds à partir du script php. Je ne peux imaginer aucun cas d'utilisation pour cela. Ce dont nous avons besoin, c'est d'un moyen de communiquer entre une instance node.js en cours d'exécution et php.
Lorenz Meyer
Non @Lorenz, c'est le script node.js, fonctionnant sur son propre serveur. J'appelle la page node.js directement depuis php avec file_get_contents (), depuis un autre serveur php. Il est maintenant utilisé quotidiennement avec plus de 500 utilisateurs par jour. Peut-être que vous êtes confus parce que le morceau "localhost: 3002"? C'est parce que cet exemple fonctionne sur mon ordinateur Windows local, avec deux serveurs autonomes dans iis.
Snorvarg
Je suis vraiment confus. Cela signifie que nodejs.jsest en fait pas un fichier source, mais il est une URL que vous avez appelé ainsi, car il contient JSON? Le premier n'aurait aucun sens, mais le second me semble très déroutant.
Lorenz Meyer
@Lorenz, j'ai essayé de clarifier l'exemple en changeant le nom de fichier du fichier nodejs js et en éditant un peu le texte. Pour répondre à votre question, le fichier désormais renommé nodeserver.js est exécuté sur son propre serveur. L'appel http.createServer () crée un serveur, qui écoute () s les connexions entrantes au port 80.
Snorvarg
Notez que vous pouvez appeler le serveur node.js directement depuis un navigateur, en entrant simplement l'url " localhost: 3002 / nodeserver.js ", et vous obtiendrez une réponse json. Le file_get_contents () dans le fichier php récupère le contenu d'un autre serveur, dans ce cas le serveur node.js.
Snorvarg
0

Essayez similaire ou vous pouvez consulter mon blog pour un exemple de code complet sur nodejs


Du côté de votre page:

  • Charger le socket JS

https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js

  • Faire objet de la prise

var socket = io ();

  • Utilisez la emitfonction pour envoyer des données au serveur de nœuds.

socket.emit ('nouvelle_notification', {
message: 'message',
titre: 'titre',
icône: 'icône',
});

Alors maintenant, votre code ressemblera à

<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");
    });

  });
});

Maintenant, du côté du serveur Node, créez un gestionnaire pour votre demande pour obtenir votre demande et envoyer un message à tous les appareils / navigateurs connectés (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');
});

Maintenant, le côté client / navigateur / client crée un récepteur pour recevoir le message de socket du serveur de nœud

<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;
}
vikujangid
la source