+
95
-

回答

swoole提供了内置包长检测的方法,该方法提供了固定包头 + 包体这种格式协议的解析。

启用后,可以保证 Worker 进程 onReceive 每次都会收到一个完整的数据包。

长度检测协议,只需要计算一次长度,数据处理仅进行指针偏移,性能非常高,推荐使用。

可见官方是推荐使用这种方式的,就是配置比其他方案要复杂一些, 首先贴一下配置:

$server->set([
// 打开包长检测特性
'package_length_check' => true,
// 包头中某个字段作为包长度的值,底层支持了 10 种长度类型。可参考 pack() 方法
'package_length_type' => 'N',
// length 长度值在包头的第几个字节。
'package_length_offset' => 8,
// 从第几个字节开始计算长度,一般有 2 种情况:
//length 的值包含了整个包(包头 + 包体),package_body_offset 为 0
//包头长度为 N 字节,length 的值不包含包头,仅包含包体,package_body_offset 设置为 N
'package_body_offset' => 16,
// 设置最大数据包尺寸,单位为字节
'package_max_length' => 81920
]);

下面是一个数据包结构例子,可以很好的体现了字段含义。


下面看具体的示例代码:

tcp服务端代码

<?php
$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){
$length = unpack('N', $data)[1];
echo "Length:".$length.PHP_EOL;
$msg = substr($data, -$length);
echo "Msg:".$msg.PHP_EOL;
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();

tcp客户端代码

$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();


网友回复

我知道答案,我要回答