Some Examples
Example One
Protocol Definition
- The header is a fixed length of 10 bytes to store the total length of the data packet, padded with 0 if insufficient bits
- The data format is xml
Sample Packet
0000000121<?xml version="1.0" encoding="ISO-8859-1"?>
<request>
<module>user</module>
<action>getInfo</action>
</request>
Here, 0000000121 represents the total length of the data packet, followed immediately by the contents of the body in xml format.
Protocol Implementation
namespace Protocols;
class XmlProtocol
{
public static function input($recv_buffer)
{
if(strlen($recv_buffer) < 10)
{
// Less than 10 bytes, return 0 and continue waiting for data
return 0;
}
// Return packet length, which includes header length + body length
$total_len = base_convert(substr($recv_buffer, 0, 10), 10, 10);
return $total_len;
}
public static function decode($recv_buffer)
{
// Request body
$body = substr($recv_buffer, 10);
return simplexml_load_string($body);
}
public static function encode($xml_string)
{
// Length of body + length of header
$total_length = strlen($xml_string)+10;
// Pad the length part to 10 bytes, filling with 0 if insufficient bits
$total_length_str = str_pad($total_length, 10, '0', STR_PAD_LEFT);
// Return data
return $total_length_str . $xml_string;
}
}
Example Two
Protocol Definition
- The header is a 4-byte unsigned int in network byte order, marking the length of the entire packet
- The data part is a Json string
Sample Packet
****{"type":"message","content":"hello all"}
Here, the first four bytes of * are an unsigned int in network byte order, represented as non-visible characters, followed by the body data in Json format.
Protocol Implementation
namespace Protocols;
class JsonInt
{
public static function input($recv_buffer)
{
// Received data is not enough for 4 bytes, unable to determine the packet length, return 0 and continue waiting for data
if(strlen($recv_buffer)<4)
{
return 0;
}
// Use the unpack function to convert the first 4 bytes of the header into a number, which is the total length of the entire data packet
$unpack_data = unpack('Ntotal_length', $recv_buffer);
return $unpack_data['total_length'];
}
public static function decode($recv_buffer)
{
// Remove the first 4 bytes to get the body Json data
$body_json_str = substr($recv_buffer, 4);
// Decode json
return json_decode($body_json_str, true);
}
public static function encode($data)
{
// Json encoding to get the body
$body_json_str = json_encode($data);
// Calculate the total length of the package, header length of 4 bytes + body byte count
$total_length = 4 + strlen($body_json_str);
// Return the packed data
return pack('N',$total_length) . $body_json_str;
}
}
Example Three (Using Binary Protocol to Upload Files)
Protocol Definition
struct
{
unsigned int total_len; // Total length of the packet, big-endian network byte order
char name_len; // Length of the file name
char name[name_len]; // File name
char file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]; // File data
}
Protocol Sample
*****logo.png******************
Here, the first four bytes of represent an unsigned int in network byte order, represented as non-visible characters. The fifth is used to store the length of the file name as one byte, immediately followed by the file name, and then the original binary file data.
Protocol Implementation
namespace Protocols;
class BinaryTransfer
{
// Protocol header length
const PACKAGE_HEAD_LEN = 5;
public static function input($recv_buffer)
{
// If it is less than the length of a protocol header, continue waiting
if(strlen($recv_buffer) < self::PACKAGE_HEAD_LEN)
{
return 0;
}
// Unpack
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// Return packet length
return $package_data['total_len'];
}
public static function decode($recv_buffer)
{
// Unpack
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// File name length
$name_len = $package_data['name_len'];
// Extract the file name from the data stream
$file_name = substr($recv_buffer, self::PACKAGE_HEAD_LEN, $name_len);
// Extract the binary file data from the data stream
$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)
{
// You can encode the data sent to the client as needed; here it is just returned as plain text
return $data;
}
}
Server Protocol Usage Example
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('BinaryTransfer://0.0.0.0:8333');
// Save file to 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();
Client File client.php (Simulating client upload with PHP)
<?php
/** File upload client **/
// Upload address
$address = "127.0.0.1:8333";
// Check upload file path parameter
if(!isset($argv[1]))
{
exit("use php client.php \$file_path\n");
}
// File path to upload
$file_to_transfer = trim($argv[1]);
// The file to upload does not exist locally
if(!is_file($file_to_transfer))
{
exit("$file_to_transfer not exist\n");
}
// Establish socket connection
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
exit("$errmsg\n");
}
// Set to blocking
stream_set_blocking($client, 1);
// File name
$file_name = basename($file_to_transfer);
// Length of the file name
$name_len = strlen($file_name);
// File binary data
$file_data = file_get_contents($file_to_transfer);
// Protocol header length 4 bytes for packet length + 1 byte for file name length
$PACKAGE_HEAD_LEN = 5;
// Protocol packet
$package = pack('NC', $PACKAGE_HEAD_LEN + strlen($file_name) + strlen($file_data), $name_len) . $file_name . $file_data;
// Execute upload
fwrite($client, $package);
// Print result
echo fread($client, 8192),"\n";
Client Usage Example
Run in the command line php client.php <file_path>
For example, php client.php abc.jpg
Example Four (Using Text Protocol to Upload Files)
Protocol Definition
json + newline, the json contains the file name and the base64_encode encoded (increases size by 1/3) file data
Protocol Sample
{"file_name":"logo.png","file_data":"PD9waHAKLyo......"}\n
Note that the end is a newline character, represented in PHP with the double quote character "\n".
Protocol Implementation
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)
{
// Unpack
$package_data = json_decode(trim($recv_buffer), true);
// Extract file name
$file_name = $package_data['file_name'];
// Extract base64_encode file data
$file_data = $package_data['file_data'];
// base64_decode restore original binary file data
$file_data = base64_decode($file_data);
// Return data
return array(
'file_name' => $file_name,
'file_data' => $file_data,
);
}
public static function encode($data)
{
// You can encode the data sent to the client as needed; here it is just returned as plain text
return $data;
}
}
Server Protocol Usage Example
Note: The writing method is the same as the binary upload method, which allows switching protocols with almost no changes to any business code.
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('TextTransfer://0.0.0.0:8333');
// Save file to 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();
Client File textclient.php (Simulating client upload with PHP)
<?php
/** File upload client **/
// Upload address
$address = "127.0.0.1:8333";
// Check upload file path parameter
if(!isset($argv[1]))
{
exit("use php client.php \$file_path\n");
}
// File path to upload
$file_to_transfer = trim($argv[1]);
// The file to upload does not exist locally
if(!is_file($file_to_transfer))
{
exit("$file_to_transfer not exist\n");
}
// Establish socket connection
$client = stream_socket_client($address, $errno, $errmsg);
if(!$client)
{
exit("$errmsg\n");
}
stream_set_blocking($client, 1);
// File name
$file_name = basename($file_to_transfer);
// File binary data
$file_data = file_get_contents($file_to_transfer);
// base64 encode
$file_data = base64_encode($file_data);
// Data packet
$package_data = array(
'file_name' => $file_name,
'file_data' => $file_data,
);
// Protocol packet json + newline
$package = json_encode($package_data)."\n";
// Execute upload
fwrite($client, $package);
// Print result
echo fread($client, 8192),"\n";
Client Usage Example
Run in the command line php textclient.php <file_path>
For example, php textclient.php abc.jpg