listen

void Worker::listen(void)

Utilizzato per eseguire l'ascolto dopo l'istanza di Worker.

Questo metodo è principalmente utilizzato per creare dinamicamente nuove istanze di Worker dopo l'avvio del processo Worker, e consente a uno stesso processo di ascoltare più porte, supportando vari protocolli. È importante notare che utilizzare questo metodo aggiunge solo l'ascolto nel processo corrente e non crea dinamicamente nuovi processi, né attiva il metodo onWorkerStart.

Ad esempio, se un Worker HTTP avvia un Worker Websocket dopo la sua istanza, allora questo processo potrà essere accessibile sia tramite il protocollo HTTP che tramite quello Websocket. Poiché il Worker Websocket e il Worker HTTP sono nello stesso processo, possono accedere a variabili di memoria condivise e condividere tutte le connessioni socket. Questo consente di ricevere richieste HTTP e quindi di interagire con i client Websocket per inviare dati ai client.

Attenzione:

Se la versione di PHP è <= 7.0, non è supportata l'istanza di Worker con la stessa porta in più sotto-processi. Ad esempio, se il processo A crea un Worker per ascoltare la porta 2016, il processo B non potrà creare un altro Worker per ascoltare la porta 2016, altrimenti verrà restituito un errore Address already in use. Ad esempio, il codice seguente è impossibile da eseguire.

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

$worker = new Worker();
// 4 processi
$worker->count = 4;
// Dopo che ogni processo è avviato, aggiungi un Worker per ascoltare nel processo corrente
$worker->onWorkerStart = function($worker)
{
    /**
     * Quando vengono avviati 4 processi, viene creato un Worker sulla porta 2016
     * Quando viene eseguito worker->listen(), verrà restituito l'errore Address already in use
     * Se worker->count=1, non verrà restituito alcun errore
     */
    $inner_worker = new Worker('http://0.0.0.0:2016');
    $inner_worker->onMessage = 'on_message';
    // Esegui l'ascolto. Qui verrà restituito l'errore Address already in use
    $inner_worker->listen();
};

$worker->onMessage = 'on_message';

function on_message(TcpConnection $connection, $data)
{
    $connection->send("hello\n");
}

// Esegui worker
Worker::runAll();

Se la tua versione di PHP è >= 7.0, puoi impostare Worker->reusePort=true, in questo modo sarà possibile per più sotto-processi creare Worker sulla stessa porta. Vedi l'esempio seguente:

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

$worker = new Worker('text://0.0.0.0:2015');
// 4 processi
$worker->count = 4;
// Dopo che ogni processo è avviato, aggiungi un Worker per ascoltare nel processo corrente
$worker->onWorkerStart = function($worker)
{
    $inner_worker = new Worker('http://0.0.0.0:2016');
    // Imposta il riutilizzo della porta, consente di creare Worker che ascoltano la stessa porta (richiede PHP>=7.0)
    $inner_worker->reusePort = true;
    $inner_worker->onMessage = 'on_message';
    // Esegui l'ascolto. L'ascolto normale non restituirà errori
    $inner_worker->listen();
};

$worker->onMessage = 'on_message';

function on_message(TcpConnection $connection, $data)
{
    $connection->send("hello\n");
}

// Esegui worker
Worker::runAll();

Esempio di backend PHP per l'invio tempestivo di messaggi al client

Principio:

  1. Stabilire un Worker Websocket, per mantenere una connessione persistente con il client.

  2. Creare un Worker di tipo text all'interno del Worker Websocket.

  3. Il Worker Websocket e il Worker di tipo text sono nello stesso processo, il che facilita la condivisione delle connessioni con il client.

  4. Un sistema backend PHP indipendente comunica con il Worker di tipo text tramite il protocollo di tipo text.

  5. Il Worker di tipo text gestisce la connessione Websocket per completare l'invio dei dati.

Codice e passaggi

push.php

<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// Inizializza un container worker, ascoltando la porta 1234
$worker = new Worker('websocket://0.0.0.0:1234');

/*
 * Attenzione, il numero di processi deve essere impostato a 1
 */
$worker->count = 1;
// Dopo l'avvio del processo worker, crea un Worker di tipo text per aprire una porta di comunicazione interna
$worker->onWorkerStart = function($worker)
{
    // Apri una porta interna, per facilitare il sistema interno nell'invio di dati, formato del protocollo Text: testo + carattere di nuova linea
    $inner_text_worker = new Worker('text://0.0.0.0:5678');
    $inner_text_worker->onMessage = function(TcpConnection $connection, $buffer)
    {
        // La struttura dell'array $data, contiene uid, che indica a quale uid inviare dati
        $data = json_decode($buffer, true);
        $uid = $data['uid'];
        // Invia dati alla pagina dell'uid tramite workerman
        $ret = sendMessageByUid($uid, $buffer);
        // Restituisci il risultato dell'invio
        $connection->send($ret ? 'ok' : 'fail');
    };
    // ## Esegui l'ascolto ##
    $inner_text_worker->listen();
};
// Aggiungi una nuova proprietà per salvare la mappatura uid-connection
$worker->uidConnections = array();
// Callback da eseguire quando un client invia un messaggio
$worker->onMessage = function(TcpConnection $connection, $data)
{
    global $worker;
    // Controlla se il client corrente è già autenticato, ossia se ha un uid impostato
    if(!isset($connection->uid))
    {
       // Se non autenticato, considera il primo pacchetto come uid (qui per semplicità, non abbiamo implementato una vera autenticazione)
       $connection->uid = $data;
       /* Salva la mappatura uid-connection, in modo da poter ricercare facilmente la connection tramite uid,
        * implementando l'invio di dati specifici per uid
        */
       $worker->uidConnections[$connection->uid] = $connection;
       return;
    }
};

// Quando un client si disconnette
$worker->onClose = function(TcpConnection $connection)
{
    global $worker;
    if(isset($connection->uid))
    {
        // Rimuovi la mappatura alla disconnessione
        unset($worker->uidConnections[$connection->uid]);
    }
};

// Invio dati a tutti gli utenti autenticati
function broadcast($message)
{
   global $worker;
   foreach($worker->uidConnections as $connection)
   {
        $connection->send($message);
   }
}

// Invio dati specifici per uid
function sendMessageByUid($uid, $message)
{
    global $worker;
    if(isset($worker->uidConnections[$uid]))
    {
        $connection = $worker->uidConnections[$uid];
        $connection->send($message);
        return true;
    }
    return false;
}

// Esegui tutti i worker
Worker::runAll();

Avvia il servizio backend
php push.php start -d

Codice js per ricevere i dati al frontend

var ws = new WebSocket('ws://127.0.0.1:1234');
ws.onopen = function(){
    var uid = 'uid1';
    ws.send(uid);
};
ws.onmessage = function(e){
    alert(e.data);
};

Codice backend per l'invio di messaggi

// Stabilire una connessione socket alla porta interna di invio
$client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
// Dati da inviare, contenente il campo uid, indica che devono essere inviati a questo uid
$data = array('uid'=>'uid1', 'percent'=>'88%');
// Invia dati, nota che la porta 5678 è per il protocollo Text, il protocollo Text richiede di aggiungere un carattere di nuova linea alla fine dei dati
fwrite($client, json_encode($data)."\n");
// Leggi il risultato dell'invio
echo fread($client, 8192);