Einige Beispiele
Beispiel Eins
Protokolldefinition
- Der Header hat eine feste Länge von 10 Byte, um die gesamte Paketlänge zu speichern; unzureichende Bits werden mit 0 aufgefüllt.
- Das Datenformat ist xml.
Beispiel für ein Datenpaket
0000000121<?xml version="1.0" encoding="ISO-8859-1"?>
<request>
<module>user</module>
<action>getInfo</action>
</request>
Dabei steht 0000000121 für die gesamte Paketlänge, gefolgt von dem Inhalt des Pakets im xml-Datenformat.
Protokollimplementierung
namespace Protocols;
class XmlProtocol
{
public static function input($recv_buffer)
{
if(strlen($recv_buffer) < 10)
{
// Weniger als 10 Byte, zurückgeben 0 und auf weitere Daten warten.
return 0;
}
// Rückgabe der Paketlänge, die die Länge der Headerdaten + die Länge des Paketkörpers enthält.
$total_len = base_convert(substr($recv_buffer, 0, 10), 10, 10);
return $total_len;
}
public static function decode($recv_buffer)
{
// Anfrage Paketkörper
$body = substr($recv_buffer, 10);
return simplexml_load_string($body);
}
public static function encode($xml_string)
{
// Länge des Paketkörpers + Headerlänge
$total_length = strlen($xml_string) + 10;
// Länge auf 10 Byte auffüllen, unzureichende Bits werden mit 0 aufgefüllt
$total_length_str = str_pad($total_length, 10, '0', STR_PAD_LEFT);
// Rückgabe der Daten
return $total_length_str . $xml_string;
}
}
Beispiel Zwei
Protokolldefinition
- Der Header ist ein 4 Byte unsigned int im Netzwerk-Bit-Reihenfolge, der die gesamte Paketlänge markiert.
- Der Datenabschnitt ist eine JSON-Zeichenkette.
Beispiel für ein Datenpaket
****{"type":"message","content":"hello all"}
Dabei stehen die ersten vier Byte * für ein unsichtbares Zeichen als unsigned int im Netzwerk-Bit-Reihenfolge, gefolgt von den Daten im JSON-Format.
Protokollimplementierung
namespace Protocols;
class JsonInt
{
public static function input($recv_buffer)
{
// Wenn die empfangenen Daten weniger als 4 Byte sind, kann die Paketlänge nicht erkannt werden, zurückgeben 0 und auf weitere Daten warten.
if(strlen($recv_buffer) < 4)
{
return 0;
}
// Mit der unpack-Funktion die Header-4-Byte in eine Zahl umwandeln, die Header-4-Byte sind die gesamte Paketlänge.
$unpack_data = unpack('Ntotal_length', $recv_buffer);
return $unpack_data['total_length'];
}
public static function decode($recv_buffer)
{
// Header mit 4 Byte entfernen, um die JSON-Daten des Paketkörpers zu erhalten.
$body_json_str = substr($recv_buffer, 4);
// JSON dekodieren
return json_decode($body_json_str, true);
}
public static function encode($data)
{
// JSON kodieren und den Paketkörper erhalten.
$body_json_str = json_encode($data);
// Gesamtlänge des Pakets berechnen, Header 4 Byte + Packkörper Byte-Anzahl.
$total_length = 4 + strlen($body_json_str);
// Packen der Daten zurückgeben.
return pack('N', $total_length) . $body_json_str;
}
}
Beispiel Drei (Verwendung des binären Protokolls zum Hochladen von Dateien)
Protokolldefinition
struct
{
unsigned int total_len; // Gesamtlänge des Pakets, Big-Endian Netzwerk-Bit-Reihenfolge.
char name_len; // Länge des Dateinamens.
char name[name_len]; // Dateiname.
char file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]; // Dateidaten.
}
Protokollbeispiel
*****logo.png******************
Dabei stehen die ersten vier Byte für einen unsigned int im Netzwerk-Bit-Reihenfolge als unsichtbares Zeichen. Das fünfte speichert die Länge des Dateinamens in einem Byte, gefolgt vom Dateinamen und dann den ursprünglichen binären Dateidaten.
Protokollimplementierung
namespace Protocols;
class BinaryTransfer
{
// Protokolkopf Länge
const PACKAGE_HEAD_LEN = 5;
public static function input($recv_buffer)
{
// Wenn die Länge des Protokolkopfes nicht ausreicht, weiter warten.
if(strlen($recv_buffer) < self::PACKAGE_HEAD_LEN)
{
return 0;
}
// Entpacken
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// Rückgabe der Paketlänge
return $package_data['total_len'];
}
public static function decode($recv_buffer)
{
// Entpacken
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// Dateinamen Länge
$name_len = $package_data['name_len'];
// Dateinamen aus dem Datenstrom extrahieren
$file_name = substr($recv_buffer, self::PACKAGE_HEAD_LEN, $name_len);
// Binäre Dateidaten aus dem Datenstrom extrahieren
$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)
{
// Daten, die basierend auf den eigenen Bedürfnissen kodiert werden sollen, werden hier einfach im Original zurückgegeben.
return $data;
}
}
Beispiel zur Verwendung des Serverprotokolls
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('BinaryTransfer://0.0.0.0:8333');
// Datei im tmp-Ordner speichern
$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();
Client-Datei client.php (hier wird PHP verwendet, um den Client-Upload zu simulieren)
<?php
/** Upload-Datei-Client **/
// Upload-Adresse
$address = "127.0.0.1:8333";
// Überprüfen des Upload-Dateipfads
if(!isset($argv[1]))
{
exit("use php client.php \$file_path\n");
}
// Upload-Dateipfad
$file_to_transfer = trim($argv[1]);
// Die lokale Datei zum Hochladen existiert nicht
if(!is_file($file_to_transfer))
{
exit("$file_to_transfer not exist\n");
}
// Socket-Verbindung aufbauen
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
exit("$errmsg\n");
}
// Blockierend einstellen
stream_set_blocking($client, 1);
// Dateiname
$file_name = basename($file_to_transfer);
// Länge des Dateinamens
$name_len = strlen($file_name);
// Binäre Dateidaten
$file_data = file_get_contents($file_to_transfer);
// Länge des Protokolkopfs: 4 Byte Paketlänge + 1 Byte Dateinamenslänge
$PACKAGE_HEAD_LEN = 5;
// Protokollpaket
$package = pack('NC', $PACKAGE_HEAD_LEN + strlen($file_name) + strlen($file_data), $name_len) . $file_name . $file_data;
// Upload ausführen
fwrite($client, $package);
// Ergebnis drucken
echo fread($client, 8192), "\n";
Beispiel zur Verwendung des Clients
Führen Sie im Terminal php client.php <Dateipfad> aus.
Zum Beispiel php client.php abc.jpg
Beispiel Vier (Verwendung des Textprotokolls zum Hochladen von Dateien)
Protokolldefinition
JSON + Zeilenumbruch, wobei JSON den Dateinamen sowie die base64_encode-kodierten (vergrößert um 1/3 der Größe) Dateidaten enthält.
Protokollbeispiel
{"file_name":"logo.png","file_data":"PD9waHAKLyo......"}\n
Beachten Sie, dass am Ende ein Zeilenumbruch vorhanden ist, der in PHP durch das doppelte Anführungszeichen Zeichen "\n" angezeigt wird.
Protokollimplementierung
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)
{
// Entpacken
$package_data = json_decode(trim($recv_buffer), true);
// Dateinamen extrahieren
$file_name = $package_data['file_name'];
// base64_encode kodierte Dateidaten extrahieren
$file_data = $package_data['file_data'];
// base64_decode zur Wiederherstellung der ursprünglichen binären Dateidaten
$file_data = base64_decode($file_data);
// Daten zurückgeben
return array(
'file_name' => $file_name,
'file_data' => $file_data,
);
}
public static function encode($data)
{
// Daten, die basierend auf den eigenen Bedürfnissen kodiert werden sollen, werden hier einfach im Original zurückgegeben.
return $data;
}
}
Beispiel zur Verwendung des Serverprotokolls
Hinweis: Die Schreibweise ist vergleichbar mit der binären Uploadschrift, wodurch nahezu keine Änderung an der Geschäftslogik erforderlich ist, um das Protokoll zu wechseln.
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('TextTransfer://0.0.0.0:8333');
// Datei im tmp-Ordner speichern
$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();
Client-Datei textclient.php (hier wird PHP verwendet, um den Client-Upload zu simulieren)
<?php
/** Upload-Datei-Client **/
// Upload-Adresse
$address = "127.0.0.1:8333";
// Überprüfen des Upload-Dateipfads
if(!isset($argv[1]))
{
exit("use php client.php \$file_path\n");
}
// Upload-Dateipfad
$file_to_transfer = trim($argv[1]);
// Die lokale Datei zum Hochladen existiert nicht
if(!is_file($file_to_transfer))
{
exit("$file_to_transfer not exist\n");
}
// Socket-Verbindung aufbauen
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
exit("$errmsg\n");
}
stream_set_blocking($client, 1);
// Dateiname
$file_name = basename($file_to_transfer);
// Binäre Dateidaten
$file_data = file_get_contents($file_to_transfer);
// base64 kodieren
$file_data = base64_encode($file_data);
// Datenpaket
$package_data = array(
'file_name' => $file_name,
'file_data' => $file_data,
);
// Protokollpaket JSON + Zeilenumbruch
$package = json_encode($package_data) . "\n";
// Upload ausführen
fwrite($client, $package);
// Ergebnis drucken
echo fread($client, 8192), "\n";
Beispiel zur Verwendung des Clients
Führen Sie im Terminal php textclient.php <Dateipfad> aus.
Zum Beispiel php textclient.php abc.jpg