Jak zintegrować nodeJS + Socket.IO i PHP?

98

Ostatnio rozglądałem się po okolicy, aby znaleźć dobry sposób komunikacji między nodeJS a PHP. Oto pomysł: nodeJS jest wciąż całkiem nowy i stworzenie pełnej aplikacji tylko z nim może być trochę trudne. Co więcej, możesz go potrzebować tylko dla jednego modułu projektu, takiego jak powiadomienia w czasie rzeczywistym, czat, ... I chcesz zarządzać wszystkimi innymi rzeczami w PHP, ponieważ prawdopodobnie jest to dla Ciebie łatwiejsze (i możesz skorzystać z istniejące frameworki, takie jak CodeIgniter czy Symfony).

Chciałbym mieć proste rozwiązanie; Nie chcę używać cURL ani trzeciego serwera do komunikacji między serwerami Apache i Node. To, czego chcę, to możliwość przechwytywania zdarzeń z węzła w prostym JavaScript, po stronie klienta.

Nie znalazłem żadnych odpowiedzi, które były kompletne, przez większość czasu po stronie klienta była uruchamiana przez serwer węzła, więc nie ma to zastosowania w moim przypadku. Przeszukałem więc wszystkie możliwe tematy i wreszcie znalazłem odpowiedź; Spróbuję się tym podzielić i mieć punkt, w którym wszystko będzie jasne.

Mam nadzieję, że to może pomóc niektórym ludziom! ;)

Jérémy Dutheil
źródło

Odpowiedzi:

131

Więc na początek umieściłem swój projekt na githubie, jeśli chcesz mieć dostęp do pełnego kodu: https://github.com/jdutheil/nodePHP

To bardzo prosty przykładowy projekt: czat internetowy. Masz tylko autora i wiadomość, a po naciśnięciu wyślij jest ona zapisywana w bazie danych mysql. Chodzi o to, aby wysyłać aktualizacje w czasie rzeczywistym i prowadzić prawdziwą rozmowę. ;) Użyjemy do tego nodeJS.

Nie będę mówić o kodzie PHP, jest tu naprawdę prosty i mało interesujący; chcę wam pokazać, jak zintegrować kod nodeJS.

Używam express i Socket.IO, więc pamiętaj, aby zainstalować te moduły z npm. Następnie tworzymy prosty serwer nodeJS:

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

Zarejestrowaliśmy nasze wywołanie zwrotne zdarzeń, gdy nowy użytkownik jest podłączony; za każdym razem, gdy otrzymujemy wiadomość (reprezentuje wiadomość na czacie), wysyłamy ją do wszystkich podłączonych użytkowników. A teraz trudna część: po stronie klienta! Ta część zajmowała mi najwięcej czasu, ponieważ nie wiedziałem, który skrypt zawiera, aby móc uruchomić kod Socket.IO bez nodeServer (ponieważ strona klienta będzie obsługiwana przez Apache).

Ale wszystko już jest zrobione; kiedy instalujesz moduł Socket.IO z npm, skrypt jest dostępny w /node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js; że skrypt umieścimy na naszej stronie PHP, w moim przypadku:

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

I na koniec mój nodeClient.js, gdzie po prostu łączymy się z serwerem węzłów i czekamy, aż zdarzenie zaktualizuje naszą stronę. ;)

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

Postaram się jak najszybciej zaktualizować i ulepszyć mój kod, ale myślę, że jest już otwarty na wszystkie fajne rzeczy! Jestem naprawdę otwarty na porady i recenzje na ten temat, czy to dobry sposób, aby to zrobić…?

Mam nadzieję, że to może pomóc niektórym ludziom!

Jérémy Dutheil
źródło
18
Cóż, kiedy piszesz pytanie, jest opcja "odpowiedz na własne pytanie, podziel się wiedzą w stylu pytań i odpowiedzi", więc pomyślałem, że możemy się tak podzielić, przepraszam, jeśli się mylę :)
Jérémy Dutheil
4
Zgodnie z sugestią, myślę, że włączenie tutaj odpowiedzi na to pytanie stackoverflow.com/questions/5818312/mysql-with-node-js jest lepszą metodą. unikanie wywołań AJAX i uczynienie kodu bardziej wbudowanym przy użyciu node. Teraz PHP może po prostu wybrać informacje z bazy danych.
blackmambo
1
Czy można połączyć się z aplikacją węzła za pomocą io.connect, jeśli znajduje się ona na innym komputerze niż główna aplikacja, zamiast mieć aplikację węzła na tym samym serwerze, ale przy użyciu innego portu?
maembe
1
wymagają podpisu hmac jako uwierzytelnienia wiadomości. zapewnia to, że tylko php może rozsyłać wiadomości do gniazda. gniazdo sprawdzi podpisany token i jeśli przejdzie pomyślnie, ti wyśle ​​wiadomość. jest to dobre do zapobiegania spamowi i zapewniania integralności danych. więc nigdy nie wysyłaj bezpośrednio do gniazda węzła od klienta. zamiast tego wyślij do aplikacji php za pomocą ajax, a następnie przekaż to do serwera gniazda. otwarcie połączenia przez gniazdo z serwerem WebSocket za pomocą fopen + fwrite lub stream select z php jest dość nietrywialne.
r3wt
1
Uzgodniono z @Bangash, możesz użyć Node.js do przechowywania danych w bazie danych mysql zamiast PHP, co znacznie przyspieszyłoby to
Parthapratim Neog
2

Mam inne rozwiązanie, które działa całkiem nieźle, ale chciałbym, aby ktoś skomentował jego skuteczność, ponieważ nie miałem (jeszcze) okazji / czasu przetestować go na prawdziwym serwerze.

Oto kod node-js. Umieściłem ten kod w pliku o nazwie 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);  

A oto prosty fragment kodu w php, wywołujący serwer node-js za pomocą file_get_contents ():

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

Działa świetnie, kiedy ładuję stronę php, to z kolei wywołuje stronę nodeserver.js, która jsonifikuje obiekt knall.

Mam dwie instalacje hosta lokalnego działające na iis w systemie Windows 10, jeden standardowy serwer php, a serwer nodejs działa z zgrabnym iisnode pakietem .

„Prawdziwy” serwer działa na systemie Ubuntu.

Myślę, że to zgrabne i łatwe rozwiązanie do komunikacji między dwoma serwerami, ale może ktoś ma na ten temat jakieś uwagi?

Snorvarg
źródło
Nie ma to dla mnie sensu, ponieważ uruchamiasz serwer węzła z poziomu skryptu php. Nie mogę sobie wyobrazić żadnego przypadku użycia tego. Potrzebujemy sposobu na komunikację między działającą instancją node.js a php.
Lorenz Meyer
Nie @Lorenz, czyli skrypt node.js działający na własnym serwerze. Wołam stronę node.js bezpośrednio z php z file_get_contents (), z innego serwera php. Jest teraz w codziennym użyciu przez ponad 500 użytkowników dziennie. Może jesteś zdezorientowany, ponieważ fragment „localhost: 3002”? Dzieje się tak, ponieważ ten przykład działa na moim lokalnym komputerze z systemem Windows, z dwoma samodzielnymi serwerami w iis.
Snorvarg
Jestem bardzo zmieszany. Oznacza to, że w nodejs.jsrzeczywistości nie jest to plik źródłowy, ale jest to adres URL, który tak nazwałeś, ponieważ zawiera json? Pierwsza nie miałaby sensu, ale ta druga wydaje mi się bardzo zagmatwana.
Lorenz Meyer
@Lorenz, próbowałem wyjaśnić przykład, zmieniając nazwę pliku js nodejs i nieco edytując tekst. Aby odpowiedzieć na Twoje pytanie, plik, którego nazwa została teraz zmieniona na nodeserver.js, jest uruchamiany na jego własnym serwerze. Http.createServer () tworzy połączenia z serwerem, który listen () s dla połączeń przychodzących na port 80.
Snorvarg
Zwróć uwagę, że możesz wywołać serwer node.js bezpośrednio z przeglądarki, po prostu wpisując adres URL „ localhost: 3002 / nodeserver.js ”, a otrzymasz odpowiedź json. Funkcja file_get_contents () w pliku php pobiera zawartość z innego serwera, w tym przypadku z serwera node.js.
Snorvarg,
0

Spróbuj podobnego lub możesz sprawdzić mój blog, aby uzyskać pełny przykładowy kod na nodejs


Po Twojej stronie:

  • Załaduj gniazdo JS

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

  • Zrób przedmiot z gniazda

var gniazdo = io ();

  • Użyj emitfunkcji, aby wysłać dane do serwera nodeser.

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

Więc teraz twój kod będzie wyglądał

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

  });
});

Teraz po stronie serwera Node utwórz moduł obsługi dla żądania, aby odebrać żądanie i wysłać wiadomość do wszystkich podłączonych urządzeń / przeglądarek (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');
});

Teraz strona klient / przeglądarka / klient tworzy odbiornik do odbierania wiadomości gniazda z serwera węzła

<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
źródło