詳解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$server = new SwooleServer(’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’, function ($server, $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 SwooleClient(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);var_dump($client->connect(’127.0.0.1’, 9500));
這里,客戶端請求的端口是9500。
啟動客戶端:
~/codeDir/phpCode/hyperf-skeleton # php client.php Warning: SwooleClient::connect(): connect to server[127.0.0.1:9500] failed, Error: Connection refused[111] in /root/codeDir/phpCode/hyperf-skeleton/client.php on line 4bool(false)~/codeDir/phpCode/hyperf-skeleton #
報錯:
Error: Connection refused[111]
Operation now in progress這個錯誤的絕大部分原因是因為連接超時了。
丟包例如路由器、網關出現了故障,包被丟了。
錯誤ip例如客戶端請求了一個錯誤的ip,那么路由器自然也就路由不到。
測試代碼如下,客戶端代碼:
<?php$client = new SwooleClient(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: SwooleClient::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 4bool(false)~/codeDir/phpCode/hyperf-skeleton #
報錯:
Error: Operation in progress[115]
backlog服務器在三次握手的最后一次,即收到客戶端發來的ACK包的時候,會把建立好的連接放到backlog隊列里面。如果Swoole一直不accept連接,那么這個backlog隊列很快就會滿。backlog隊列滿了之后,服務端就會丟棄三次握手的SYN包,讓客戶端重新去連接服務端。
測試代碼如下,服務端代碼:
<?php$server = new SwooleServer(’127.0.0.1’, 9501, SWOOLE_BASE);$server->set([ ’work_num’ => 2, ’backlog’ => 128,]);$server->on(’connect’, function ($server, $fd){ echo 'Client: Connect.n'; sleep(1000);});$server->on(’receive’, function ($server, $fd, $reactor_id, $data){ var_dump($data);});$server->on(’close’, function (){ var_dump(’close’);});$server->start();
要想測試backlog問題必須在Swoole的SWOOLE_BASE模式下,默認的SWOOLE_PROCESS模式是沒有這個問題的。
這里,我們的backlog大小是128。
然后,我們通過sleep(1000);來阻塞住進程,使得Swoole不會繼續accept連接,從而導致backlog隊列在某個時刻變滿。
客戶端代碼:
<?php$i = 0;while (true){ $client = new SwooleClient(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: SwooleClient::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 7bool(false)Warning: SwooleClient::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 7bool(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_backlogtcp_synack_retriestcp_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三次握手的資料請關注好吧啦網其它相關文章!
相關文章:
