page contents

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

连接,心跳,tcp,udp 对比及粘包处理


1.连接与长短连接

   常识:任何需要从服务中获取数据都是需要建立连接的,并且建立与关闭连接均是会有资源消耗

   短连接:一次连接一次交互,简称一次性。场景与例子:如大部分web网页、ajax、http。案例略

   长连接:一次连接多次交互,简称连绵不绝。QQ微信聊天、直播、rpc。

   xshell中小技巧: pwd命令 查看当前路径

                                端口被占用时,通过 命令 netstat -nlap | grep xxxx 查看(xxxx被占用端口) 干掉进程 kill -9 id (进程id号 )

   长连接案例

  长连接服务端:tcpServer.php

 <?php

//var_dump(swoole_get_local_ip());
echo swoole_get_local_ip()['ens33'].':9503\n';
/*
swoole_get_local_ip() 此函数用于获取本机所有网络接口的 IP 地址
函数原型:function swoole_get_local_ip(): array
返回当前机器的所有网络接口的 IP 地址,结果为数组
返回值类似如下:
Array
(
[eno1] => 10.10.28.228
[br-1e72ecd47449] => 172.20.0.1
[docker0] => 172.17.0.1
)
*/
$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) {
$server->send($fd, "Swoole: {$data}");
//$server->close($fd); //客户端多次发送时,注意关闭此行
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});

$server->start();

  客户端:tcpClient.php

<?php

$client = new Swoole\Client(SWOOLE_SOCK_TCP);

if (!$client->connect('127.0.0.1', 9503, -1)) {
$err=$client->errCode;
//$client->close();
exit("connect failed. Error: {$err}\n");
}

$client->send("hello world\n");

echo $client->recv();

//client->close(); //多次发送,可重复上述发送接收信息,最后关闭,

/************多次发送 start **********/

/*sleep(1); //防止粘包

$client->send("hello world\n");

echo $client->recv();

sleep(1); //防止粘包

$client->send("hello world\n");

echo $client->recv();*/

//$client->close(); //即使不关闭,服务端也会监测到。因为代码执行送了,没再发包。

/************多次发送 end **********/

/************保持长连接 start **********/
while(true){

}
//需要手动关闭连接 xshell ctrl+c
/************保持长连接 end **********/

 运行,打开两个 xshell,进入相应目录,分别运行,结果如图

默认状态:客户端执行完毕,即使没有关闭,服务器检测,将自动关闭,

attachments-2020-11-CcOQ8ggl5fbdaf6976c80.jpg多次发送:客户端执行完毕,即使没有关闭,服务器检测,将自动关闭,

attachments-2020-11-P9pqPjZw5fbdb0b4c87bb.png

whiel循环,保持长连接

attachments-2020-11-ieSbJYW95fbdae319d17d.jpg不断开了,长连接


长连接客户端 tcClinet_keep.php(服务端见上tcpServer.php)

<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP);
/*
Swoole\Client 支持在 PHP-FPM/Apache 中创建一个 TCP 长连接到服务器端。使用方法:
$client = new Swoole\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP);
启用 SWOOLE_KEEP 选项后,一个请求结束不会关闭 socket,下一次再进行 connect 时会自动复用
上次创建的连接。如果执行 connect 发现连接已经被服务器关闭,那么 connect 会创建新的连接。
SWOOLE_KEEP 的优势
TCP 长连接可以减少 connect 3 次握手 /close 4 次挥手带来的额外 IO 消耗
降低服务器端 close/connect 次数
局限性:适用于http连接,xshell中不起作用

*/

if (!$client->connect('127.0.0.1', 9503, -1)) {
$err=$client->errCode;
//$client->close();
exit("connect failed. Error: {$err}\n");
}

$client->send("hello world\n");

echo $client->recv();

//client->close();

attachments-2020-11-ae8xDhdJ5fbb1d07c16c4.png


长连接的客户端关闭而连接始终不断开,消耗大?怎么办---健康检查:心跳

2.健康检查:心跳

长连接的断开检测方法:

服务端保存客户端会话的有效性---轮询机制

 attachments-2020-11-35nUr7FS5fbd97133e22b.jpg

存在的问题:长连接的访问客个数不确定,消耗仍然大,采用这个方法的比较少,这里暂不深究

平台上监控所有客户端的网络状况---心跳机制

attachments-2020-11-McpLdxP35fbd979045d2f.png

心跳包:从客户端到服务器这条巨大的链路中会经过无数的路由器,每个路由器都可能会检测多少秒时间内没有数据包,则会自动关闭连接的节能机制。为了让这个可能会出现的节能机制失效,客户端可以设置一个定时器,每隔固定时间发送一个随机字符一 字节的数据包,这种数据包就是心跳包。

心跳检测设置参数:

heartbeat_check_interval:此选项表示每隔多久轮循一次,单位为秒。如 heartbeat_check_interval => 60,表示每 60 秒,遍历所有连接,如果该连接在 120 秒内(heartbeat_idle_time 未设置时默认为 interval 的两倍),没有向服务器发送任何数据,此连接将被强制关闭。若未配置,则不会启用心跳,该配置默认关闭

注意:

Server 并不会主动向客户端发送心跳包,而是被动等待客户端发送心跳。服务器端的 heartbeat_check 仅仅是检测连接上一次发送数据的时间,如果超过限制,将切断连接。

heartbeat_check 仅支持 TCP 连接

heartbeat_idle_time:连接最大允许空闲的时间,需要与 heartbeat_check_interval 配合使用

使用实例
array('heartbeat_idle_time'=>600,// 表示一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭'heartbeat_check_interval'=>60,// 表示每60秒遍历一次);
注意:如果只设置了 heartbeat_idle_time 未设置 heartbeat_check_interval 底层将不会创建心跳检测线程,PHP 代码中可以调用 heartbeat 方法手工处理超时的连接

案例1 长连接的断开设置
服务端  tcpServer_heart.php
<?php
echo swoole_get_local_ip()['ens33'].':9503\n';

$server = new Swoole\Server("0.0.0.0", 9503);
//set设置参数.这里设置的参数意义为:每隔2s,在3s内,没有给我发送消息的连接
$server->set([
'heartbeat_check_interval'=>2, //检测所有的连接
'heartbeat_idle_time'=>3
]);

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

$server->on('receive', function ($server, $fd, $reactor_id, $data) {
$server->send($fd, "Swoole: {$data}");
//$server->close($fd); //客户端多次发送时,注意关闭此行
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});

$server->start();
客户端tcpCliet_heart.php
<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9503, -1)) {
$err=$client->errCode;
//$client->close();
exit("connect failed. Error: {$err}\n");
}
echo date('Y-m-d H:i:s',time())."\n";
$client->send("hello world\n");
echo $client->recv();
//停止1s后往下运行
sleep(1);
echo date('Y-m-d H:i:s',time())."\n";
$client->send("hello world\n");
echo $client->recv();
//停止4s后往下运行
sleep(4);
echo date('Y-m-d H:i:s',time())."\n";
$client->send("hello world\n");
echo $client->recv();
//保持不断开
while(true){

}
//client->close();

运行,打开两个 xshell,进入相应目录,分别运行,结果如图
attachments-2020-11-m3vs8ZOT5fbdac368c26f.png

案例2 长连接的断开-客户端优化定时器
服务器端onReceive方法中的发送消息到客户端,加个换行符,便于在客户端观看效果 
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
$server->send($fd, "Swoole: {$data}\n");
//$server->close($fd); //客户端多次发送时,注意关闭此行
});
客户端:tcpClient_heart_tick.php
<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9503, -1)) {
$err=$client->errCode;
//$client->close();
exit("connect failed. Error: {$err}\n");
}

swoole_timer_tick(2000,function($timer_id) use ($client){
$client->send(1);
echo date('Y-m-d H:i:s',time())."\n";
echo $client->recv();
});
运行,打开两个 xshell,进入相应目录,分别运行,结果如图
attachments-2020-11-VU6nkHv75fbdba6f30d3a.png

注意,上述客户端定时器下面,应用sleep时的问题,会阻塞进程而影响到定时器!

3.tcp PS  udp
  TCP(Transmission Control Protocol传输控制协议):是一 种面向连接的、可靠的、基于字节流的传输层通信协议,使用三次握手协议建立连接、四次挥手断开连接。面向连接意味着两个使用TCP的应用(通常是一个客户端和一 个服务器)在彼此交换数据包之前必须先建立一一个TCP连接。在一一个TCP连接中,仅有两方进行彼此通信,广播和多播不能用TCP。TCP协议的作用是,保证数据通信的完整性和可靠性,防止丢包。TCP把连接作为最基本的对象,每-条TCP连接都有两个端点, 这种端点我们叫作套接字(socket),端口号拼接到IP地址即构成了套接字
UDP(User Datagram Protocol用户数据报协议):是OSI(Open System Interconnection开放式系统互联)参考模型中- -种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。
Tcp与udp对比
1)、TCP提供的是面向连接的、可靠的数据流传输; UDP提供的是非面向连接的、不可靠的数据流传输。TCP提供可靠的服务,通过TCP连接传送的数据,无差错、不丢失、不重复,按序到达; UDP尽最大努力交付,即不保证可靠交付。
2)、TCP面向字节流; UDP面向报文。
3)、TCP连接只能是点到点的; UDP支持-对一、一对多 多对一和多对多的交互通信。
4)、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
**电话 udp 短信tcp

4.Tcp连接及粘包处理
  41)Tcp连接 三次握手及四次挥手
         SYN:标记位、用于建立会话连接(服务端根据这个确定客户端)
         seq:序列号(随机产生)
         ACK:服务端生成的确认码
         ack:用于校验数据包 ,即确认码(ack是组装的=ACK+seq)
         FIN:用于标识关闭连接

attachments-2020-11-o1uMUgfc5fbdd401ca461.png

三次握手:
第一次握手:客户端发送网络包,服务端收到了。 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。 这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常

四次挥手
attachments-2020-11-KiS5T3WL5fbdd40f85fc6.png

关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

补充:当服务端不发送消息时,客户端始终处于等待状态,连接不会关闭,除非手动关闭!
案例:服务端不发送消息给客户端
 服务器端 tcpServer_nianbao.php
<?php
//var_dump(swoole_get_local_ip());
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) {
//$server->send($fd, "Swoole: {$data}");
//接收到信息直接输出,不给客户端发送信息
echo '接收到信息:'.$data.",但我不给客户端发送信息,让他等吧\n";
//$server->close($fd); //这里不要关闭,否则没有这个预期的结果
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});

$server->start();
客户端:tcpClient_nianbao.php
<?php

$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9503, -1)) {
$err=$client->errCode;
//$client->close();
exit("connect failed. Error: {$err}\n");
}
$client->send("hello world\n");

echo $client->recv(); //这个接收消息语句也必须有,否则会直接关闭

$client->close();
运行:开两个xshell,进入对应目录,分别运行服务器端及客户端,结果如下:客户端一直在等待,连接也持续中
attachments-2020-11-baeCR4lA5fbdf91cc6989.jpg
42)粘包的问题
案例:小数据合包问题
服务端: 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) {
echo "接收到数据:".$data."\n";
$server->send($fd, "Swoole: ok\n");
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();
客户端: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}");
}
for ($i=0; $i < 100; $i++) {
$client->send("hello_world_{$i}|");
}
echo $client->recv();
运行结果:
attachments-2020-11-0SFyASiE5fbe0874090f9.png
由运行结果发现:100条小数据,分别合成两条进行发送!

为了便于看的更清楚,更改服务端tcpServer.php接收到信息时的输出,其它不变,代码如下:
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
//echo "接收到数据:".$data."\n";
echo "接收到数据:".$fd."\n";
$server->send($fd, "Swoole: ok\n");
});
运行结果:
attachments-2020-11-JdeybKbM5fbe0db0096d4.png

案例:大数据拆包问题
服务器端: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) {
echo "接收到数据:".$fd."\n";
$server->send($fd, "Swoole: ok\n");
});

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

$server->start();
客户端: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}\n");
}
//str_repeat() 把字符串重复指定的次数。这里将123456重复100000次,会得到一个大点的数据
$client->send(str_repeat('123456', 100000));

echo $client->recv();
运行结果,开两个Xshell
attachments-2020-11-kEeHH5lc5fbe1749b9e72.png
由运行结果发现:明明客户端只发送了一次数据,而服务器端偏偏收到了10次数据。这说明,客户端发送的数据,拆分成了10次发送。
tcp数据发送过程
attachments-2020-11-HcwxYXQB5fbee2698b360.png

(貌似超出字符限制了)见swoole 第3次课-四方云动:连接、心跳、 tcp PS udp、Tcp连接及粘包处理 (2) 
  • 发表于 2020-11-23 07:49
  • 阅读 ( 588 )

你可能感兴趣的文章

相关问题

0 条评论

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

21 篇文章

作家榜 »

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