詳解PHP Swoole與TCP三次握手
1、連接拒絕
2、Operation now in progress 多是因?yàn)閬G包、錯(cuò)誤ip、backlog滿了&阻塞&tcp_abort_on_overflow=0
3、min(maxconn, backlog) ss -lt
連接拒絕在TCP三次握手的時(shí)候,客戶端發(fā)送SYN這個(gè)包給服務(wù)端,服務(wù)端不接受這個(gè)請(qǐng)求,操作系統(tǒng)直接返回了一個(gè)RST的包,來(lái)拒絕連接的請(qǐng)求。
最常見(jiàn)的情況就是客戶端去請(qǐng)求某個(gè)服務(wù)器,服務(wù)端沒(méi)有綁定對(duì)應(yīng)的端口。
測(cè)試代碼如下,服務(wù)端代碼:
<?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();
這里,服務(wù)端綁定的端口是9501。
啟動(dòng)服務(wù)器:
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));
這里,客戶端請(qǐng)求的端口是9500。
啟動(dòng)客戶端:
~/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 #
報(bào)錯(cuò):
Error: Connection refused[111]
Operation now in progress這個(gè)錯(cuò)誤的絕大部分原因是因?yàn)檫B接超時(shí)了。
丟包例如路由器、網(wǎng)關(guān)出現(xiàn)了故障,包被丟了。
錯(cuò)誤ip例如客戶端請(qǐng)求了一個(gè)錯(cuò)誤的ip,那么路由器自然也就路由不到。
測(cè)試代碼如下,客戶端代碼:
<?php$client = new SwooleClient(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);var_dump($client->connect(’8.8.8.8’, 9501));
這里,我訪問(wèn)的是谷歌的DNS服務(wù)器。因?yàn)槲覜](méi)有FQ,所以是訪問(wèn)不了這個(gè)IP的。因此,我們發(fā)送的包是到達(dá)不了8.8.8.8服務(wù)器的。
啟動(dòng)客戶端:
~/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 #
報(bào)錯(cuò):
Error: Operation in progress[115]
backlog服務(wù)器在三次握手的最后一次,即收到客戶端發(fā)來(lái)的ACK包的時(shí)候,會(huì)把建立好的連接放到backlog隊(duì)列里面。如果Swoole一直不accept連接,那么這個(gè)backlog隊(duì)列很快就會(huì)滿。backlog隊(duì)列滿了之后,服務(wù)端就會(huì)丟棄三次握手的SYN包,讓客戶端重新去連接服務(wù)端。
測(cè)試代碼如下,服務(wù)端代碼:
<?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();
要想測(cè)試backlog問(wèn)題必須在Swoole的SWOOLE_BASE模式下,默認(rèn)的SWOOLE_PROCESS模式是沒(méi)有這個(gè)問(wèn)題的。
這里,我們的backlog大小是128。
然后,我們通過(guò)sleep(1000);來(lái)阻塞住進(jìn)程,使得Swoole不會(huì)繼續(xù)accept連接,從而導(dǎo)致backlog隊(duì)列在某個(gè)時(shí)刻變滿。
客戶端代碼:
<?php$i = 0;while (true){ $client = new SwooleClient(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); if ($client->connect(’127.0.0.1’, 9501) == false) {break; }}
我們啟動(dòng)服務(wù)器:
~/codeDir/phpCode/hyperf-skeleton # php server.php
然后啟動(dòng)客戶端:
~/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 #
我們會(huì)發(fā)現(xiàn),過(guò)一段時(shí)間,客戶端這邊會(huì)報(bào)錯(cuò):
Error: Operation in progress[115]
服務(wù)端這邊輸出:
~/codeDir/phpCode/hyperf-skeleton # php server.php
Client: Connect.
因?yàn)楫?dāng)Swoole服務(wù)器從backlog隊(duì)列里面accept一個(gè)連接的時(shí)候,才會(huì)觸發(fā)onReceive回調(diào)函數(shù)。所以,當(dāng)服務(wù)端accept一個(gè)連接之后,Swoole自己就會(huì)陷入阻塞,不會(huì)再accept了。但是需要注意的是,盡管Swoole服務(wù)器自身是阻塞的,操作系統(tǒng)還會(huì)繼續(xù)去把建立好的連接放入backlog隊(duì)列里面。所以,backlog隊(duì)列會(huì)滿。
SYN Flood除了三次握手成功之后會(huì)使用到的backlog隊(duì)列,還有一個(gè)SYN隊(duì)列。也就是在三次握手時(shí)候,客戶端給服務(wù)端發(fā)送了SYN包,服務(wù)端會(huì)有一個(gè)SYN隊(duì)列來(lái)維護(hù)。
與其有關(guān)的內(nèi)核配置:
tcp_max_syn_backlogtcp_synack_retriestcp_syncookies
其中,tcp_max_syn_backlog就是這個(gè)SYN隊(duì)列的長(zhǎng)度。如果大量的SYN包把SYN隊(duì)列塞滿了,那么其他正常的連接過(guò)來(lái),服務(wù)端就無(wú)法處理。
SYN Flood攻擊就是客戶端瘋狂的給服務(wù)端發(fā)送SYN包,然后服務(wù)端每次都會(huì)把請(qǐng)求放到SYN隊(duì)列里面。但是,客戶端不給服務(wù)端回ACK包。如果客戶端不回ACK包,那么服務(wù)端就會(huì)給客戶端回SYN + ACK包,即第二次握手發(fā)送的包。而回復(fù)SYN + ACK包的次數(shù)就是由tcp_synack_retries參數(shù)決定的。如果把tcp_synack_retries設(shè)置為0,那么如果服務(wù)端沒(méi)有收到ACK包,那么服務(wù)端就不會(huì)重試發(fā)送SYN + ACK包了,這樣就減少了SYN隊(duì)列里面那個(gè)請(qǐng)求的存活時(shí)間。
tcp_syncookies的原理就是,客戶端發(fā)送SYN包的時(shí)候,不會(huì)維護(hù)SYN隊(duì)列,而是返回一個(gè)cookie給客戶端。然后客戶端發(fā)送第三次握手的時(shí)候,攜帶這個(gè)cookie值,只有這個(gè)cookie驗(yàn)證通過(guò),服務(wù)端才會(huì)給連接分配資源。
以上就是詳解PHP Swoole與TCP三次握手的詳細(xì)內(nèi)容,更多關(guān)于PHP Swoole與TCP三次握手的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. Python如何實(shí)現(xiàn)感知器的邏輯電路2. JS實(shí)現(xiàn)表單中點(diǎn)擊小眼睛顯示隱藏密碼框中的密碼3. JS錯(cuò)誤處理與調(diào)試操作實(shí)例分析4. asp讀取xml文件和記數(shù)5. python基于scrapy爬取京東筆記本電腦數(shù)據(jù)并進(jìn)行簡(jiǎn)單處理和分析6. 原生js實(shí)現(xiàn)的觀察者和訂閱者模式簡(jiǎn)單示例7. Python ellipsis 的用法詳解8. 在終端啟動(dòng)Python時(shí)報(bào)錯(cuò)的解決方案9. vue 驗(yàn)證兩次輸入的密碼是否一致的方法示例10. xml中的空格之完全解說(shuō)
