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