1. 程式人生 > 程式設計 >詳解PHP Swoole與TCP三次握手

詳解PHP Swoole與TCP三次握手

握手常見問題

1、連線拒絕

2、Operation now in progress 多是因為丟包、錯誤ip、backlog滿了&阻塞&tcp_abort_on_overflow=0

3、min(maxconn,backlog) ss -lt

連線拒絕

在TCP三次握手的時候,客戶端傳送SYN這個包給服務端,服務端不接受這個請求,作業系統直接返回了一個RST的包,來拒絕連線的請求。

最常見的情況就是客戶端去請求某個伺服器,服務端沒有繫結對應的埠。

測試程式碼如下,服務端程式碼:

<?php
程式設計客棧203;
$s程式設計客棧erver = new \Swoole\Server('127.0.0.1',9501);
​
$server->set([
    'work_num' => 2,'backlog' => 128,]);
​
$server->on('connect',function ($server,$fd)
{
    echo "Client: Connect.\n";
});
​
$server->on('receive',$fd,$reactor_id,$data)
{
    var_dump($data);
});
​
$server->on('close',function ()
{
    var_dump('close');
});
​
$server->start();

這裡,服務端繫結的埠是9501。

啟動伺服器:

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

客戶端程式碼:

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

這裡,客戶端請求的埠是9500。

啟動客戶端:

~/codeDir/phpCode/hyperf-skeleton # php client.php 
​
Warning: 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
bool(false)
~/codeDir/phpCode/hyperf-skeleton #

報錯:

Error: Connection refused[www.cppcns.com111]

Operation now in progress

這個錯誤的絕大部分原因是因為連線超時了。

丟包

例如路由器、網關出現了故障,包被丟了。

錯誤ip

例如客戶端請求了一個錯誤的ip,那麼路由器自然也就路由不到。

測試程式碼如下,客戶端程式碼:

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

這裡,我訪問的是谷歌的DNS伺服器。因為我沒有FQ,所以是訪問不了這個IP的。因此,我們傳送的包是到達不了8.8.8.8伺服器的。

啟動客戶端:

~/codeDir/phpCode/hyperf-skeleton # php client.php 
​
Warning: 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
bool(false)
~/codeDir/phpCode/hyperf-skeleton #

報錯:

Error: Operation in progress[115]

backlog

伺服器在三次握手的最後一次,即收到客戶端發來的ACK包的時候,會把建立好的連線放到backlog佇列裡面。如果Swoole一直不accept連線,那麼這個backlog佇列很快就會滿。backlog佇列滿了之後,服務端就會丟棄三次握手的SYN包,讓客戶端重新去連線服務端。

測試程式碼如下,服務端程式碼:

<?php
​
$server = new \Swoole\Server('127.0.0.1',9501,SWOOLE_BASE);
​
$server->set([
    'work_num' => 2,$fd)
{
    echo "Client: Connect.\n";
    sleep(1000);
});
​
$server->on('receive',function ()
{
    var_dump('close');
});
​
$server->start();

要想測試backlog問題必須在Swoole的SWOOLE_BASE模式下,預設的SWOOLE_PROCESS模式是沒有這個問題的。

這裡,我們的backlog大小是128。

然後,我們通過sleep(1000);來阻塞住程序,使得Swoole不會繼續accept連線,從而導致backlog佇列在某個時刻變滿。

客戶端程式碼:

<?php
​
$i = 0;
while (trreZcEDue)
{
    $client = new \Swoole\Client(SWOOLE_SOCK_TCP,SWOOLE_SOCK_SYNC);
    if ($client->connect('127.0.0.1',9501) == false)
    {
        break;
    }
}

我們啟動伺服器:

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

然後啟動客戶端:

~/codeDir/phpCode/hyperf-skeleton # php client.php 
省略了其他的輸出
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
​
Warning: 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
bool(false)
​
Warning: 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
bool(false)
^C
~/codeDir/phpCode/hyperf-skeleton #

我們會發現,過一段時間,客戶端這邊會報錯:

Error: Operation in progress[115]

服務端這邊輸出:

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

Client: Connect.

因為當Swoole伺服器從backlog佇列裡面accept一個連線的時候,才會觸發onReceive回撥函式。所以,當服務端accept一個連線之後,Swoole自己就會陷入阻塞,不會再accept了。但是需要注意的是,儘管Swoole伺服器自身是阻塞的,作業系統還會繼續去把建立好的連線放入backlog佇列裡面。所以,backlog佇列會滿。

SYN Flood

除了三次握手成功之後會使用到的backlog佇列,還有一個SYN佇列。也就是在三次握手時候,客戶端給服務端傳送了SYN包,服務端會有一個SYN佇列來維護。

與其有關的核心配置:

tcp_max_syn_backlog
tcp_synack_retries
tcp_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驗證通過,服務端才會給連線分配資源。

以上就是詳解PHP Swoole與TCP三次握手的詳細內容,更多關於PHP Swoole與TCP三次握手的資料請關注我們其它相關文章!