Come personalizzare il protocollo

In realtà, creare il proprio protocollo è un'operazione relativamente semplice. Un protocollo semplice di solito comprende due parti:

  • Un identificatore per distinguere i confini dei dati
  • Una definizione del formato dei dati

Un esempio

Definizione del protocollo

Qui assumiamo che l'identificatore per distinguere i confini dei dati sia il carattere di nuova riga "\n" (nota che i dati della richiesta stessi non possono contenere il carattere di nuova riga), e il formato dei dati è JSON; ad esempio, il seguente è un pacchetto di richiesta che soddisfa questa regola.

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

Nota che alla fine dei dati della richiesta sopra c'è un carattere di nuova riga (in PHP rappresentato dalla stringa doppio apice "\n"), che indica la fine di una richiesta.

Passaggi di implementazione

In Workerman, per implementare il protocollo sopra, supponiamo che il nome del protocollo sia JsonNL e il progetto sia MyApp, quindi sono necessari i seguenti passaggi:

  1. Mettere il file di protocollo nella cartella Protocols del progetto, ad esempio il file MyApp/Protocols/JsonNL.php.

  2. Implementare la classe JsonNL utilizzando namespace Protocols; come spazio dei nomi, deve implementare tre metodi statici chiamati rispettivamente input, encode e decode.

Nota: workerman chiamerà automaticamente questi tre metodi statici per gestire la suddivisione dei pacchetti, il disimballaggio e l'imballaggio. Per il flusso specifico, vedere la seguente descrizione del flusso di esecuzione.

Flusso di interazione tra workerman e la classe del protocollo

  1. Supponiamo che un client invii un pacchetto di dati al server; una volta che il server riceve i dati (che potrebbero essere dati parziali), chiamerà immediatamente il metodo input del protocollo per controllare la lunghezza di questo pacchetto. Il metodo input restituisce un valore di lunghezza $length al framework workerman.
  2. Dopo che il framework workerman riceve questo valore $length, verifica se nel buffer dei dati attuale sono già stati ricevuti dati di lunghezza $length. Se non ci sono, continuerà ad attendere i dati fino a quando la lunghezza dei dati nel buffer non è maggiore o uguale a $length.
  3. Una volta che la lunghezza dei dati nel buffer è sufficiente, workerman estrarrà dal buffer i dati di lunghezza $length (cioè suddividerà il pacchetto) e chiamerà il metodo decode del protocollo per disimballare, i dati disimballati saranno $data.
  4. Dopo il disimballaggio, workerman passerà i dati $data alla business logic tramite il callback onMessage($connection, $data), così la logica di business potrà utilizzare la variabile $data per ottenere i dati completi e disimballati inviati dal client.
  5. Quando la logica di business in onMessage ha bisogno di inviare dati al client attraverso il metodo $connection->send($buffer), workerman utilizzerà automaticamente il metodo encode del protocollo per imballare nuovamente $buffer e poi inviarlo al client.

Implementazione specifica

Implementazione di MyApp/Protocols/JsonNL.php

namespace Protocols;
class JsonNL
{
    /**
     * Controlla l'integrità del pacchetto
     * Se è possibile ottenere la lunghezza del pacchetto, restituisce la lunghezza del pacchetto nel buffer, altrimenti restituisce 0 per continuare ad attendere dati
     * Se ci sono problemi con il protocollo, può restituire -1, il che porterà alla disconnessione del client attuale
     * @param string $buffer
     * @return int
     */
    public static function input($buffer)
    {
        // Ottieni la posizione del carattere di nuova riga "\n"
        $pos = strpos($buffer, "\n");
        // Nessun carattere di nuova riga, non è possibile determinare la lunghezza del pacchetto, restituisce 0 per continuare ad attendere dati
        if($pos === false)
        {
            return 0;
        }
        // C'è un carattere di nuova riga, restituisce la lunghezza del pacchetto attuale (incluso il carattere di nuova riga)
        return $pos+1;
    }

    /**
     * Imballa, verrà chiamato automaticamente quando si inviano dati al client
     * @param string $buffer
     * @return string
     */
    public static function encode($buffer)
    {
        // Serializzazione json, e aggiunge un carattere di nuova riga come marcatore di fine richiesta
        return json_encode($buffer)."\n";
    }

    /**
     * Disimballa, verrà chiamato automaticamente quando il numero di byte dei dati ricevuti è uguale al valore restituito da input (un valore maggiore di 0)
     * E passato come parametro $data alla funzione di callback onMessage
     * @param string $buffer
     * @return string
     */
    public static function decode($buffer)
    {
        // Rimuove la nuova riga e ripristina in un array
        return json_decode(trim($buffer), true);
    }
}

Fino a questo punto, l'implementazione del protocollo JsonNL è completata e può essere utilizzata nel progetto MyApp, come negli esempi seguenti:

File: 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 sono i dati inviati dal client, i dati sono stati elaborati tramite JsonNL::decode
    echo $data;

    // Il metodo $connection->send invierà automaticamente i dati imballati tramite JsonNL::encode
    $connection->send(array('code'=>0, 'msg'=>'ok'));

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

Suggerimento
Workerman tenterà di caricare i protocolli sotto lo spazio dei nomi Protocols, ad esempio new Worker('JsonNL://0.0.0.0:1234') tenterà di caricare il protocollo Protocols\JsonNL.
Se si verifica l'errore Class 'Protocols\JsonNL' not found, consultare Autoload per implementare il caricamento automatico.

Descrizione dell'interfaccia del protocollo

Le classi di protocollo sviluppate in Workerman devono implementare tre metodi statici: input, encode e decode. La descrizione dell'interfaccia del protocollo è come segue, e si trova in Workerman/Protocols/ProtocolInterface.php:

namespace Workerman\Protocols;

use \Workerman\Connection\ConnectionInterface;

/**
 * Interfaccia del protocollo
 * @author walkor <walkor@workerman.net>
 */
interface ProtocolInterface
{
    /**
     * Utilizzato per suddividere i pacchetti nel recv_buffer ricevuto
     *
     * Se è possibile ottenere la lunghezza del pacchetto dalla $recv_buffer, restituisce l'intera lunghezza del pacchetto
     * Altrimenti restituisce 0, indicando che sono necessari più dati per ottenere la lunghezza del pacchetto della richiesta corrente
     * Se restituisce -1, significa che la richiesta è errata e la connessione verrà interrotta
     *
     * @param ConnectionInterface $connection
     * @param string $recv_buffer
     * @return int
     */
    public static function input($recv_buffer, ConnectionInterface $connection);

    /**
     * Utilizzato per disimballare la richiesta
     *
     * Quando il valore restituito da input è maggiore di 0 e Workerman ha ricevuto dati sufficienti, verrà automaticamente chiamato decode
     * Quindi attiverà la callback onMessage e passerà i dati decodificati a onMessage come secondo parametro
     * In altre parole, quando si riceve una richiesta completa dal client, verrà automaticamente chiamato decode senza bisogno di chiamate manuali nel codice di business
     * @param ConnectionInterface $connection
     * @param string $recv_buffer
     */
    public static function decode($recv_buffer, ConnectionInterface $connection);

    /**
     * Utilizzato per imballare la richiesta
     *
     * Quando è necessario inviare dati al client e si chiama $connection->send($data); 
     * Verrà automaticamente imballato $data utilizzando encode, trasformandolo nel formato dei dati conforme al protocollo e poi inviato al client
     * In altre parole, i dati inviati al client verranno automaticamente imballati tramite encode senza necessità di chiamate manuali nel codice di business
     * @param ConnectionInterface $connection
     * @param mixed $data
     */
    public static function encode($data, ConnectionInterface $connection);
}

Nota:

In Workerman non è strettamente richiesto che le classi di protocollo implementino ProtocolInterface, in realtà, qualsiasi classe di protocollo che contenga i tre metodi statici input, encode e decode è sufficiente.