Quelques exemples

Exemple un

Définition du protocole

  • L'en-tête fixe de 10 octets de longueur pour stocker la longueur totale du paquet, complétée par des 0 si nécessaire
  • Le format de données est xml

Exemple de paquet de données

0000000121<?xml version="1.0" encoding="ISO-8859-1"?>
<request>
    <module>user</module>
    <action>getInfo</action>
</request>

où 0000000121 représente la longueur totale du paquet, immédiatement suivi du contenu du corps de paquet au format xml

Mise en œuvre du protocole

namespace Protocols;
class XmlProtocol
{
    public static function input($recv_buffer)
    {
        if(strlen($recv_buffer) < 10)
        {
            // Pas assez de 10 octets, retourne 0 et continue à attendre des données
            return 0;
        }
        // Retourne la longueur du paquet, la longueur du paquet inclut la longueur des données d'en-tête + la longueur du corps
        $total_len = base_convert(substr($recv_buffer, 0, 10), 10, 10);
        return $total_len;
    }

    public static function decode($recv_buffer)
    {
        // Corps de la requête
        $body = substr($recv_buffer, 10);
        return simplexml_load_string($body);
    }

    public static function encode($xml_string)
    {
        // Longueur du corps + longueur d'en-tête
        $total_length = strlen($xml_string)+10;
        // Partie longueur complétée par 10 octets, complétée par des 0 si nécessaire
        $total_length_str = str_pad($total_length, 10, '0', STR_PAD_LEFT);
        // Retourne les données
        return $total_length_str . $xml_string;
    }
}

Exemple deux

Définition du protocole

  • En-tête de 4 octets en ordre d'octets réseau unsigned int, indiquant la longueur totale du paquet
  • La partie données est une chaîne Json

Exemple de paquet de données

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

où l'en-tête de quatre octets * représente une donnée unsigned int en ordre d'octets réseau, qu'il est impossible de voir, immédiatement suivi par les données au format Json

Mise en œuvre du protocole

namespace Protocols;
class JsonInt
{
    public static function input($recv_buffer)
    {
        // Les données reçues ne contiennent pas encore 4 octets, impossible de savoir la longueur du paquet, retourne 0 et continue à attendre des données
        if(strlen($recv_buffer)<4)
        {
            return 0;
        }
        // Utilise la fonction unpack pour convertir les 4 octets de l'en-tête en nombre, les 4 octets de l'en-tête étant la longueur totale du paquet
        $unpack_data = unpack('Ntotal_length', $recv_buffer);
        return $unpack_data['total_length'];
    }

    public static function decode($recv_buffer)
    {
        // Enlève les 4 octets de l'en-tête, obtient les données Json du corps
        $body_json_str = substr($recv_buffer, 4);
        // Décodage json
        return json_decode($body_json_str, true);
    }

    public static function encode($data)
    {
        // Encodage Json pour obtenir le corps
        $body_json_str = json_encode($data);
        // Calcule la longueur totale du paquet, 4 octets d'en-tête + longueur du corps
        $total_length = 4 + strlen($body_json_str);
        // Retourne les données empaquetées
        return pack('N',$total_length) . $body_json_str;
    }
}

Exemple trois (utilisant le protocole binaire pour télécharger des fichiers)

Définition du protocole

struct
{
  unsigned int total_len;  // Longueur totale du paquet, ordre d'octets réseau
  char         name_len;   // Longueur du nom de fichier
  char         name[name_len]; // Nom du fichier
  char         file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]; // Données du fichier
}

Exemple de protocole

 *****logo.png****************** 

où l'en-tête de quatre octets représente une donnée unsigned int en ordre d'octets réseau, qu'il est impossible de voir, le cinquième est un octet stockant la longueur du nom de fichier, suivi immédiatement par le nom du fichier, puis les données binaires du fichier d'origine

Mise en œuvre du protocole

namespace Protocols;
class BinaryTransfer
{
    // Longueur d'en-tête de protocole
    const PACKAGE_HEAD_LEN = 5;

    public static function input($recv_buffer)
    {
        // Si la longueur du protocole n'est pas suffisante, continue d'attendre
        if(strlen($recv_buffer) < self::PACKAGE_HEAD_LEN)
        {
            return 0;
        }
        // Dépacke
        $package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
        // Retourne la longueur du paquet
        return $package_data['total_len'];
    }

    public static function decode($recv_buffer)
    {
        // Dépacke
        $package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
        // Longueur du nom de fichier
        $name_len = $package_data['name_len'];
        // Extrait le nom de fichier du flux de données
        $file_name = substr($recv_buffer, self::PACKAGE_HEAD_LEN, $name_len);
        // Extrait les données binaires du fichier du flux de données
        $file_data = substr($recv_buffer, self::PACKAGE_HEAD_LEN + $name_len);
         return array(
             'file_name' => $file_name,
             'file_data' => $file_data,
         );
    }

    public static function encode($data)
    {
        // Vous pouvez encoder les données à envoyer au client selon vos besoins, ici cela renvoie simplement les textes tels quels
        return $data;
    }
}

Exemple d'utilisation du protocole sur le serveur

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

$worker = new Worker('BinaryTransfer://0.0.0.0:8333');
// Sauvegarde le fichier dans tmp
$worker->onMessage = function(TcpConnection $connection, $data)
{
    $save_path = '/tmp/'.$data['file_name'];
    file_put_contents($save_path, $data['file_data']);
    $connection->send("upload success. save path $save_path");
};

Worker::runAll();

Fichier client client.php (ici en utilisant php pour simuler le téléchargement client)

<?php
/** Client de téléchargement de fichiers **/
// Adresse de téléchargement
$address = "127.0.0.1:8333";
// Vérifie le paramètre du chemin de téléchargement de fichier
if(!isset($argv[1]))
{
   exit("use php client.php \$file_path\n");
}
// Chemin du fichier à télécharger
$file_to_transfer = trim($argv[1]);
// Le fichier à télécharger n'existe pas localement
if(!is_file($file_to_transfer))
{
    exit("$file_to_transfer not exist\n");
}
// Établit une connexion socket
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
    exit("$errmsg\n");
}
// Met en mode bloquant
stream_set_blocking($client, 1);
// Nom de fichier
$file_name = basename($file_to_transfer);
// Longueur du nom de fichier
$name_len = strlen($file_name);
// Données binaires du fichier
$file_data = file_get_contents($file_to_transfer);
// Longueur d'en-tête de protocole 4 octets longueur du paquet + 1 octet longueur du nom de fichier
$PACKAGE_HEAD_LEN = 5;
// Paquet protocole
$package = pack('NC', $PACKAGE_HEAD_LEN  + strlen($file_name) + strlen($file_data), $name_len) . $file_name . $file_data;
// Exécute le téléchargement
fwrite($client, $package);
// Imprime le résultat
echo fread($client, 8192),"\n";

Exemple d'utilisation du client

Exécutez la commande php client.php <chemin_fichier>

Par exemple php client.php abc.jpg

Exemple quatre (utilisant le protocole texte pour télécharger des fichiers)

Définition du protocole

json+retour à la ligne, json contenant le nom de fichier ainsi que les données de fichier encodées en base64 (ce qui augmentera le volume d'1/3)

Exemple de protocole

{"file_name":"logo.png","file_data":"PD9waHAKLyo......"}\n

Remarque : se termine par un caractère de retour à la ligne, signalé en PHP par le caractère double quote "\n"

Mise en œuvre du protocole

namespace Protocols;
class TextTransfer
{
    public static function input($recv_buffer)
    {
        $recv_len = strlen($recv_buffer);
        if($recv_buffer[$recv_len-1] !== "\n")
        {
            return 0;
        }
        return strlen($recv_buffer);
    }

    public static function decode($recv_buffer)
    {
        // Dépacke
        $package_data = json_decode(trim($recv_buffer), true);
        // Prend le nom de fichier
        $file_name = $package_data['file_name'];
        // Prend les données de fichier après encodage en base64
        $file_data = $package_data['file_data'];
        // Décode base64 pour restaurer les données de fichier binaires d'origine
        $file_data = base64_decode($file_data);
        // Retourne les données
        return array(
             'file_name' => $file_name,
             'file_data' => $file_data,
         );
    }

    public static function encode($data)
    {
        // Vous pouvez encoder les données à envoyer au client selon vos besoins, ici cela renvoie simplement les textes tels quels
        return $data;
    }
}

Exemple d'utilisation du protocole sur le serveur

Remarque : l'écriture est similaire à celle du téléchargement binaire, c'est-à-dire qu'il est possible de passer au protocole presque sans changer aucun code métier

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

$worker = new Worker('TextTransfer://0.0.0.0:8333');
// Sauvegarde le fichier dans tmp
$worker->onMessage = function(TcpConnection $connection, $data)
{
    $save_path = '/tmp/'.$data['file_name'];
    file_put_contents($save_path, $data['file_data']);
    $connection->send("upload success. save path $save_path");
};

Worker::runAll();

Fichier client textclient.php (ici en utilisant php pour simuler le téléchargement client)

<?php
/** Client de téléchargement de fichiers **/
// Adresse de téléchargement
$address = "127.0.0.1:8333";
// Vérifie le paramètre du chemin de téléchargement de fichier
if(!isset($argv[1]))
{
   exit("use php client.php \$file_path\n");
}
// Chemin du fichier à télécharger
$file_to_transfer = trim($argv[1]);
// Le fichier à télécharger n'existe pas localement
if(!is_file($file_to_transfer))
{
    exit("$file_to_transfer not exist\n");
}
// Établit une connexion socket
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
    exit("$errmsg\n");
}
stream_set_blocking($client, 1);
// Nom de fichier
$file_name = basename($file_to_transfer);
// Données binaires du fichier
$file_data = file_get_contents($file_to_transfer);
// encodage base64
$file_data = base64_encode($file_data);
// Données du paquet
$package_data = array(
    'file_name' => $file_name,
    'file_data' => $file_data,
);
// Paquet protocole json + saut de ligne
$package = json_encode($package_data)."\n";
// Exécute le téléchargement
fwrite($client, $package);
// Imprime le résultat
echo fread($client, 8192),"\n";

Exemple d'utilisation du client

Exécutez la commande php textclient.php <chemin_fichier>

Par exemple php textclient.php abc.jpg