Skip to content

Instantly share code, notes, and snippets.

@sebastiaanluca
Last active October 1, 2024 08:06
Show Gist options
  • Save sebastiaanluca/c80db73d720c3307b4867e6af86369ef to your computer and use it in GitHub Desktop.
Save sebastiaanluca/c80db73d720c3307b4867e6af86369ef to your computer and use it in GitHub Desktop.
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 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';
}
}
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)
})
@sebastiaanluca
Copy link
Author

Create a new Redis channel using var pub = redis.createClient() and pub.publish('check the docs for the right command here') to allow sending messages to Redis (and thus Laravel).

@quochoangvp
Copy link

How can I receive message from client in Laravel?

@gemins
Copy link

gemins commented Oct 9, 2017

Hi quochoangvp, make a emit function with your client and recive in your server with function on:
Client:
socket.emit('send-data', message);

Server:

socket.on('send-data', function (data) {
       var serverUrl = "https://tuserver";
       try{
                request.post({
                    url: serverUrl + 'recive-message',
                    headers: {'Content-Type': 'application/json'},
                    form: {
                        message: data},
                    json: true
                })
            }catch(err){

            }
    });

Try with this example make your own function.

@shinecorner
Copy link

@gemins Your trick is helpful for me. Thanks.

@jamesh38
Copy link

Great Gist. Sorry if this is a silly question, noob here. How could I make sure a certain user couldn't or could subscribe to specific channels?

@gasparyanyur
Copy link

Please help to solve the problem. Here is my repo https://github.com/gasparyanyur/Laravel-SocketIo-Redis-Nodejs/.. In my case get-data event not works!

@wangta69
Copy link

wangta69 commented Jun 18, 2020

How can I receive message from client in Laravel?

  1. maker subscribe php file.
    == subscribe.php
    `<?php
    //forever -c php subscribe.php
    function f($redis, $chan, $msg) {
    switch($chan) {
    case 'chan-1':
    print "get $msg from $chan\n";
    break;
    case 'chan-2':
    print "get $msg FROM $chan\n";
    break;
    case 'chan-3':
    break;
    }
    }

ini_set('default_socket_timeout', -1);

$redis = new Redis();
$redis->pconnect('127.0.0.1',6379);

$redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f');
?>`

  1. use forever and listen subscribe.php
    forever -c php subscribe.php

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment