hören

void Worker::listen(void)

Verwendet, um nach der Instanziierung des Workers zu lauschen.

Diese Methode wird hauptsächlich verwendet, um nach dem Start des Worker-Prozesses dynamisch neue Worker-Instanzen zu erstellen. Damit kann ein einzelner Prozess mehrere Ports überwachen und verschiedene Protokolle unterstützen. Es ist wichtig zu beachten, dass diese Methode lediglich das Hören im aktuellen Prozess hinzufügt und keinen neuen Prozess dynamisch erstellt, noch die onWorkerStart-Methode auslöst.

Zum Beispiel kann ein http Worker, der gestartet wird, nach der Instanziierung eines websocket Workers sowohl über das http-Protokoll als auch über das websocket-Protokoll erreicht werden. Da sich der websocket Worker und der http Worker im selben Prozess befinden, können sie auf gemeinsame Speichervariablen zugreifen und alle Socketverbindungen teilen. Dies ermöglicht es, http-Anfragen zu empfangen und dann den websocket-Client zu verwenden, um Daten an den Client zu pushen.

Hinweis:

Wenn die PHP-Version <= 7.0 ist, wird die Instanziierung desselben Ports in mehreren Unterprozessen nicht unterstützt. Wenn z.B. der Prozess A einen Worker erstellt, der den Port 2016 überwacht, kann der Prozess B keinen weiteren Worker erstellen, der den Port 2016 überwacht, andernfalls tritt der Fehler Address already in use auf. Beispielsweise ist der folgende Code nicht ausführbar.

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

$worker = new Worker();
// 4 Prozesse
$worker->count = 4;
// Jeder Prozess erstellt nach dem Start einen neuen Worker, der die aktuellen Prozesse überwacht
$worker->onWorkerStart = function($worker)
{
    /**
     * Wenn alle 4 Prozesse gestartet werden, wird ein Worker erstellt, der Port 2016 überwacht.
     * Wenn worker->listen() aufgerufen wird, tritt der Fehler Address already in use auf.
     * Wenn worker->count=1, tritt kein Fehler auf.
     */
    $inner_worker = new Worker('http://0.0.0.0:2016');
    $inner_worker->onMessage = 'on_message';
    // Lauschen ausführen. Hier tritt der Fehler Address already in use auf.
    $inner_worker->listen();
};

$worker->onMessage = 'on_message';

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

// worker ausführen
Worker::runAll();

Wenn Ihre PHP-Version >= 7.0 ist, können Sie Worker->reusePort=true einstellen, wodurch mehrere Unterprozesse identische Worker auf demselben Port erstellen können. Siehe folgendes Beispiel:

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

$worker = new Worker('text://0.0.0.0:2015');
// 4 Prozesse
$worker->count = 4;
// Jeder Prozess erstellt nach dem Start einen neuen Worker, der die aktuellen Prozesse überwacht
$worker->onWorkerStart = function($worker)
{
    $inner_worker = new Worker('http://0.0.0.0:2016');
    // Port-Wiederverwendung aktivieren, um Worker zu erstellen, die denselben Port überwachen (benötigt PHP >= 7.0)
    $inner_worker->reusePort = true;
    $inner_worker->onMessage = 'on_message';
    // Lauschen ausführen. Normale Überwachung erzeugt keinen Fehler.
    $inner_worker->listen();
};

$worker->onMessage = 'on_message';

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

// worker ausführen
Worker::runAll();

Beispiel für PHP-Backend zur sofortigen Nachrichtenübertragung an Clients

Prinzip:

  1. Ein websocket Worker wird eingerichtet, um eine dauerhafte Verbindung zu den Clients aufrechtzuerhalten.

  2. Innerhalb des websocket Workers wird ein text Worker erstellt.

  3. Der websocket Worker und der text Worker befinden sich im selben Prozess, wodurch die Client-Verbindungen leicht geteilt werden können.

  4. Ein separates PHP-Backend-System kommuniziert über das Text-Protokoll mit dem text Worker.

  5. Der text Worker nutzt die websocket-Verbindung, um Daten zu pushen.

Code und Schritte

push.php

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

// Initialisieren eines Worker-Containers zur Überwachung von Port 1234
$worker = new Worker('websocket://0.0.0.0:1234');

/*
 * Hinweis: Die Anzahl der Prozesse muss hier auf 1 gesetzt werden.
 */
$worker->count = 1;
// Nach dem Start des Worker-Prozesses wird ein text Worker erstellt, um einen internen Kommunikationsport zu öffnen.
$worker->onWorkerStart = function($worker)
{
    // Erstellen eines internen Ports zum einfachen Pushen von Daten durch das interne System. Textprotokollformat: Text + Zeilenumbruch.
    $inner_text_worker = new Worker('text://0.0.0.0:5678');
    $inner_text_worker->onMessage = function(TcpConnection $connection, $buffer)
    {
        // Das Datenarray-Format, das uid enthält, um zu bestimmen, an welche uid-Seite die Daten gesendet werden.
        $data = json_decode($buffer, true);
        $uid = $data['uid'];
        // Pushen von Daten an die uid-Seite über Workerman.
        $ret = sendMessageByUid($uid, $buffer);
        // Rückgabe des Push-Ergebnisses.
        $connection->send($ret ? 'ok' : 'fail');
    };
    // ## Lauschen ausführen ##
    $inner_text_worker->listen();
};
// Hinzufügen einer neuen Eigenschaft zur Speicherung der uid-zu-Verbindung-Zuordnung.
$worker->uidConnections = array();
// Callback-Funktion, die ausgeführt wird, wenn ein Client eine Nachricht sendet.
$worker->onMessage = function(TcpConnection $connection, $data)
{
    global $worker;
    // Überprüfen, ob der aktuelle Client bereits authentifiziert ist, d.h. ob uid gesetzt wurde.
    if(!isset($connection->uid))
    {
       // Wenn nicht authentifiziert, das erste Paket als uid behandeln (hier zur Vereinfachung des Demonstrationszwecks nicht wirklich authentifiziert).
       $connection->uid = $data;
       /* Speichert die Zuordnung von uid zur Verbindung, sodass sie einfach über uid gefunden werden kann,
        * um gezielte Daten zu pushen.
        */
       $worker->uidConnections[$connection->uid] = $connection;
       return;
    }
};

// Wenn eine Client-Verbindung getrennt wird.
$worker->onClose = function(TcpConnection $connection)
{
    global $worker;
    if(isset($connection->uid))
    {
        // Mapping beim Trennen der Verbindung löschen.
        unset($worker->uidConnections[$connection->uid]);
    }
};

// Daten an alle authentifizierten Nutzer pushen.
function broadcast($message)
{
   global $worker;
   foreach($worker->uidConnections as $connection)
   {
        $connection->send($message);
   }
}

// Daten an eine bestimmte uid pushen.
function sendMessageByUid($uid, $message)
{
    global $worker;
    if(isset($worker->uidConnections[$uid]))
    {
        $connection = $worker->uidConnections[$uid];
        $connection->send($message);
        return true;
    }
    return false;
}

// Alle Worker ausführen.
Worker::runAll();

Backend-Service starten
php push.php start -d

JavaScript-Code zum Empfangen von Push-Nachrichten auf der Client-Seite

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

Code zum Pushen von Nachrichten vom Backend

// Eine Socket-Verbindung zum internen Push-Port aufbauen.
$client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
// Die zu pushenden Daten, die das uid-Feld enthalten, um zu kennzeichnen, dass es an diese uid gepusht wird.
$data = array('uid'=>'uid1', 'percent'=>'88%');
// Daten senden, beachten Sie, dass der Port 5678 der Textprotokoll-Port ist. Textprotokoll benötigt einen Zeilenumbruch am Ende der Daten.
fwrite($client, json_encode($data)."\n");
// Push-Ergebnis lesen.
echo fread($client, 8192);