page contents

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
  • 阅读 ( 679 )

你可能感兴趣的文章

相关问题

0 条评论

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

21 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1478 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章