swoole 第3次课-四方云动:连接、心跳、 tcp PS udp、Tcp连接及粘包处理 (2)

粘包 ,粘包处理,特殊字符,固定包头+包头协议

接上

43)粘包的处理--特殊字符
传统方法
attachments-2020-11-ajqglqb35fbee3186e13b.jpg
客户端 tcpClient.php
<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP );
if (!$client->connect('127.0.0.1', 9503, -1)) {
exit("connect failed. Error: {$client->errCode}");
}
//结束的特殊字符追加到发送数据的结尾
$end="\r\n";

for ($i=0; $i < 100; $i++) {
$client->send("hello_world_{$i}|".$end);
}
echo $client->recv();

服务端:ordinary\tcpServer.php

<?php
// var_dump();
echo swoole_get_local_ip()['ens33'].":9503\n";
$server = new Swoole\Server("0.0.0.0", 9503);
$server->on('connect', function ($server, $fd){
echo "connection open: {$fd}\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
var_dump(explode("\r\n",$data));
echo "接收到数据:".$fd."\n";
$server->send($fd, "Swoole: ok\n");
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();

打开两个xshell,分别进入对应目录 ,先运行服务端,再运行客户端,运行结果如下:

attachments-2020-11-c0CZ0JwP5fbe422c55ff0.png

百次发送合并成两次发送,客户端及服务端分别处理数据。

swoole方法处理粘包

客户端同上

服务端:swoole\tcpServer.php

<?php
// var_dump();
echo swoole_get_local_ip()['ens33'].":9503\n";
$server = new Swoole\Server("0.0.0.0", 9503);
//设置参数
$server->set([
'open_eof_check' => true, //打开EOF检测
'package_eof' => "\r\n", //设置EOF
]);
/*参数设置意义
open_eof_check:打开 EOF 检测【默认值:false】
此选项将检测客户端连接发来的数据,当数据包结尾是指定的字符串时才会投递给 Worker 进程。否则会一直拼接数据包,直到超过缓存区或者超时才会中止。
当出错时底层会认为是恶意连接,丢弃数据并强制关闭连接。
常见的 Memcache/SMTP/POP 等协议都是以 \r\n 结束的,就可以使用此配置。开启后可以保证 Worker 进程一次性总是收到一个或者多个完整的数据包。
注意:
此配置仅对 STREAM(流式的) 类型的 Socket 有效,如 TCP 、Unix Socket Stream
EOF 检测不会从数据中间查找 eof 字符串,所以 Worker 进程可能会同时收到多个数据包,需要在应用层代码中自行 explode("\r\n", $data) 来拆分数据包
*/
/*参数设置意义
package_eof:设置 EOF 字符串。
需要与 open_eof_check 或者 open_eof_split 配合使用。
注意:package_eof 最大只允许传入 8 个字节的字符串
*/
$server->on('connect', function ($server, $fd){
echo "connection open: {$fd}\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
echo "接收到数据:".$data."\n";
//echo "接收到数据:".$fd."\n";
$server->send($fd, "Swoole: ok\n");
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();

打开两个xshell,分别进入对应目录,运行服务端及客户端,运行结果如下:

attachments-2020-11-9GCNRFfk5fbe49a3c9cc1.png

另,补充: open_eof_split设置

attachments-2020-11-pZ0g9Nc55fbee5c18c968.png

44)固定包头+包头协议方法

attachments-2020-11-X6pm1toy5fbee37b1871f.png


即把数据利用pack函数,根据N/n协议,转化为包头+数据的二进制数据内容,后期根据包头的固定长度及记录的数据长度,循环截取转化(即解析)

attachments-2020-11-1745NMnI5fbeed53be9f0.png

再如:attachments-2020-11-GUyd3yUT5fbef15cceab0.png


传统方法

客户端:jhybaotou\tcpClient.php

<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP );
if (!$client->connect('127.0.0.1', 9503, -1)) {
exit("connect failed. Error: {$client->errCode}");
}
//pack();//将数据打包成二进制字符串
//unpack();//解析二进制字符串,把二制制变成十进制数据字符串
for ($i=0; $i < 10; $i++) {
//$client->send("hello_world_{$i}");
echo "start ====> \n";
$context = "123";
var_dump($context);
//利用Pack打包长度
$len = pack("n",strlen($context));
var_dump($len);//注意,二进制数字打印不出来,可看长度来辨别,确实存在
//组包
$send=$len. $context;
var_dump($send);//注意,其内的二进制数据打印不出来,可看长度来辨别,确实存在
$client->send($send);
echo " <==== end \n";
}
echo $client->recv();

服务器端:jhybaotou\ordinary\tcpServer.php

<?php
// var_dump();
echo swoole_get_local_ip()['ens33'].":9503\n";
$server = new Swoole\Server("0.0.0.0", 9503);


$server->on('connect', function ($server, $fd){
echo "connection open: {$fd}\n";
});

$server->on('receive', function ($server, $fd, $reactor_id, $data) {
//源数据 其中的包头二进制数据打印不出来,可看长度来辨别,确实存在
var_dump($data);
//包头长度
$fooLen=unpack("n",substr($data,0,2))[1];
var_dump($fooLen);
//实际数据
$context=substr($data,2,$fooLen);
var_dump($context);

$server->send($fd,"swoole:ok\n");
});

$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});

$server->start();

运行:打开两个xshell,分别进入对应目录,运行服务端及客户端,结果如下:

attachments-2020-11-hYoX49aP5fbf02ee7ebe0.png


swoole 方法

客户端同上

服务端:swoole\tcpServer.php

<?php
// var_dump();
echo swoole_get_local_ip()['ens33'].":9503\n";
$server = new Swoole\Server("0.0.0.0", 9503);
//设置参数
$server->set([
'open_length_check' => true,
'package_length_type' => 'n',
'package_body_offset' => 2,
'package_length_offset' => 0,
'package_max_length' => 2 * 1024 * 1024,
]);
/*参数设置意义
open_length_check:打开包长检测特性【默认值:false】
包长检测提供了固定包头 + 包体这种格式协议的解析。启用后,可以保证 Worker 进程 onReceive 每次都会收到一个完整的数据包。
长度检测协议,只需要计算一次长度,数据处理仅进行指针偏移,性能非常高,推荐使用。
此配置仅对 STREAM 类型的 Socket 有效,如 TCP、Unix Socket Stream

长度协议提供了 3 个选项来控制协议细节。
package_length_type:包头中某个字段作为包长度的值,底层支持了 10 种长度类型。如 N ,n
package_body_offset:从第几个字节开始计算长度,一般有 2 种情况:
length 的值包含了整个包(包头 + 包体),package_body_offset 为 0;包头长度为 N 字节,length 的值不包含包头,仅包含包体,package_body_offset 设置为 N
package_length_offset:length 长度值在包头的第几个字节。

package_max_length 设置最大数据包尺寸,单位为字节。【默认值:2M 即 2 * 1024 * 1024,最小值为 64K】
*/

$server->on('connect', function ($server, $fd){
echo "connection open: {$fd}\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
echo "接收到数据:".$data."\n";
//echo "接收到数据:".$fd."\n";
$server->send($fd, "Swoole: ok\n");
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();

运行:打开两个xshell,分别进入对应目录,运行服务端及客户端,结果如下:

attachments-2020-11-9aDZKm7v5fbf09b875ce0.png

待总结

作业待更新

  • 发表于 2020-11-25 19:45
  • 阅读 ( 64 )
  • 分类:swoole

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
吉洪叶
吉洪叶

19 篇文章

作家榜 »

  1. 六星官方社区工作人员 1094 文章
  2. 吉洪叶 19 文章
  3. 天晓冥 2 文章
  4. muEasy 2 文章
  5. 百日慕晴 2 文章
  6. baiyang 2 文章
  7. plum 1 文章
  8. 周前 1 文章