¿Cómo integrar nodeJS + Socket.IO y PHP?

98

Recientemente he estado buscando para encontrar una buena manera de comunicarme entre nodeJS y PHP. Aquí está la idea: nodeJS es todavía bastante nuevo y puede ser complicado desarrollar una aplicación completa solo con él. Además, es posible que solo lo necesite para un módulo de su proyecto, como notificaciones en tiempo real, chat, ... Y desea administrar todas las demás cosas con PHP, porque probablemente sea más fácil para usted (y puede aprovechar los marcos existentes, como CodeIgniter o Symfony).

Me gustaría tener una solución fácil; No quiero usar cURL o un tercer servidor para comunicarme entre los servidores Apache y Node. Lo que quiero es poder capturar eventos del nodo en Javascript simple, del lado del cliente.

No encontré ninguna respuesta que estuviera completa, la mayor parte del tiempo el lado del cliente se ejecutaba en el servidor de nodo y, por lo tanto, no es aplicable en mi caso. Así que rastreé todos los temas posibles y finalmente encontré mi respuesta; Intentaré compartir esto y tener un punto en el que todo esté claro.

¡Espero que esto pueda ayudar a algunas personas! ;)

Jérémy Dutheil
fuente

Respuestas:

131

Entonces, para empezar, puse mi proyecto en github, si quieres acceder al código completo: https://github.com/jdutheil/nodePHP

Es un proyecto de ejemplo muy simple: un chat web. Solo tiene un autor y un mensaje, y cuando presiona enviar, se guarda en una base de datos mysql. La idea es enviar actualizaciones en tiempo real y tener una conversación real. ;) Usaremos nodeJS para eso.

No hablaré de código PHP, es realmente simple y no es interesante aquí; lo que quiero mostrarte es cómo integrar tu código nodeJS.

Yo uso express y Socket.IO, así que asegúrese de instalar esos módulos con npm. Luego, creamos un servidor 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 );

Registramos nuestra devolución de llamada de eventos cuando se conecta un nuevo usuario; cada vez que recibimos un mensaje (representa un mensaje de chat), lo transmitimos a todos los usuarios conectados. Ahora, la parte complicada: ¡del lado del cliente! Esa es la parte que me tomó la mayor parte del tiempo, porque no sabía qué script incluir para poder ejecutar el código Socket.IO sin el nodeServer (porque la página del cliente será servida por Apache).

Pero todo ya está hecho; cuando instala el módulo Socket.IO con npm, hay un script disponible en /node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js; que el script lo incluiremos en nuestra página PHP, en mi caso:

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

Y para finalizar, mi nodeClient.js, donde simplemente nos conectamos al servidor de nodos y esperamos a que el evento actualice nuestra página. ;)

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

Intentaré actualizar y mejorar mi código lo antes posible, ¡pero creo que ya está abierto a todas las cosas interesantes! Estoy realmente abierto a recibir consejos y reseñas sobre este tema, ¿es la buena manera de hacerlo?

¡Espero que esto pueda ayudar a algunas personas!

Jérémy Dutheil
fuente
18
Bueno, cuando escribes una pregunta, hay una opción "responde tu propia pregunta, comparte el estilo de preguntas y respuestas de conocimiento", así que pensé que podíamos compartirlo así, lo siento si me equivoco :)
Jérémy Dutheil
4
Como sugerencia, creo que incorporar la respuesta a esta pregunta aquí stackoverflow.com/questions/5818312/mysql-with-node-js es un método superior. evitando cualquier llamada ajax y haciendo que el código esté más en línea con el uso de node. Ahora, PHP puede simplemente seleccionar la información de la base de datos.
blackmambo
1
¿Es posible conectarse a la aplicación de nodo usando io.connect si está en una máquina diferente a su aplicación principal en lugar de tener la aplicación de nodo en el mismo servidor pero usando un puerto diferente?
maembe
1
requieren la firma de hmac como autenticación de mensajes. esto asegura que solo php pueda transmitir mensajes al socket. el socket inspeccionará el token firmado y, si pasa, ti transmitirá el mensaje. esto es bueno para prevenir el spam y garantizar la integridad de los datos. así que nunca publiques directamente en el socket del nodo desde el cliente. en su lugar publique en la aplicación php con ajax, luego transmita eso al servidor de socket. No es trivial abrir una conexión de socket a un servidor websocket con fopen + fwrite o stream select from php.
r3wt
1
De acuerdo con @Bangash, puede usar Node.js para almacenar los datos en la base de datos mysql en lugar de PHP, lo que lo haría mucho más rápido
Parthapratim Neog
2

Tengo otra solución que me funciona bastante bien, pero me gustaría que alguien comentara qué tan efectiva es, ya que (todavía) no he tenido la oportunidad / tiempo de probarla en el servidor real.

Aquí va el código node-js. Puse este código en un archivo llamado 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);  

Y aquí está el fragmento de código simple en php, llamando al servidor node-js con la ayuda de file_get_contents ():

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

Funciona muy bien, cuando cargo la página php, a su vez llama a la página nodeserver.js, que jsonifica el objeto knall.

Tengo dos instalaciones de host local ejecutándose en iis en Windows 10, un servidor php estándar y el servidor nodejs funciona con el iisnode ordenado paquete .

El servidor 'real' se ejecuta en ubuntu.

Creo que esta es una solución fácil y ordenada para la comunicación entre dos servidores, pero tal vez alguien tenga algún comentario al respecto.

Snorvarg
fuente
Esto no tiene sentido para mí, porque está iniciando el servidor de nodo desde dentro del script php. No puedo imaginar ningún caso de uso para esto. Lo que necesitamos es una forma de comunicarnos entre una instancia de node.js en ejecución y php.
Lorenz Meyer
No @Lorenz, ese es el script node.js, que se ejecuta en su propio servidor. Estoy llamando a la página node.js directamente desde php con file_get_contents (), desde otro servidor php. Ahora se usa a diario con más de 500 usuarios al día. ¿Quizás estás confundido porque la pieza "localhost: 3002"? Esto se debe a que este ejemplo se ejecuta en mi computadora local con Windows, con dos servidores independientes en iis.
Snorvarg
Estoy realmente confundido. Esto significa que en nodejs.jsrealidad no es un archivo fuente, pero es una URL que nombró así, porque contiene json? Lo primero no tendría ningún sentido, pero lo segundo me parece muy confuso.
Lorenz Meyer
@Lorenz, traté de aclarar el ejemplo cambiando el nombre del archivo js de nodejs y editando un poco el texto. Para responder a su pregunta, el archivo ahora renombrado a nodeserver.js se ejecuta en su propio servidor. La llamada http.createServer () crea un servidor, que escucha () s para conexiones entrantes en el puerto 80.
Snorvarg
Tenga en cuenta que puede llamar al servidor node.js directamente desde un navegador, simplemente ingrese la URL " localhost: 3002 / nodeserver.js ", y obtendrá una respuesta json. El file_get_contents () en el archivo php obtiene el contenido de otro servidor, en este caso el servidor node.js.
Snorvarg
0

Pruebe algo similar o puede consultar mi blog para obtener un código de muestra completo en nodejs


En el lado de su página:

  • Toma de carga JS

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

  • Hacer objeto del enchufe

var socket = io ();

  • Utilice la emitfunción para enviar datos a nodeserver.

socket.emit ('nueva_notificación', {
mensaje: 'mensaje',
título: 'título',
icono: 'icono',
});

Así que ahora tu código se verá como

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

  });
});

Ahora, en el lado del servidor de Nodo, haga un controlador para su solicitud para obtener su solicitud y envíe un mensaje a todos los dispositivos / navegadores conectados (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');
});

Ahora el lado del cliente / navegador / cliente crea un receptor para recibir el mensaje de socket del servidor del nodo

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