您当前的位置: 首页 > 慢生活 > 程序人生 网站首页程序人生
workerman使用二进制协议上传文件
发布时间:2021-11-28 19:07:05编辑:雪饮阅读()
协议定义
struct
{
unsigned int total_len; // 整个包的长度,大端网络字节序
char name_len; // 文件名的长度
char name[name_len]; // 文件名
char file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]; // 文件数据
}
解释:
total_len:首部四字节(unsigned int)*号代表一个网络字节序的unsigned int数据,为不可见字符
name_len:第5个*是用一个字节(char)存储文件名长度,
name[name_len]:紧接着是文件名
file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]:接着是原始的二进制文件数据
然后是协议的实现:BinaryTransfer.php:
<?php
namespace Workerman\Protocols;
class BinaryTransfer
{
// 协议头长度
const PACKAGE_HEAD_LEN = 5;
public static function input($recv_buffer)
{
// 如果不够一个协议头的长度,则继续等待
if(strlen($recv_buffer) < self::PACKAGE_HEAD_LEN)
{
return 0;
}
// 解包
/*从二进制字符串对数据进行解包:
* unpack(format,data)
* format:
* N - unsigned long(总是32位, big endian 字节顺序)
* C - unsigned char
* */
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// 返回包长
return $package_data['total_len'];
}
public static function decode($recv_buffer)
{
// 解包
$package_data = unpack('Ntotal_len/Cname_len', $recv_buffer);
// 文件名长度
$name_len = $package_data['name_len'];
// 从数据流中截取出文件名
$file_name = substr($recv_buffer, self::PACKAGE_HEAD_LEN, $name_len);
// 从数据流中截取出文件二进制数据
$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)
{
// 可以根据自己的需要编码发送给客户端的数据,这里只是当做文本原样返回
return $data;
}
}
接下来是协议应用入口脚本:start.php:
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('BinaryTransfer://0.0.0.0:8333');
// 保存文件到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.php:
<?php
/** 上传文件客户端 **/
// 上传地址
$address = "127.0.0.1:8333";
// 检查上传文件路径参数
if (!isset($argv[1])) {
exit("use php client.php \$file_path\n");
}
// 上传文件路径
$file_to_transfer = trim($argv[1]);
// 上传的文件本地不存在
if (!is_file($file_to_transfer)) {
exit("$file_to_transfer not exist\n");
}
// 建立socket连接
$client = stream_socket_client($address, $errno, $errmsg);
if (!$client) {
exit("$errmsg\n");
}
// 设置成阻塞
stream_set_blocking($client, 1);
// 文件名
$file_name = basename($file_to_transfer);
// 文件名长度
$name_len = strlen($file_name);
// 文件二进制数据
$file_data = file_get_contents($file_to_transfer);
// 协议头长度 4字节包长+1字节文件名长度
$PACKAGE_HEAD_LEN = 5;
// 协议包
/*
*把数据装入一个二进制字符串:
* pack(format,args+)
* format:
* N - unsigned long(总是32位, big endian 字节顺序)
* C - unsigned char
* args+:可选。规定被包装的一个或多个参数。
*/
/*
*
* 协议定义
struct
{
unsigned int total_len; // 整个包的长度,大端网络字节序
char name_len; // 文件名的长度
char name[name_len]; // 文件名
char file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]; // 文件数据
}
*
* total_len:首部四字节(unsigned int)*号代表一个网络字节序的unsigned int数据,为不可见字符
name_len:第5个*是用一个字节(char)存储文件名长度,
name[name_len]:紧接着是文件名
file[total_len - BinaryTransfer::PACKAGE_HEAD_LEN - name_len]:接着是原始的二进制文件数据
这里N应该是对应:
$PACKAGE_HEAD_LEN + strlen($file_name) + strlen($file_data)
这里我懂的一知半解,暂时先这样,后面有机会再研究
C应该是对应:
pack参数的$name_len("args+:可选。规定被包装的一个或多个参数。"中的那个多个参数)
* */
$package = pack('NC', $PACKAGE_HEAD_LEN + strlen($file_name) + strlen($file_data), $name_len) . $file_name . $file_data;
// 执行上传
fwrite($client, $package);
// 打印结果
echo fread($client, 8192), "\n";
开启协议上传的服务端:
[root@localhost workerman]# php -c /usr/local/php734/lib/php/php.ini start.php start
Workerman[start.php] start in DEBUG mode
---------------------------------------------- WORKERMAN ----------------------------------------------
Workerman version:4.0.22 PHP version:7.3.4
----------------------------------------------- WORKERS -----------------------------------------------
proto user worker listen processes status
tcp root none binaryTransfer://0.0.0.0:8333 1 [OK]
-------------------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
用客户端进行协议上传测试:
[root@localhost ~]# php -c /usr/local/php734/lib/php/php.ini /workerman/client.php dump.rdb
upload success. save path /tmp/dump.rdb
[root@localhost ~]# ls /tmp/ | grep dump
dump.rdb
关键字词:workerman,二进制,协议,上传