PHP Swoole与TCP三次握手

握手常见问题

11、连接拒绝 22、Operation now in progress 3 多是因为丢包、错误ip、backlog满了&阻塞&tcp_abort_on_overflow=0 43、min(maxconn, backlog) ss -lt

连接拒绝

TCP三次握手的时候,客户端发送SYN这个包给服务端,服务端不接受这个请求,操作系统直接返回了一个RST的包,来拒绝连接的请求。

最常见的情况就是客户端去请求某个服务器,服务端没有绑定对应的端口。

测试代码如下,服务端代码:

1 1 <?php 2 23 3 $server = new \Swoole\Server('127.0.0.1', 9501); 4 45 5 $server->set([ 6 6 'work_num' => 2, 7 7 'backlog' => 128, 8 8 ]); 9 91010 $server->on('connect', function ($server, $fd) 1111 { 1212 echo "Client: Connect.\n"; 1313 }); 14141515 $server->on('receive', function ($server, $fd, $reactor_id, $data) 1616 { 1717 var_dump($data); 1818 }); 19192020 $server->on('close', function () 2121 { 2222 var_dump('close'); 2323 }); 24242525 $server->start();

这里,服务端绑定的端口是9501

启动服务器:

1 ~/codeDir/phpCode/hyperf-skeleton # php server.php 

客户端代码:

1<?php 23$client = new \Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); 4var_dump($client->connect('127.0.0.1', 9500));

这里,客户端请求的端口是9500

启动客户端:

1~/codeDir/phpCode/hyperf-skeleton # php client.php 23Warning: Swoole\Client::connect(): connect to server[127.0.0.1:9500] failed, Error: Connection refused[111] in /root/codeDir/phpCode/hyperf-skeleton/client.php on line 4 4bool(false) 5~/codeDir/phpCode/hyperf-skeleton #

我们发现,报错:

Error: Connection refused[111]

Operation now in progress

这个错误的绝大部分原因是因为连接超时了。

丢包

例如路由器、网关出现了故障,包被丢了。

错误ip

例如客户端请求了一个错误的ip,那么路由器自然也就路由不到。

测试代码如下,客户端代码:

1<?php 23$client = new \Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); 4var_dump($client->connect('8.8.8.8', 9501));

这里,我访问的是谷歌的DNS服务器。因为我没有翻墙,所以是访问不了这个IP的。因此,我们发送的包是到达不了8.8.8.8服务器的。

启动客户端:

1~/codeDir/phpCode/hyperf-skeleton # php client.php 23Warning: Swoole\Client::connect(): connect to server[8.8.8.8:9501] failed, Error: Operation in progress[115] in /root/codeDir/phpCode/hyperf-skeleton/client.php on line 4 4bool(false) 5~/codeDir/phpCode/hyperf-skeleton #

我们发现,报错:

Error: Operation in progress[115]

backlog

服务器在三次握手的最后一次,即收到客户端发来的ACK包的时候,会把建立好的连接放到backlog队列里面。如果Swoole一直不accept连接,那么这个backlog队列很快就会满。backlog队列满了之后,服务端就会丢弃三次握手的SYN包,让客户端重新去连接服务端。

测试代码如下,服务端代码:

1 1 <?php 2 23 3 $server = new \Swoole\Server('127.0.0.1', 9501, SWOOLE_BASE); 4 45 5 $server->set([ 6 6 'work_num' => 2, 7 7 'backlog' => 128, 8 8 ]); 9 91010 $server->on('connect', function ($server, $fd) 1111 { 1212 echo "Client: Connect.\n"; 1313 sleep(1000); 1414 }); 15151616 $server->on('receive', function ($server, $fd, $reactor_id, $data) 1717 { 1818 var_dump($data); 1919 }); 20202121 $server->on('close', function () 2222 { 2323 var_dump('close'); 2424 }); 25252626 $server->start();

要想测试**backlog问题必须在SwooleSWOOLE_BASE模式下,默认的SWOOLE_PROCESS**模式是没有这个问题的。

这里,我们的**backlog大小是128。**

然后,我们通过**sleep(1000);来阻塞住进程,使得Swoole不会继续accept连接,从而导致backlog**队列在某个时刻变满。

客户端代码:

1 1 <?php 2 23 3 $i = 0; 4 4 while (true) 5 5 { 6 6 $client = new \Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); 7 7 if ($client->connect('127.0.0.1', 9501) == false) 8 8 { 9 9 break; 1010 } 1111 }

我们启动服务器:

1~/codeDir/phpCode/hyperf-skeleton # php server.php 2

然后启动客户端:

1~/codeDir/phpCode/hyperf-skeleton # php client.php 2省略了其他的输出 3bool(true) 4bool(true) 5bool(true) 6bool(true) 7bool(true) 8bool(true) 9bool(true) 10bool(true) 11bool(true) 12bool(true) 13bool(true) 14bool(true) 15bool(true) 16bool(true) 17bool(true) 18bool(true) 19bool(true) 20bool(true) 21bool(true) 22bool(true) 23bool(true) 24bool(true) 25bool(true) 26bool(true) 27bool(true) 28bool(true) 29bool(true) 30bool(true) 31bool(true) 32bool(true) 33bool(true) 34bool(true) 35bool(true) 36bool(true) 3738Warning: Swoole\Client::connect(): connect to server[127.0.0.1:9501] failed, Error: Operation in progress[115] in /root/codeDir/phpCode/hyperf-skeleton/client.php on line 7 39bool(false) 4041Warning: Swoole\Client::connect(): connect to server[127.0.0.1:9501] failed, Error: Operation in progress[115] in /root/codeDir/phpCode/hyperf-skeleton/client.php on line 7 42bool(false) 43^C 44~/codeDir/phpCode/hyperf-skeleton #

我们会发现,过一段时间,客户端这边会报错:

Error: Operation in progress[115]

服务端这边输出:

1~/codeDir/phpCode/hyperf-skeleton # php server.php 2Client: Connect. 3

因为当Swoole服务器从backlog队列里面accept一个连接的时候,才会触发onReceive回调函数。所以,当服务端accept一个连接之后,Swoole自己就会陷入阻塞,不会再accept了。但是需要注意的是,尽管Swoole服务器自身是阻塞的,操作系统还会继续去把建立好的连接放入backlog队列里面。所以,backlog队列会满。

SYN Flood

除了三次握手成功之后会使用到的backlog队列,还有一个SYN队列。也就是在三次握手时候,客户端给服务端发送了SYN包,服务端会有一个SYN队列来维护。

与其有关的内核配置:

1tcp_max_syn_backlog 2tcp_synack_retries 3tcp_syncookies

其中,tcp_max_syn_backlog就是这个SYN队列的长度。如果大量的SYN包把SYN队列塞满了,那么其他正常的连接过来,服务端就无法处理。

SYN Flood攻击就是客户端疯狂的给服务端发送SYN包,然后服务端每次都会把请求放到SYN队列里面。但是,客户端不给服务端回ACK包。如果客户端不回ACK包,那么服务端就会给客户端回SYN + ACK包,即第二次握手发送的包。而回复SYN + ACK包的次数就是由tcp_synack_retries参数决定的。如果把tcp_synack_retries设置为0,那么如果服务端没有收到ACK包,那么服务端就不会重试发送SYN + ACK包了,这样就减少了SYN队列里面那个请求的存活时间。

tcp_syncookies的原理就是,客户端发送SYN包的时候,不会维护SYN队列,而是返回一个cookie给客户端。然后客户端发送第三次握手的时候,携带这个cookie值,只有这个cookie验证通过,服务端才会给连接分配资源。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

HTTP面试题精选_langsiming的博客

1.简述下TCP三次握手的过程,并解释采用3次握手建立连接的原因?1.客户端发送建立连接请求,携带信息syn1,seq X 第一次握手2.

tcp的三次握手四次挥手

tcp的三次握手流程:在tcp/ip协议中,tcp协议提供可靠的连接服务,采用三次握手建立一个连接。第一次握手:建立连接时,客户端发送SYN包【synj】到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到SYN包,必须确认客户的SYN(ackj1),同时自己也发送一个SYN包(syn