Swoole 实现长连接
Swoole底层把这些配置
?php
$server = new \Swoole\Server('127.0.0.1', 6666, SWOOLE_PROCESS);
$server->set([
'worker_num' => 1,
'open_tcp_keepalive' => 1,
'tcp_keepidle' => 4, // 对应tcp_keepalive_time
'tcp_keepinterval' => 1, // 对应tcp_keepalive_intvl
'tcp_keepcount' => 5, // 对应tcp_keepalive_probes
]);
其中
'open_tcp_keepalive' => 1, // 总开关,用来开启tcp_keepalive
'tcp_keepidle' => 4, // 4s没有数据传输就进行检测
// 检测的策略如下:
'tcp_keepinterval' => 1, // 1s探测一次,即每隔1s给客户端发一个包(然后客户端可能会回一个ack的包,如果服务端收到了这个ack包,那么说明这个连接是活着的)
'tcp_keepcount' => 5, // 探测的次数,超过5次后客户端还没有回ack包,那么close此连接
服务端脚本如下:
<?php
$server = new \Swoole\Server('127.0.0.1', 6666, SWOOLE_PROCESS);
$server->set([
'worker_num' => 1,
'open_tcp_keepalive' => 1, // 开启tcp_keepalive
'tcp_keepidle' => 4, // 4s没有数据传输就进行检测
'tcp_keepinterval' => 1, // 1s探测一次
'tcp_keepcount' => 5, // 探测的次数,超过5次后还没有回包close此连接
]);
$server->on('connect', function ($server, $fd) {
var_dump("Client: Connect $fd");
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
var_dump($data);
});
$server->on('close', function ($server, $fd) {
var_dump("close fd $fd");
});
$server->start();
启动这个服务器
php server.php
在开一个窗口执行命令tcpdump -i lo port 6666
通过tcpdump进行抓包
然后在开一个窗口当客户端去连接它nc 127.0.0.1 6666
tcpdump的输出信息如下:
01:48:40.178439 IP localhost.33933 > localhost.6666: Flags [S], seq 43162537, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 0,nop,wscale 7], length 0
01:48:40.178484 IP localhost.6666 > localhost.33933: Flags [S.], seq 1327460565, ack 43162538, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 9833698,nop,wscale 7], length 0
01:48:40.178519 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9833698 ecr 9833698], length 0
01:48:44.229926 IP localhost.6666 > localhost.33933: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0
01:48:44.229951 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0
01:48:44.229926 IP localhost.6666 > localhost.33933: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0
01:48:44.229951 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0
01:48:44.229926 IP localhost.6666 > localhost.33933: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0
// 省略了其他的输出
最开始的时候,会打印三次握手的包
01:48:40.178439 IP localhost.33933 > localhost.6666: Flags [S], seq 43162537, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 0,nop,wscale 7], length 0
01:48:40.178484 IP localhost.6666 > localhost.33933: Flags [S.], seq 1327460565, ack 43162538, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 9833698,nop,wscale 7], length 0
01:48:40.178519 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9833698 ecr 9833698], length 0
然后,停留了4s没有任何包的输出。
之后,每隔1s左右就会打印出一组:
01:52:54.359341 IP localhost.6666 > localhost.43101: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9858736], length 0
01:52:54.359377 IP localhost.43101 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9855887], length 0
其实这就是我们配置的策略:
'tcp_keepinterval' => 1, // 1s探测一次
'tcp_keepcount' => 5, // 探测的次数,超过5次后还没有回包close此连接
因为我们操作系统底层会自动的给客户端回ack,所以这个连接不会在5次探测后被关闭。操作系统底层会持续不断的发送这样的一组包:
01:52:54.359341 IP localhost.6666 > localhost.43101: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9858736], length 0
01:52:54.359377 IP localhost.43101 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9855887], length 0
如果我们要测试5次探测后关闭这个连接,可以禁掉6666端口的包:
iptables -A INPUT -p tcp --dport 6666 -j DROP
这样会把所有从6666端口进来的包给禁掉,自然,服务器就接收不到从客户端那一边发来的ack包了。
然后服务器过5秒就会打印出close(服务端主动的调用了close方法,给客户端发送了FIN包):
~/codeDir/phpCode/hyperf-skeleton # php server.php
string(17) "Client: Connect 1"
string(10) "close fd 1"
我们恢复一下iptables的规则:
~/codeDir/phpCode # iptables -D INPUT -p tcp -m tcp --dport 6666 -j DROP
即把我们设置的规则给删除了。
通过tcp_keepalive的方式实现心跳的功能,优点是简单,不要写代码就可以完成这个功能,并且发送的心跳包小。 缺点是依赖于系统的网络环境,必须保证服务器和客户端都实现了这样的功能,需要客户端配合发心跳包。 还有一个更为严重的缺点是如果客户端和服务器不是直连的,而是通过代理来进行连接的,例如socks5代理,它只会转发应用层的包,不会转发更为底层的tcp探测包,那这个心跳功能就失效了。
所以,Swoole就提供了其他的解决方案,一组检测死连接的配置。
'heartbeat_check_interval' => 1, // 1s探测一次
'heartbeat_idle_time' => 5, // 5s未发送数据包就close此连接
猜你喜欢
Laravel验证码
阅读 620Composer生成Laravel验证码
在 Laravel 中集成 Swoole 实现 WebSocket 服务器
阅读 2286基于 LaravelS 扩展包把 Swoole 集成到 Laravel 项目来实现 WebSocket 服务器,以便与客户端进行 WebSocket 通信从而实现广播功能。
黑客、后门
阅读 1425留下的网站后门,可以作什么?
Git使用
阅读 591Git基本配置/服务器搭建仓库
LaravelS基于Swoole实现高性能 HTTP 服务器
阅读 1629LaravelS基于Swoole 配置nginx等
PHP定时任务
阅读 1597PHP框架Laravel定时任务的实现
swoole 极简聊天室
阅读 1156五分钟教你写超简单的swoole聊天室
微擎常用记录
阅读 663微擎常用记录