Wie man ein Protokoll anpasst

Tatsächlich ist es recht einfach, ein eigenes Protokoll zu erstellen. Ein einfaches Protokoll besteht normalerweise aus zwei Teilen:

  • Einem Identifikator zum Trennen der Datengrenzen
  • Einer Definition des Datenformats

Ein Beispiel

Protokolldefinition

Hier nehmen wir an, dass der Identifikator zum Trennen der Daten grenz ein Zeilenumbruch "\n" ist (Hinweis: Die Anfragedaten selbst dürfen keinen Zeilenumbruch enthalten), das Datenformat ist JSON, wie untenstehend in einem gültigen Anfragepaket dargestellt.

{"type":"message","content":"hello"}

Beachten Sie, dass am Ende der obigen Anfragedaten ein Zeilenumbruch (darstellt durch doppelte Anführungszeichen-Zeichen "\n" in PHP) steht, der das Ende einer Anfrage zeigt.

Implementierungsschritte

Um das oben genannte Protokoll in Workerman zu implementieren, nehmen wir an, dass der Protokollname JsonNL ist und sich das Projekt MyApp nennt. Folgende Schritte sind erforderlich:

  1. Die Protokolldatei wird in den Ordner Protocols des Projekts gelegt, zum Beispiel die Datei MyApp/Protocols/JsonNL.php.

  2. Implementieren Sie die JsonNL-Klasse mit namespace Protocols; als Namensraum und müssen drei statische Methoden implementieren: input, encode, decode.

Hinweis: Workerman wird diese drei statischen Methoden automatisch aufrufen, um das Verpacken, Entpacken und die Paketierung durchzuführen. Der genaue Ablauf ist unten in der Ausführungsbeschreibung dargestellt.

Interaktionsablauf zwischen Workerman und der Protokollklasse

  1. Angenommen, der Client sendet ein Datenpaket an den Server. Der Server ruft sofort nach Erhalt der Daten (möglicherweise nur Teile davon) die input-Methode des Protokolls auf, um die Länge dieses Pakets zu überprüfen. Die input-Methode gibt den Längenwert $length an das Workerman-Framework zurück.
  2. Das Workerman-Framework prüft bei Erhalt des $length-Werts, ob die aktuelle Datenpuffergröße bereits die Länge $length der Daten empfangen hat. Falls nicht, wartet es weiterhin auf Daten, bis die Datenlänge im Puffer nicht kleiner ist als $length.
  3. Wenn die Datenlänge im Puffer ausreichend ist, wird Workerman die $length-Länge der Daten aus dem Puffer ausschneiden (d.h. Paket trennen) und die decode-Methode des Protokolls aufrufen, um das Paket zu entpacken. Die entpackten Daten sind in $data.
  4. Nach dem Entpacken gibt Workerman die Daten $data in Form eines Callback onMessage($connection, $data) an die Geschäftslogik weiter. Die Geschäftslogik kann innerhalb von onMessage die Variable $data verwenden, um die vollständigen und bereits entpackten Daten vom Client zu erhalten.
  5. Wenn die Geschäftslogik innerhalb von onMessage die Methode $connection->send($buffer) aufruft, um Daten an den Client zu senden, wird Workerman automatisch die encode-Methode des Protokolls aufrufen, um das $buffer zu verpacken und es dann an den Client zu senden.

Konkrete Implementierung

Implementierung von MyApp/Protocols/JsonNL.php

namespace Protocols;
class JsonNL
{
    /**
     * Überprüft die Integrität des Pakets
     * Wenn die Paketlänge ermittelt werden kann, wird die Länge des Pakets im Puffer zurückgegeben; 
     * andernfalls wird 0 zurückgegeben, um auf Daten zu warten.
     * Wenn es ein Problem mit dem Protokoll gibt, kann -1 zurückgegeben werden, wodurch die 
     * aktuelle Client-Verbindung getrennt wird.
     * @param string $buffer
     * @return int
     */
    public static function input($buffer)
    {
        // Position des Zeilenumbruchs "\n" erhalten
        $pos = strpos($buffer, "\n");
        // Kein Zeilenumbruch, kann die Paketlänge nicht ermitteln, gibt 0 zurück und wartet auf Daten
        if($pos === false)
        {
            return 0;
        }
        // Zeilenumbruch vorhanden, gibt die aktuelle Paketlänge (einschließlich Zeilenumbruch) zurück
        return $pos+1;
    }

    /**
     * Verpackt die Daten, wird automatisch aufgerufen, wenn Daten an den Client gesendet werden.
     * @param string $buffer
     * @return string
     */
    public static function encode($buffer)
    {
        // json serialisieren und mit Zeilenumbruch als Endemarkierung der Anfrage versehen
        return json_encode($buffer)."\n";
    }

    /**
     * Entpackt das Paket, wenn die Anzahl der empfangenen Bytes 
     * der von input zurückgegebenen Länge (größer als 0) entspricht
     * und übergibt es als $data Parameter an die onMessage Callback-Funktion.
     * @param string $buffer
     * @return string
     */
    public static function decode($buffer)
    {
        // Entfernt den Zeilenumbruch, stellt es als Array wieder her
        return json_decode(trim($buffer), true);
    }
}

Damit ist das JsonNL-Protokoll implementiert und kann im MyApp-Projekt verwendet werden. Die Verwendung erfolgt wie folgt:

Datei: MyApp\start.php

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

$json_worker = new Worker('JsonNL://0.0.0.0:1234');
$json_worker->onMessage = function(TcpConnection $connection, $data) {

    // $data sind die vom Client gesendeten Daten, die bereits mit JsonNL::decode verarbeitet wurden
    echo $data;

    // Mit $connection->send gesendete Daten rufen automatisch die JsonNL::encode-Methode auf, um 
    // diese zu verpacken, bevor sie an den Client gesendet werden.
    $connection->send(array('code'=>0, 'msg'=>'ok'));

};
Worker::runAll();
...

Hinweis
Workerman versucht, Protokolle im Namensraum Protocols zu laden, z.B. new Worker('JsonNL://0.0.0.0:1234') versucht, das Protokoll Protocols\JsonNL zu laden.
Wenn der Fehler Class 'Protocols\JsonNL' not found auftritt, lesen Sie Automatisches Laden zur Implementierung des automatischen Ladens.

Protokoll-Schnittstellenerklärung

Protokollklassen, die in Workerman entwickelt wurden, müssen drei statische Methoden implementieren: input, encode, decode. Die spezifische Protokollschnittstellenerklärung finden Sie in Workerman/Protocols/ProtocolInterface.php, wie folgt definiert:

namespace Workerman\Protocols;

use \Workerman\Connection\ConnectionInterface;

/**
 * Protokollschnittstelle
* @author walkor <walkor@workerman.net>
 */
interface ProtocolInterface
{
    /**
     * Zum Trennen von Paketen im empfangenen recv_buffer
     *
     * Wenn die Länge des Anfragepakets im $recv_buffer ermittelt werden kann, geben Sie 
     * die gesamte Paketlänge zurück. Andernfalls gibt 0 zurück, was bedeutet, dass 
     * mehr Daten benötigt werden, um die aktuelle Paketlänge zu ermitteln.
     * Wenn -1 zurückgegeben wird, bedeutet dies, dass es sich um eine fehlerhafte Anfrage handelt, 
     * und die Verbindung wird getrennt.
     *
     * @param ConnectionInterface $connection
     * @param string $recv_buffer
     * @return int
     */
    public static function input($recv_buffer, ConnectionInterface $connection);

    /**
     * Zum Entpacken von Anforderungen
     *
     * Wenn der Rückgabewert von input größer als 0 ist und Workerman genügend Daten empfangen hat, 
     * wird decode automatisch aufgerufen und löst dann den Callback onMessage aus, 
     * wobei die aus decode entpackten Daten als zweiten Parameter an die onMessage-Callback- 
     * Funktion übergeben werden.
     * Das bedeutet, dass wenn eine vollständige Client-Anfrage empfangen wird, decode 
     * automatisch aufgerufen wird, ohne dass in Geschäftslogik eine manuelle Aufruf erforderlich ist.
     * @param ConnectionInterface $connection
     * @param string $recv_buffer
     */
    public static function decode($recv_buffer, ConnectionInterface $connection);

    /**
     * Zum Verpacken von Anforderungen
     *
     * Wenn Daten an den Client gesendet werden sollen, dh beim Aufruf von $connection->send($data);
     * wird automatisch $data einmal mit encode verpackt, sodass es dem Protokollformat entspricht, 
     * bevor es an den Client gesendet wird.
     * Das bedeutet, dass die an den Client gesendeten Daten automatisch mit encode verpackt werden, 
     * ohne dass in der Geschäftslogik manuelle Aufrufe erforderlich sind.
     * @param ConnectionInterface $connection
     * @param mixed $data
     */
    public static function encode($data, ConnectionInterface $connection);
}

Hinweis:

In Workerman besteht nicht die strenge Anforderung, dass Protokollklassen auf der ProtocolInterface-Schnittstelle basieren müssen. Tatsächlich reicht es aus, wenn die Klasse die drei statischen Methoden input, encode und decode enthält.