Làm thế nào để tích hợp nodeJS + Socket.IO và PHP?


98

Gần đây tôi đã tìm kiếm xung quanh, để tìm một cách tốt để giao tiếp giữa nodeJS và PHP. Đây là ý tưởng: nodeJS vẫn còn khá mới và có thể hơi khó để phát triển một ứng dụng đầy đủ chỉ với nó. Hơn nữa, bạn có thể chỉ cần nó cho một mô-đun trong dự án của mình, như thông báo thời gian thực, trò chuyện, ... Và bạn muốn quản lý tất cả những thứ khác bằng PHP, vì nó có thể dễ dàng hơn cho bạn (và bạn có thể tận dụng các khuôn khổ hiện có, như CodeIgniter hoặc Symfony).

Tôi muốn có một giải pháp dễ dàng; Tôi không muốn sử dụng cURL hoặc máy chủ thứ ba để giao tiếp giữa máy chủ Apache và Node. Điều tôi muốn là có thể bắt các sự kiện từ nút trong Javascript đơn giản, phía máy khách.

Tôi không tìm thấy bất kỳ câu trả lời nào mà hoàn chỉnh, hầu hết thời gian phía máy khách đang chạy bởi máy chủ nút và do đó không áp dụng trong trường hợp của tôi. Vì vậy, tôi đã thu thập thông tin tất cả các chủ đề có thể, và cuối cùng tìm ra câu trả lời của mình; Tôi sẽ cố gắng chia sẻ điều này, và có một điểm mà tất cả đều rõ ràng.

Hy vọng điều này có thể giúp một số người! ;)

Câu trả lời:


131

Vì vậy, để bắt đầu, tôi đặt dự án của mình trên github, nếu bạn muốn truy cập vào mã đầy đủ: https://github.com/jdutheil/nodePHP

Đó là một dự án ví dụ rất đơn giản: một cuộc trò chuyện trên web. Bạn chỉ có một tác giả và tin nhắn, và khi bạn nhấn gửi, nó sẽ được lưu trong cơ sở dữ liệu mysql. Ý tưởng là gửi thông tin cập nhật theo thời gian thực và trò chuyện thực tế. ;) Chúng tôi sẽ sử dụng nodeJS cho điều đó.

Tôi sẽ không nói về mã PHP, nó thực sự đơn giản và không thú vị ở đây; điều tôi muốn chỉ cho bạn là cách tích hợp mã nodeJS của bạn.

Tôi sử dụng express và Socket.IO, vì vậy hãy đảm bảo cài đặt các mô-đun đó với npm. Sau đó, chúng tôi tạo một máy chủ nodeJS đơn giản:

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

Chúng tôi đã đăng ký gọi lại sự kiện của mình khi một người dùng mới được kết nối; mỗi khi chúng tôi nhận được một tin nhắn (đại diện cho một tin nhắn trò chuyện), chúng tôi sẽ phát nó cho mọi người dùng được kết nối. Bây giờ, phần khó khăn: phía khách hàng! Đó là phần khiến tôi mất phần lớn thời gian, vì tôi không biết tập lệnh nào bao gồm để có thể chạy mã Socket.IO mà không có nodeServer (vì trang khách sẽ được Apache phục vụ).

Nhưng mọi thứ đã được thực hiện; khi bạn cài đặt mô-đun Socket.IO với npm, một tập lệnh có sẵn trong /node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js; rằng tập lệnh mà chúng tôi sẽ đưa vào trang PHP của mình, trong trường hợp của tôi:

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

Và để kết thúc, nodeClient.js của tôi, nơi chúng tôi chỉ cần kết nối với máy chủ nút và đợi sự kiện cập nhật trang của chúng tôi. ;)

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

Tôi sẽ cố gắng cập nhật và cải thiện mã của mình càng sớm càng tốt, nhưng tôi nghĩ nó đã mở cho tất cả những điều thú vị! Tôi thực sự cởi mở để được tư vấn và đánh giá về công cụ này, nó có phải là cách tốt để làm điều đó, ..?

Hy vọng điều này có thể giúp một số người!


18
Vâng, khi bạn viết một câu hỏi có là một lựa chọn "trả lời câu hỏi của riêng bạn, chia sẻ kiến thức Q & A phong cách", vì vậy tôi nghĩ rằng chúng tôi có thể chia sẻ như thế này, xin lỗi nếu tôi sai :)
Jérémy Dutheil

4
Theo gợi ý, tôi nghĩ kết hợp câu trả lời cho câu hỏi này tại đây stackoverflow.com/questions/5818312/mysql-with-node-js là một phương pháp tốt hơn. tránh bất kỳ lệnh gọi ajax nào và làm cho mã nội tuyến hơn với việc sử dụng nút. Bây giờ, PHP có thể chỉ cần chọn thông tin từ cơ sở dữ liệu.
blackmambo

1
Có thể kết nối với ứng dụng nút bằng io.connect nếu nó nằm trên một máy khác với ứng dụng chính của bạn thay vì có ứng dụng nút trên cùng một máy chủ nhưng sử dụng một cổng khác không?
maembe

1
yêu cầu ký hmac làm xác thực tin nhắn. điều này đảm bảo rằng chỉ php mới có thể phát thông báo tới socket. socket sẽ kiểm tra mã thông báo đã ký và nếu nó vượt qua, ti sẽ phát thông báo. điều này rất tốt để ngăn chặn thư rác và đảm bảo tính toàn vẹn của dữ liệu. vì vậy không bao giờ đăng trực tiếp lên ổ cắm nút từ máy khách. thay vào đó đăng lên ứng dụng php với ajax, sau đó chuyển tiếp nó đến máy chủ socket. việc mở một kết nối socket đến máy chủ websocket bằng fopen + fwrite hoặc chọn luồng từ php là khá không tầm thường.
r3wt

1
Đồng ý với @Bangash, Bạn có thể sử dụng Node.js để lưu trữ các dữ liệu vào db mysql thay vì PHP, điều đó sẽ làm cho nó một lô nhanh
Parthapratim Neog

2

Tôi có một giải pháp khác hoạt động khá tốt đối với tôi, nhưng tôi muốn ai đó nhận xét về mức độ hiệu quả của nó, vì tôi chưa (chưa) có cơ hội / thời gian để thử nghiệm nó trên máy chủ thực.

Đây là mã node-js. Tôi đặt mã này trong một tệp có tên là nodeerver.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);  

Và đây là đoạn mã đơn giản trong php, gọi máy chủ node-js với sự trợ giúp của file_get_contents ():

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

Hoạt động tuyệt vời, khi tôi tải trang php, nó lần lượt gọi trang nodeerver.js, trang này jsonify đối tượng knall.

Tôi có hai bản cài đặt localhost đang chạy trên iis trên windows 10, một bản php-server tiêu chuẩn và nodejs-server hoạt động với gói iisnode gọn gàng .

Máy chủ 'thực' được chạy trên ubuntu.

Tôi nghĩ rằng đây là một giải pháp gọn gàng và dễ dàng để giao tiếp giữa hai máy chủ, nhưng có thể ai đó có bất kỳ nhận xét nào về nó?


Điều này không có ý nghĩa với tôi, bởi vì bạn đang khởi chạy máy chủ nút từ bên trong tập lệnh php. Tôi không thể chọn bất kỳ trường hợp sử dụng nào cho việc này. Những gì chúng ta cần là một cách để giao tiếp giữa một phiên bản node.js đang chạy và php.
Lorenz Meyer

Không có @Lorenz, đó là tập lệnh node.js, chạy trên máy chủ của chính nó. Tôi đang gọi trang node.js trực tiếp từ php với file_get_contents (), từ một máy chủ php khác. Nó hiện đang được sử dụng hàng ngày với hơn 500 người dùng mỗi ngày. Có thể bạn đang bối rối vì mẩu "localhost: 3002"? Đó là vì ví dụ này chạy trên máy tính Windows cục bộ của tôi, với hai máy chủ độc lập trong iis.
Snorvarg

Tôi thực sự bối rối. Điều này có nghĩa là nó nodejs.jsthực sự không phải là một tệp nguồn, nhưng nó là một URL mà bạn đặt tên như vậy, bởi vì nó chứa json? Cái đầu tiên sẽ không có ý nghĩa gì, nhưng cái sau có vẻ rất khó hiểu đối với tôi.
Lorenz Meyer

@Lorenz, tôi đã cố gắng làm rõ ví dụ bằng cách thay đổi tên tệp của tệp nodejs js và chỉnh sửa văn bản một chút. Để trả lời câu hỏi của bạn, tệp hiện được đổi tên thành nodeerver.js được chạy trên máy chủ của chính nó. Lệnh gọi http.createServer () tạo ra một máy chủ, máy chủ này sẽ lắng nghe () các kết nối đến tại cổng 80.
Snorvarg

Lưu ý rằng bạn có thể gọi máy chủ node.js trực tiếp từ trình duyệt, chỉ cần nhập url " localhost: 3002 / nodeerver.js " và bạn sẽ nhận được phản hồi json. File_get_contents () trong tệp php tìm nạp nội dung từ một máy chủ khác, trong trường hợp này là máy chủ node.js.
Snorvarg

0

Hãy thử tương tự hoặc bạn có thể kiểm tra blog của tôi để biết mã mẫu hoàn chỉnh trên nodejs


Về phía trang của bạn:

  • Tải Socket JS

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

  • Tạo đối tượng của ổ cắm

var socket = io ();

  • Sử dụng emitchức năng để gửi dữ liệu đến máy chủ.

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

Vì vậy, bây giờ mã của bạn sẽ giống như

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

  });
});

Bây giờ, phía máy chủ Node thực hiện xử lý yêu cầu của bạn để nhận yêu cầu của bạn và gửi tin nhắn đến tất cả các thiết bị / trình duyệt được kết nối (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');
});

Bây giờ phía máy khách / trình duyệt / máy khách tạo bộ thu để nhận thông báo socket từ máy chủ nút

<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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.