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此连接
猜你喜欢
Git使用
阅读 669Git基本配置/服务器搭建仓库
LaravelS基于Swoole实现高性能 HTTP 服务器
阅读 1777LaravelS基于Swoole 配置nginx等
基于 Swoole 实现简单的 WebSocket 服务器及客户端
阅读 1319基于 Swoole 实现简单的 WebSocket 服务器及客户端
在 Laravel 中集成 Swoole 实现 WebSocket 服务器
阅读 2458基于 LaravelS 扩展包把 Swoole 集成到 Laravel 项目来实现 WebSocket 服务器,以便与客户端进行 WebSocket 通信从而实现广播功能。
黑客、后门
阅读 1572留下的网站后门,可以作什么?
Layui富文本视频等多功能
阅读 702Layui富文本多功能添加
Resultful API规范
阅读 811什么是resultful
PHP定时任务
阅读 1730PHP框架Laravel定时任务的实现