toannk
4/29/2016 - 12:12 PM

Laravel + Redis + NodeJS + Socket.io pub/sub secure server and client supporting multiple rooms, channels, users, … Add `client.js` to your

Laravel + Redis + NodeJS + Socket.io pub/sub secure server and client supporting multiple rooms, channels, users, … Add client.js to your client app, run node server.js, and trigger the Laravel event any way you want to broadcast the data.

const SERVER_PORT = 8000

//

var fs = require('fs')
var https = require('https')

var express = require('express')
var app = express()

var options = {
    key: fs.readFileSync('/etc/nginx/ssl/yourapp.key'),
    cert: fs.readFileSync('/etc/nginx/ssl/yourapp.crt'),
}

var server = https.createServer(options, app)
var io = require('socket.io').listen(server)

var redis = require('redis')
var ioredis = require('socket.io-redis')

// Multi-server socket handling allowing you to scale horizontally 
// or use a load balancer with Redis distributing messages across servers.
io.adapter(ioredis({host: 'localhost', port: 6379}))

//

/*
 * Redis pub/sub
 */

// Listen to local Redis broadcasts
var sub = redis.createClient()

sub.on('error', function (error) {
    console.log('ERROR ' + error)
})

sub.on('subscribe', function (channel, count) {
    console.log('SUBSCRIBE', channel, count)
})

// Handle messages from channels we're subscribed to
sub.on('message', function (channel, payload) {
    console.log('INCOMING MESSAGE', channel, payload)
    
    payload = JSON.parse(payload)
    
    // Merge channel into payload
    payload.data._channel = channel
    
    // Send the data through to any client in the channel room (!)
    // (i.e. server room, usually being just the one user)
    io.sockets.in(channel).emit(payload.event, payload.data)
})

/*
 * Server
 */

// Start listening for incoming client connections
io.sockets.on('connection', function (socket) {
    
    console.log('NEW CLIENT CONNECTED')
    
    socket.on('subscribe-to-channel', function (data) {
        console.log('SUBSCRIBE TO CHANNEL', data)
        
        // Subscribe to the Redis channel using our global subscriber
        sub.subscribe(data.channel)
        
        // Join the (somewhat local) server room for this channel. This
        // way we can later pass our channel events right through to 
        // the room instead of broadcasting them to every client.
        socket.join(data.channel)
    })
    
    socket.on('disconnect', function () {
        console.log('DISCONNECT')
    })
    
})

// Start listening for client connections
server.listen(SERVER_PORT, function () {
    console.log('Listening to incoming client connections on port ' + SERVER_PORT)
})
const PRIVATE_CHANNEL = 'yourprivatehashedchannelid'

//

var io = require('socket.io-client')

var host = window.location.host.split(':')[0]
var socket = io.connect('//' + host + ':8000', {secure: true, rejectUnauthorized: false})

socket.on('connect', function () {
    console.log('CONNECT')
    
    socket.on('event', function (data) {
        console.log('EVENT', data)
    })
    
    socket.on('messages.new', function (data) {
        console.log('NEW PRIVATE MESSAGE', data)
    })
    
    socket.on('disconnect', function () {
        console.log('disconnect')
    })
    
    // Kick it off
    // Can be any channel. For private channels, Laravel should pass it upon page load (or given by another user).
    socket.emit('subscribe-to-channel', {channel: PRIVATE_CHANNEL})
    console.log('SUBSCRIBED TO <' + PRIVATE_CHANNEL + '>');
})
<?php

namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class NewMessage extends Event implements ShouldBroadcast
{
    
    use SerializesModels;
    
    public $from;
    public $to;
    public $message;
    
    public function __construct()
    {
        $this->from = 'Bond';
        $this->to = 'Bean';
        $this->message = 'You don\'t say??';
    }
    
    public function broadcastOn()
    {
        return ['yourprivatehashedchannelid', 'thiscouldbeaglobalchanneltoo'];
    }
    
    /**
     * Get the broadcast event name.
     *
     * @return string
     */
    public function broadcastAs()
    {
        return 'messages.new';
    }
    
}