Alcuni esempi
Esempio uno
Definizione del protocollo
- Un'intestazione fissa di 10 byte per salvare la lunghezza dell'intero pacchetto, con zeri aggiunti se la lunghezza è inferiore a 10 byte
- Il formato dei dati è XML
Esempio di pacchetto
0000000121<?xml version="1.0" encoding="ISO-8859-1"?>
<request>
<module>user</module>
<action>getInfo</action>
</request>
Dove 0000000121 rappresenta la lunghezza dell'intero pacchetto, seguito dal contenuto del pacchetto in formato XML
Implementazione del protocollo
namespace Protocols;
class XmlProtocol
{
public static function input($recv_buffer)
{
if(strlen($recv_buffer) < 10)
{
// Se la lunghezza è inferiore a 10 byte, restituisci 0 e aspetta ulteriori dati
return 0;
}
// Restituisci la lunghezza del pacchetto, comprensiva della lunghezza dell'intestazione e del corpo del pacchetto
$total_len = base_convert(substr($recv_buffer, 0, 10), 10, 10);
return $total_len;
}
public static function decode($recv_buffer)
{
// Corpo del pacchetto
$body = substr($recv_buffer, 10);
return simplexml_load_string($body);
}
public static function encode($xml_string)
{
// Lunghezza totale del pacchetto (lunghezza del corpo + 10 per l'intestazione)
$total_length = strlen($xml_string) + 10;
// Aggiungi zeri davanti per raggiungere la lunghezza di 10 byte nell'intestazione
$total_length_str = str_pad($total_length, 10, '0', STR_PAD_LEFT);
// Restituisci i dati
return $total_length_str . $xml_string;
}
}
Esempio due
Definizione del protocollo
- Un'intestazione di 4 byte in formato di rete, unsigned int, che indica la lunghezza dell'intero pacchetto
- La parte dati è una stringa JSON
Esempio di pacchetto
****{"type":"message","content":"hello all"}
Dove i primi quattro byte asterisco (*) rappresentano un unsigned int in formato di rete e sono caratteri non visibili, seguiti dai dati del pacchetto in formato JSON
Implementazione del protocollo
namespace Protocols;
class JsonInt
{
public static function input($recv_buffer)
{
// Se la lunghezza dei dati è inferiore a 4 byte, non è possibile determinare la lunghezza del pacchetto, restituisci 0 e aspetta ulteriori dati
if(strlen($recv_buffer) < 4)
{
return 0;
}
// Utilizza la funzione unpack per convertire i primi 4 byte in numeri, che rappresentano la lunghezza dell'intero pacchetto
$unpack_data = unpack('Ntotal_length', $recv_buffer);
return $unpack_data['total_length'];
}
public static function decode($recv_buffer)
{
// Rimuovi i primi 4 byte per ottenere i dati JSON del pacchetto
$body_json_str = substr($recv_buffer, 4);
// Decodifica JSON
return json_decode($body_json_str, true);
}
public static function encode($data)
{
// Codifica i dati in JSON per ottenere il corpo del pacchetto
$body_json_str = json_encode($data);
// Calcola la lunghezza totale del pacchetto (4 per l'intestazione + lunghezza della stringa JSON)
$total_length = 4 + strlen($body_json_str);
// Restituisci i dati confezionati
return pack('N', $total_length) . $body_json_str;
}
}
Esempio tre (trasferimento di file con protocollo binario)
Definizione del protocollo
struct
{
unsigned int total_len; // Lunghezza dell'intero pacchetto, byte di ordine di rete big-endian
char name_len; // Lunghezza del nome del file
char name[name_len]; // Nome del file
char file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]; // Dati del file
}
Esempio del protocollo
*****logo.png******************
Dove i primi quattro byte asterisco () rappresentano un unsigned int in formato di rete e sono caratteri non visibili, il quinto è il numero di byte utilizzato per memorizzare la lunghezza del nome del file, seguito direttamente dal nome del file e successivamente dai dati binari non elaborati del file
Implementazione del protocollo
namespace Protocols;
class BinaryTransfer
{
// Lunghezza dell'intestazione del protocollo
const PACKAGE_HEAD_LEN = 5;
public static function input($recv_buffer)
{
// Se i dati non sono lunghi quanto l'intestazione del protocollo, continua ad aspettare
if(strlen($recv_buffer) < self::PACKAGE_HEAD_LEN)
{
return 0;
}
// Scompatta i dati
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// Restituisci la lunghezza del pacchetto
return $package_data['total_len'];
}
public static function decode($recv_buffer)
{
// Scompatta i dati
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// Lunghezza del nome del file
$name_len = $package_data['name_len'];
// Estrai il nome del file dal flusso di dati
$file_name = substr($recv_buffer, self::PACKAGE_HEAD_LEN, $name_len);
// Estrai i dati binari del file dal flusso di dati
$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)
{
// Puoi codificare i dati da inviare al client in base alle tue esigenze, qui vengono restituiti solo i dati iniziali di testo
return $data;
}
}
Esempio di utilizzo del protocollo sul server
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('BinaryTransfer://0.0.0.0:8333');
// Salva il file in 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();
Esempio di utilizzo del client per inviare file client.php (in questo esempio, il client viene simulato in PHP per l'upload)
<?php
/** Client di upload file **/
// Indirizzo di upload
$address = "127.0.0.1:8333";
// Controlla i parametri del percorso del file da inviare
if(!isset($argv[1]))
{
exit("Usa php client.php \$file_path\n");
}
// Percorso del file da inviare
$file_to_transfer = trim($argv[1]);
// Il file da inviare non esiste localmente
if(!is_file($file_to_transfer))
{
exit("$file_to_transfer non esiste\n");
}
// Stabilisci la connessione del socket
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
exit("$errmsg\n");
}
// Imposta il blocco
stream_set_blocking($client, 1);
// Nome del file
$file_name = basename($file_to_transfer);
// Lunghezza del nome del file
$name_len = strlen($file_name);
// Dati binari del file
$file_data = file_get_contents($file_to_transfer);
// Lunghezza dell'intestazione del protocollo 4 byte per la lunghezza + 1 byte per la lunghezza del nome del file
$PACKAGE_HEAD_LEN = 5;
// Pacchetto del protocollo
$package = pack('NC', $PACKAGE_HEAD_LEN + strlen($file_name) + strlen($file_data), $name_len) . $file_name . $file_data;
// Esegui l'upload
fwrite($client, $package);
// Stampa i risultati
echo fread($client, 8192),"\n";
Esempio di utilizzo del client
Esegui da linea di comando php client.php <percorso_del_file>
Ad esempio php client.php abc.jpg
Esempio 4 (Caricamento di file tramite protocollo di testo)
Definizione del protocollo
json + nuova riga, il json contiene il nome del file e i dati del file codificati in base64 (aumenterà il volume del 1/3)
Esempio del protocollo
{"file_name":"logo.png","file_data":"PD9waHAKLyo......"}\n
Nota che alla fine c'è un carattere di nuova riga, rappresentato da "\n"
tra virgolette doppie in PHP.
Implementazione del protocollo
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)
{
// Decodifica il pacchetto
$package_data = json_decode(trim($recv_buffer), true);
// Prende il nome del file
$file_name = $package_data['file_name'];
// Prende i dati del file codificati in base64
$file_data = $package_data['file_data'];
// Decodifica base64 per ottenere i dati binari originali del file
$file_data = base64_decode($file_data);
// Ritorna i dati
return array(
'file_name' => $file_name,
'file_data' => $file_data,
);
}
public static function encode($data)
{
// Si può codificare i dati da inviare al client secondo le proprie esigenze, qui semplicemente restituiamo il testo originale
return $data;
}
}
Esempio di utilizzo del protocollo lato server
Spiegazione: La sintassi è la stessa del caricamento binario, quindi è possibile passare al protocollo con pochissime modifiche al codice di business.
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('TextTransfer://0.0.0.0:8333');
// Salvare il file in/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();
Esempio di utilizzo del client di file textclient.php (in questo caso verrà simulato il client in PHP per il caricamento)
<?php
/** Client di caricamento file **/
// Indirizzo di caricamento
$address = "127.0.0.1:8333";
// Controlla i parametri del percorso di caricamento del file
if(!isset($argv[1]))
{
exit("utilizzare php client.php \$file_path\n");
}
// Percorso del file da caricare
$file_to_transfer = trim($argv[1]);
// Il file da caricare non esiste localmente
if(!is_file($file_to_transfer))
{
exit("$file_to_transfer non esiste\n");
}
// Stabilire una connessione socket
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
exit("$errmsg\n");
}
stream_set_blocking($client, 1);
// Nome del file
$file_name = basename($file_to_transfer);
// Dati binari del file
$file_data = file_get_contents($file_to_transfer);
// Codifica in base64
$file_data = base64_encode($file_data);
// Pacchetto di dati
$package_data = array(
'file_name' => $file_name,
'file_data' => $file_data,
);
// Pacchetto del protocollo json + invio
$package = json_encode($package_data)."\n";
// Esecuzione del caricamento
fwrite($client, $package);
// Stampare il risultato
echo fread($client, 8192),"\n";
Esempio di utilizzo del client
Eseguire il comando nella riga di comando php textclient.php <percorso_del_file>
Ad esempio php textclient.php abc.jpg