จะรวม nodeJS + Socket.IO และ PHP ได้อย่างไร?


98

เมื่อเร็ว ๆ นี้ฉันได้มองไปรอบ ๆ เพื่อหาวิธีที่ดีในการสื่อสารระหว่าง nodeJS และ PHP นี่คือแนวคิด: nodeJS ยังค่อนข้างใหม่และอาจเป็นเรื่องยากที่จะพัฒนาแอปพลิเคชันเต็มรูปแบบเฉพาะกับมัน ยิ่งไปกว่านั้นคุณอาจต้องการเพียงโมดูลเดียวในโครงการของคุณเช่นการแจ้งเตือนแบบเรียลไทม์แชท ... และคุณต้องการจัดการสิ่งอื่น ๆ ทั้งหมดด้วย PHP เพราะมันอาจจะง่ายกว่าสำหรับคุณ (และคุณสามารถใช้ประโยชน์จาก เฟรมเวิร์กที่มีอยู่เช่น CodeIgniter หรือ Symfony)

ฉันต้องการวิธีง่ายๆ ฉันไม่ต้องการใช้ cURL หรือเซิร์ฟเวอร์ที่สามเพื่อสื่อสารระหว่างเซิร์ฟเวอร์ Apache และ Node สิ่งที่ฉันต้องการคือสามารถจับเหตุการณ์จากโหนดใน Javascript แบบง่ายฝั่งไคลเอ็นต์

ฉันไม่พบคำตอบใด ๆ ว่าที่สมบูรณ์เวลาส่วนใหญ่ฝั่งไคลเอ็นต์ทำงานโดยเซิร์ฟเวอร์โหนดดังนั้นจึงไม่สามารถใช้ได้ในกรณีของฉัน ดังนั้นฉันจึงรวบรวมข้อมูลทุกหัวข้อที่เป็นไปได้และในที่สุดก็พบคำตอบของฉัน ฉันจะพยายามแบ่งปันสิ่งนี้และเพื่อให้มีจุดที่ชัดเจนทั้งหมด

หวังว่านี่จะช่วยคนได้บ้าง! ;)

คำตอบ:


131

ดังนั้นก่อนอื่นฉันวางโปรเจ็กต์ของฉันไว้ที่ github หากคุณต้องการเข้าถึงโค้ดแบบเต็ม: https://github.com/jdutheil/nodePHP

เป็นโครงการตัวอย่างที่ง่ายมาก: เว็บแชท คุณมีเพียงผู้เขียนและข้อความและเมื่อคุณกดส่งจะถูกบันทึกไว้ในฐานข้อมูล mysql แนวคิดคือการส่งการอัปเดตตามเวลาจริงและมีการสนทนาจริง ;) เราจะใช้ nodeJS สำหรับสิ่งนั้น

ฉันจะไม่พูดถึงโค้ด PHP มันง่ายมากและไม่น่าสนใจที่นี่ สิ่งที่ฉันต้องการแสดงให้คุณเห็นคือวิธีการรวมโค้ด nodeJS ของคุณ

ฉันใช้ express และ Socket.IO ดังนั้นอย่าลืมติดตั้งโมดูลเหล่านั้นด้วย npm จากนั้นเราสร้างเซิร์ฟเวอร์ 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 );

เราลงทะเบียนการโทรกลับเหตุการณ์ของเราเมื่อมีการเชื่อมต่อกับผู้ใช้ใหม่ ทุกครั้งที่เราได้รับข้อความ (แสดงถึงข้อความแชท) เราจะถ่ายทอดให้ผู้ใช้ทุกคนที่เชื่อมต่อ ตอนนี้ส่วนที่ยุ่งยาก: ฝั่งไคลเอ็นต์! ส่วนที่ใช้เวลาส่วนใหญ่ฉันเพราะฉันไม่รู้ว่าสคริปต์ใดรวมถึงสามารถรันโค้ด Socket.IO ได้โดยไม่ต้องใช้ nodeServer (เนื่องจากหน้าไคลเอนต์จะให้บริการโดย Apache)

แต่ทุกอย่างเรียบร้อยแล้ว เมื่อคุณติดตั้งโมดูล Socket.IO ด้วย npm สคริปต์จะพร้อมใช้งานใน/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js; ว่าสคริปต์ที่เราจะรวมไว้ในหน้า PHP ของเราในกรณีของฉัน:

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

และเพื่อเสร็จสิ้น nodeClient.js ของฉันซึ่งเราเพียงแค่เชื่อมต่อกับเซิร์ฟเวอร์โหนดและรอให้เหตุการณ์อัปเดตหน้าของเรา ;)

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

ฉันจะพยายามอัปเดตและปรับปรุงโค้ดของฉันโดยเร็วที่สุด แต่ฉันคิดว่ามันเปิดรับสิ่งดีๆทั้งหมดแล้ว! ฉันยินดีให้คำแนะนำและรีวิวเกี่ยวกับเรื่องนี้เป็นวิธีที่ดีหรือไม่ .. ?

หวังว่านี่จะช่วยคนได้บ้าง!


18
เมื่อคุณเขียนคำถามจะมีตัวเลือก "ตอบคำถามของคุณเองแบ่งปันความรู้สไตล์การถามตอบ" ดังนั้นฉันคิดว่าเราสามารถแบ่งปันแบบนี้ได้ขอโทษถ้าฉันผิด :)
Jérémy Dutheil

4
ตามคำแนะนำฉันคิดว่าการรวมคำตอบสำหรับคำถามนี้ไว้ที่นี่stackoverflow.com/questions/5818312/mysql-with-node-jsเป็นวิธีที่ดีกว่า หลีกเลี่ยงการโทร ajax และทำให้โค้ดอินไลน์มากขึ้นด้วยการใช้โหนด ตอนนี้ PHP สามารถเลือกข้อมูลจากฐานข้อมูลได้แล้ว
blackmambo

1
เป็นไปได้ไหมที่จะเชื่อมต่อกับแอปโหนดโดยใช้ io.connect หากอยู่ในเครื่องอื่นที่ไม่ใช่แอปหลักของคุณแทนที่จะมีแอปโหนดบนเซิร์ฟเวอร์เดียวกัน แต่ใช้พอร์ตอื่น
maembe

1
ต้องการการลงนาม hmac เป็นการรับรองความถูกต้องของข้อความ สิ่งนี้ทำให้มั่นใจได้ว่ามีเพียง php เท่านั้นที่สามารถถ่ายทอดข้อความไปยังซ็อกเก็ตได้ ซ็อกเก็ตจะตรวจสอบโทเค็นที่เซ็นชื่อแล้วและหากผ่านไปแล้ว ti จะถ่ายทอดข้อความ นี่เป็นสิ่งที่ดีสำหรับการป้องกันสแปมและทำให้ข้อมูลมีความสมบูรณ์ ดังนั้นอย่าโพสต์โดยตรงไปยังซ็อกเก็ตโหนดจากไคลเอนต์ แทนที่จะโพสต์ไปที่แอพ php ด้วย ajax จากนั้นส่งต่อไปยังเซิร์ฟเวอร์ซ็อกเก็ต การเปิดการเชื่อมต่อซ็อกเก็ตไปยังเซิร์ฟเวอร์ websocket ด้วย fopen + fwrite หรือสตรีมที่เลือกจาก php นั้นค่อนข้างไม่สำคัญ
r3wt

1
เห็นด้วยกับ @Bangash คุณสามารถใช้ Node.js เพื่อจัดเก็บข้อมูลลงใน mysql db แทน PHP ซึ่งจะทำให้เร็วขึ้นมาก
Parthapratim Neog

2

ฉันมีวิธีแก้ปัญหาอื่นที่ใช้งานได้ดีสำหรับฉัน แต่ฉันต้องการให้ใครบางคนแสดงความคิดเห็นว่ามันมีประสิทธิภาพเพียงใดเนื่องจากฉันยังไม่มีโอกาส / เวลาทดสอบบนเซิร์ฟเวอร์จริง

นี่คือรหัส node-js ฉันใส่รหัสนี้ในไฟล์ชื่อ 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);  

และนี่คือโค้ดง่ายๆใน php เรียกเซิร์ฟเวอร์ node-js ด้วยความช่วยเหลือของ file_get_contents ():

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

ใช้งานได้ดีเมื่อฉันโหลด php-page มันจะเรียกหน้า nodeserver.js ซึ่ง jsonify the knall-object

ฉันมีการติดตั้ง localhost สองตัวที่ทำงานบน iis บน windows 10 หนึ่งเซิร์ฟเวอร์ php มาตรฐานและเซิร์ฟเวอร์ nodejs ทำงานร่วมกับแพ็คเกจiisnode ที่เรียบร้อย

เซิร์ฟเวอร์ 'จริง' ทำงานบนอูบุนตู

ฉันคิดว่านี่เป็นวิธีที่ง่ายและเป็นระเบียบสำหรับการสื่อสารระหว่างเซิร์ฟเวอร์สองเครื่อง แต่อาจมีใครแสดงความคิดเห็นเกี่ยวกับเรื่องนี้บ้าง?


สิ่งนี้ไม่สมเหตุสมผลสำหรับฉันเพราะคุณกำลังเรียกใช้เซิร์ฟเวอร์โหนดจากภายในสคริปต์ php ฉันไม่สามารถจินตนาการถึงกรณีการใช้งานใด ๆ สำหรับสิ่งนี้ สิ่งที่เราต้องการคือวิธีสื่อสารระหว่างอินสแตนซ์ node.js ที่กำลังรันและ php
Lorenz Meyer

ไม่มี @Lorenz นั่นคือสคริปต์ node.js ซึ่งทำงานบนเซิร์ฟเวอร์ของตัวเอง ฉันกำลังเรียก node.js-page โดยตรงจาก php ด้วย file_get_contents () จากเซิร์ฟเวอร์ php อื่น ขณะนี้มีการใช้งานในชีวิตประจำวันโดยมีผู้ใช้มากกว่า 500 คนต่อวัน คุณอาจจะสับสนเพราะชิ้นส่วน "localhost: 3002"? นั่นเป็นเพราะตัวอย่างนี้ทำงานบนคอมพิวเตอร์ที่ใช้ Windows ของฉันโดยมีเซิร์ฟเวอร์แบบสแตนด์อโลนสองเซิร์ฟเวอร์ใน iis
Snorvarg

ฉันสับสนจริงๆ ซึ่งหมายความว่าnodejs.jsจริง ๆ แล้วไม่ใช่ซอร์สไฟล์ แต่เป็น URL ที่คุณตั้งชื่อเช่นนั้นเนื่องจากมี json? ครั้งแรกจะไม่สมเหตุสมผล แต่อย่างหลังดูสับสนสำหรับฉันมาก
Lorenz Meyer

@Lorenz ฉันพยายามอธิบายตัวอย่างโดยการเปลี่ยนชื่อไฟล์ของไฟล์ nodejs js และแก้ไขข้อความเล็กน้อย เพื่อตอบคำถามของคุณตอนนี้ไฟล์ที่เปลี่ยนชื่อเป็น nodeserver.js จะทำงานบนเซิร์ฟเวอร์ของตัวเอง การเรียกใช้ http.createServer () สร้างเซิร์ฟเวอร์ซึ่งรับฟังการเชื่อมต่อขาเข้าที่พอร์ต 80
Snorvarg

โปรดทราบว่าคุณสามารถเรียกเซิร์ฟเวอร์ node.js ได้โดยตรงจากเบราว์เซอร์โดยเพียงแค่ป้อน url " localhost: 3002 / nodeserver.js " และคุณจะได้รับ json-response file_get_contents () ในไฟล์ php ดึงเนื้อหาจากเซิร์ฟเวอร์อื่นในกรณีนี้คือเซิร์ฟเวอร์ node.js
Snorvarg

0

ลองคล้ายกันหรือคุณสามารถตรวจสอบบล็อกของฉันเพื่อดูโค้ดตัวอย่างที่สมบูรณ์บน nodejs


ในหน้าของคุณ:

  • โหลด Socket JS

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

  • สร้างวัตถุของซ็อกเก็ต

var ซ็อกเก็ต = io ();

  • ใช้emitฟังก์ชันเพื่อส่งข้อมูลไปยังเซิร์ฟเวอร์โหนด

socket.emit ('new_notification', {
ข้อความ: 'ข้อความ',
หัวเรื่อง: 'title',
ไอคอน: 'icon',
});

ตอนนี้รหัสของคุณจะเป็นอย่างไร

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

  });
});

ตอนนี้ทางฝั่งเซิร์ฟเวอร์ Node สร้างตัวจัดการสำหรับคำขอของคุณเพื่อรับคำขอของคุณและส่งข้อความไปยังอุปกรณ์ / เบราว์เซอร์ที่เชื่อมต่อทั้งหมด (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');
});

ตอนนี้ฝั่งไคลเอนต์ / เบราว์เซอร์ / ไคลเอนต์สร้างตัวรับเพื่อรับข้อความซ็อกเก็ตจากเซิร์ฟเวอร์โหนด

<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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.