### 关于分包
> 以前习惯于用固定字符串来处理分包,这种更适合纯软件的交互,数据格式封装成json;
> 嵌入式硬件一般不会上传json的字符串,更习惯用十六进制字符表示,或者acsii码,所以协议也就需要用‘固定包头 + 包体协议’来处理。
### 固定包头
1. 这里也有两种方式,一种是上传的数据是swoole能直接解析的10进制或者字符串,这时候可以用unpack/pack来处理,比如:
“`
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
$data = “123456789012345678901234567890”;
$type = 0x30;
$uid = 0x123;
$length = strlen($data);
$serid = 0x15;
$head = pack(“N4”, $type, $uid, $length, $serid);
$body = pack(“a{$length}”, $data);
$message = $head.$body;
if ($client->connect(‘127.0.0.1’, 9502, -1)) {
$client->send($message);
echo $client->recv();
}
$client->close();
“`
“`
$serv = new Swoole\Server(‘127.0.0.1’, 9502);
$serv->set([
‘open_length_check’ => true,
‘package_max_length’ => 81920,
‘package_length_type’ => ‘N’,
‘package_length_offset’ => 8,
‘package_body_offset’ => 16,
]);
$serv->on(‘connect’, function($server, $fd){
echo $fd. ” Connect !”.PHP_EOL;
});
$serv->on(‘receive’, function($server, $fd, $from_id, $data){
var_dump($data); // 源数据
$tmp = unpack(“Ntype/Nuid/Nlength”, $data);
$unpacking = unpack(“Ntype/Nuid/Nlength/Nserid/a{$tmp[‘length’]}body”, $data);
var_dump($unpacking); // 解包后数据
$server->send($fd, ” Server Receive Data: “. $unpacking[‘body’]);
});
$serv->on(‘close’, function($server){
});
$serv->start();
“`
2. 还有一种是上传过来的数据是二进制数,需要自己用自定义回调处理长度。设置长度解析函数(package_length_func)。
“`
$server = new Swoole\Server(“127.0.0.1”, 9501);
$server->set(array(
‘open_length_check’ => true,
‘dispatch_mode’ => 1,
‘package_length_func’ => function ($data) {
if (strlen($data) < 8) {
return 0;
}
$length = intval(trim(substr($data, 0, 8)));
if ($length <= 0) {
return -1;
}
return $length + 8;
},
‘package_max_length’ => 2000000, //协议最大长度
));
$server->on(‘receive’, function (Swoole\Server $server, $fd, $reactor_id, $data) {
var_dump($data);
echo “#{$server->worker_id}>> received length=” . strlen($data) . “\n”;
});
$server->start();
“`
> 还有一种是上传过来的数据是二进制数,需要自己用自定义回调处理长度。设置长度解析函数(package_length_func)。
“`
‘package_length_func’ => function ($data) {
// 2进制转16进制,这个坑好久才反应过来
$d = bin2hex($data);
dump(date(‘Y-m-d H:i:s’) . ‘ >> ‘.$d);
if (strlen($d) < 8) {
return 0;
}
$length = substr($d, 4, 4);
// echo ‘str length: ‘ . $length . “.\n”;
// 十六进制转10进制,这里的处理可能会有问题,这里算的是字节位数,一个16进制数占用0.5字节
$length = base_convert(trim($length, ‘0’), 16, 10);
// echo ‘byte length: ‘ . $length . “.\n”;
if ($length <= 0) {
return -1;
}
return $length;
}
“`
### 其他问题
1. 一般协议都长这样
> 起始域 长度域 版本域 序列号域 命令代CMD 数据域 校验和域
AAF5 – 6300 – 10 – 00 – 6A00 – N – 00
2. 校验和算法大同小异,比如这样:<https://www.cnblogs.com/kala00k/p/12686664.html>
“`
// 计算校验和
public static function getJyh($m){
$dexArr = str_split($m, 2);
foreach ($dexArr as $key => $value) {
$dexArr[$key] = hexdec($value);
}
$ck2 = dechex(array_sum($dexArr));
$check = substr($ck2, strlen($ck2) – 2, 2);
if (strlen($check) == ‘1’) {
$check = ‘0’.$check;
}
return $check;
}
“`
3. 进制转换的时候是需要用0来补足字节位数的
“`
public static function decto_bin($data)
{
$d = base_convert($data, 10, 16);
if (strlen($d) == ‘1’) {
$d = ‘0’ . $d;
}
if (strlen($d) < 4) {
$num = 4 – strlen($d);
$arr = [”, ‘0’, ’00’];
$d .= $arr[$num];
}
return strtoupper($d);
}
“`
4. 十六进制转十进制的时候如果刚好是16的倍数需要处理0的问题,暂时没有遇到这样定义的先如下:
“`
// 这个解法在正好是16倍数的时候有问题,应该先拆成2组,理论上应该拆成这样的两两数组 $dexArr = str_split($m, 2);
$cmd = base_convert(trim($cmd,’0′), 16, 10);
“`