Como integrar nodeJS + Socket.IO e PHP?

98

Recentemente, tenho procurado encontrar uma boa maneira de se comunicar entre nodeJS e PHP. Aqui está a ideia: nodeJS ainda é bastante novo e pode ser meio complicado desenvolver um aplicativo completo apenas com ele. Além disso, você pode precisar dele apenas para um módulo do seu projeto, como notificações em tempo real, chat, ... E você deseja gerenciar todas as outras coisas com o PHP, porque provavelmente é mais fácil para você (e você pode aproveitar os frameworks existentes, como CodeIgniter ou Symfony).

Eu gostaria de ter uma solução fácil; Não quero usar cURL ou um terceiro servidor para comunicação entre os servidores Apache e Node. O que eu quero é ser capaz de capturar eventos do nó em Javascript simples, do lado do cliente.

Não encontrei nenhuma resposta que, quando completa, na maioria das vezes o lado do cliente estava sendo executado pelo servidor do nó e, portanto, não aplicável no meu caso. Então eu pesquisei todos os tópicos possíveis e finalmente encontrei minha resposta; Vou tentar compartilhar isso e chegar a um ponto em que esteja tudo claro.

Espero que isso possa ajudar algumas pessoas! ;)

Jérémy Dutheil
fonte

Respostas:

131

Então, para começar, coloco meu projeto no github, se você quiser acessar o código completo: https://github.com/jdutheil/nodePHP

É um exemplo de projeto muito simples: um chat na web. Você só tem um autor e uma mensagem e, quando pressiona enviar, ela é salva em um banco de dados mysql. A ideia é enviar atualizações em tempo real e ter uma conversa real. ;) Usaremos nodeJS para isso.

Não vou falar sobre código PHP, é muito simples e não é interessante aqui; o que quero mostrar a você é como integrar seu código nodeJS.

Eu uso express e Socket.IO, então certifique-se de instalar esses módulos com npm. Em seguida, criamos um servidor nodeJS simples:

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 nosso callback de eventos quando um novo usuário é conectado; cada vez que recebemos uma mensagem (representa uma mensagem de chat), a transmitimos para todos os usuários conectados. Agora, a parte complicada: lado do cliente! Essa é a parte que mais me ocupava, pois não sabia qual script incluir para poder rodar o código Socket.IO sem o nodeServer (porque a página do cliente será servida pelo Apache).

Mas tudo já está feito; quando você instala o módulo Socket.IO com npm, um script está disponível em /node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js; que o script incluiremos em nossa página PHP, no meu caso:

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

E para terminar, meu nodeClient.js, onde simplesmente nos conectamos ao servidor do nó e esperamos o evento atualizar nossa 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 );
});

Vou tentar atualizar e melhorar meu código o mais rápido possível, mas acho que já está aberto para todas as coisas legais! Estou realmente aberto a conselhos e análises sobre essas coisas, é a boa maneira de fazer isso, ..?

Espero que isso possa ajudar algumas pessoas!

Jérémy Dutheil
fonte
18
Bem, quando você escreve uma pergunta, há uma opção "responda sua própria pergunta, compartilhe conhecimento no estilo P&R", então pensei que poderíamos compartilhar assim, desculpe se estou errado :)
Jérémy Dutheil
4
Como sugestão, acho que incorporar a resposta a esta pergunta aqui stackoverflow.com/questions/5818312/mysql-with-node-js é um método superior. evitando qualquer chamada ajax e tornando o código mais alinhado com o uso de node. Agora, o PHP pode simplesmente selecionar as informações do banco de dados.
blackmambo
1
É possível se conectar ao aplicativo de nó usando io.connect se ele estiver em uma máquina diferente do seu aplicativo principal, em vez de ter o aplicativo de nó no mesmo servidor, mas usando uma porta diferente?
maembe de
1
requer assinatura hmac como autenticação de mensagem. isso garante que apenas php pode transmitir mensagens para o soquete. o socket irá inspecionar o token assinado e, se passar, ti irá então transmitir a mensagem. isso é bom para prevenir spam e garantir a integridade dos dados. portanto, nunca poste diretamente no socket do nó do cliente. em vez disso, poste no aplicativo php com ajax, e então retransmita isso para o servidor de soquete. não é trivial abrir uma conexão de socket para um servidor websocket com fopen + fwrite ou stream select from php.
r3wt
1
De acordo com @Bangash, você pode usar Node.js para armazenar os dados no banco de dados mysql em vez de PHP, o que o tornaria muito mais rápido
Parthapratim Neog
2

Tenho outra solução que funciona muito bem para mim, mas gostaria que alguém comentasse o quão eficaz ela é, pois (ainda) não tive a oportunidade / tempo de testá-la no servidor real.

Aqui vai o código node-js. Eu coloquei este código em um arquivo chamado 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);  

E aqui está um pedaço de código simples em php, chamando o servidor node-js com a ajuda de file_get_contents ():

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

Funciona muito bem, quando eu carrego a página php, ela por sua vez chama a página nodeserver.js, que jsonifica o objeto knall.

Eu tenho duas instalações localhost em execução no iis no windows 10, um servidor php padrão e o nodejs-server funciona com o pacote iisnode puro .

O servidor 'real' é executado no Ubuntu.

Acho que esta é uma solução simples e fácil para comunicação entre dois servidores, mas talvez alguém tenha algum comentário sobre isso?

Snorvarg
fonte
Isso não faz sentido para mim, porque você está iniciando o servidor de nó de dentro do script php. Não consigo imaginar nenhum caso de uso para isso. O que precisamos é uma forma de comunicação entre uma instância node.js em execução e php.
Lorenz Meyer
Não @Lorenz, que é o script node.js, rodando em seu próprio servidor. Estou chamando a página node.js diretamente do php com file_get_contents (), de outro servidor php. Agora é usado diariamente com mais de 500 usuários por dia. Talvez você esteja confuso porque a peça "localhost: 3002"? Isso ocorre porque este exemplo é executado em meu computador Windows local, com dois servidores autônomos no iis.
Snorvarg
Estou realmente confuso. Isso significa que, nodejs.jsna verdade, não é um arquivo de origem, mas é uma URL que você nomeou assim, porque contém json? O primeiro não faria sentido, mas o último me parece muito confuso.
Lorenz Meyer
@Lorenz, tentei esclarecer o exemplo alterando o nome do arquivo nodejs js e editando um pouco o texto. Para responder à sua pergunta, o arquivo agora renomeado para nodeserver.js é executado em seu próprio servidor. A chamada http.createServer () cria um servidor, que escuta () s para conexões de entrada na porta 80.
Snorvarg
Observe que você pode chamar o servidor node.js diretamente de um navegador, bastando inserir o url " localhost: 3002 / nodeserver.js " e você receberá uma resposta json. O file_get_contents () no arquivo php busca o conteúdo de outro servidor, neste caso o servidor node.js.
Snorvarg
0

Tente algo semelhante ou você pode verificar meu blog para obter um exemplo de código completo em nodejs


Do lado da sua página:

  • Carregar soquete JS

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

  • Faça objeto do soquete

var socket = io ();

  • Use a emitfunção para enviar dados para nodeserver.

socket.emit ('new_notification', {
message: 'message',
title: 'title',
icon: 'icon',
});

Então, agora seu código será semelhante a

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

  });
});

Agora, no lado do servidor Node, faça um manipulador para sua solicitação para obter sua solicitação e enviar uma mensagem para todos os 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');
});

Agora, o lado cliente / navegador / cliente faz um receptor para receber mensagem de socket do servidor de nó

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