詳解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è)請求,操作系統(tǒng)直接返回了一個(gè)RST的包,來拒絕連接的請求。
最常見的情況就是客戶端去請求某個(gè)服務(wù)器,服務(wù)端沒有綁定對應(yīng)的端口。
測試代碼如下,服務(wù)端代碼:
<?php
$server = 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', 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 \Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
var_dump($client->connect('127.0.0.1', 9500));
這里,客戶端請求的端口是9500。
啟動(dòng)客戶端:
~/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 #
報(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
例如客戶端請求了一個(gè)錯(cuò)誤的ip,那么路由器自然也就路由不到。
測試代碼如下,客戶端代碼:
<?php
$client = new \Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
var_dump($client->connect('8.8.8.8', 9501));
這里,我訪問的是谷歌的DNS服務(wù)器。因?yàn)槲覜]有FQ,所以是訪問不了這個(gè)IP的。因此,我們發(fā)送的包是到達(dá)不了8.8.8.8服務(wù)器的。
啟動(dòng)客戶端:
~/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 #
報(bào)錯(cuò):
Error: Operation in progress[115]
backlog
服務(wù)器在三次握手的最后一次,即收到客戶端發(fā)來的ACK包的時(shí)候,會把建立好的連接放到backlog隊(duì)列里面。如果Swoole一直不accept連接,那么這個(gè)backlog隊(duì)列很快就會滿。backlog隊(duì)列滿了之后,服務(wù)端就會丟棄三次握手的SYN包,讓客戶端重新去連接服務(wù)端。
測試代碼如下,服務(wù)端代碼:
<?php
$server = new \Swoole\Server('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模式下,默認(rèn)的SWOOLE_PROCESS模式是沒有這個(gè)問題的。
這里,我們的backlog大小是128。
然后,我們通過sleep(1000);來阻塞住進(jìn)程,使得Swoole不會繼續(xù)accept連接,從而導(dǎo)致backlog隊(duì)列在某個(gè)時(shí)刻變滿。
客戶端代碼:
<?php
$i = 0;
while (true)
{
$client = new \Swoole\Client(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: 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 #
我們會發(fā)現(xiàn),過一段時(shí)間,客戶端這邊會報(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í)候,才會觸發(fā)onReceive回調(diào)函數(shù)。所以,當(dāng)服務(wù)端accept一個(gè)連接之后,Swoole自己就會陷入阻塞,不會再accept了。但是需要注意的是,盡管Swoole服務(wù)器自身是阻塞的,操作系統(tǒng)還會繼續(xù)去把建立好的連接放入backlog隊(duì)列里面。所以,backlog隊(duì)列會滿。
SYN Flood
除了三次握手成功之后會使用到的backlog隊(duì)列,還有一個(gè)SYN隊(duì)列。也就是在三次握手時(shí)候,客戶端給服務(wù)端發(fā)送了SYN包,服務(wù)端會有一個(gè)SYN隊(duì)列來維護(hù)。
與其有關(guān)的內(nèi)核配置:
tcp_max_syn_backlog tcp_synack_retries tcp_syncookies
其中,tcp_max_syn_backlog就是這個(gè)SYN隊(duì)列的長度。如果大量的SYN包把SYN隊(duì)列塞滿了,那么其他正常的連接過來,服務(wù)端就無法處理。
SYN Flood攻擊就是客戶端瘋狂的給服務(wù)端發(fā)送SYN包,然后服務(wù)端每次都會把請求放到SYN隊(duì)列里面。但是,客戶端不給服務(wù)端回ACK包。如果客戶端不回ACK包,那么服務(wù)端就會給客戶端回SYN + ACK包,即第二次握手發(fā)送的包。而回復(fù)SYN + ACK包的次數(shù)就是由tcp_synack_retries參數(shù)決定的。如果把tcp_synack_retries設(shè)置為0,那么如果服務(wù)端沒有收到ACK包,那么服務(wù)端就不會重試發(fā)送SYN + ACK包了,這樣就減少了SYN隊(duì)列里面那個(gè)請求的存活時(shí)間。
tcp_syncookies的原理就是,客戶端發(fā)送SYN包的時(shí)候,不會維護(hù)SYN隊(duì)列,而是返回一個(gè)cookie給客戶端。然后客戶端發(fā)送第三次握手的時(shí)候,攜帶這個(gè)cookie值,只有這個(gè)cookie驗(yàn)證通過,服務(wù)端才會給連接分配資源。
以上就是詳解PHP Swoole與TCP三次握手的詳細(xì)內(nèi)容,更多關(guān)于PHP Swoole與TCP三次握手的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
php常用Output和ptions/Info函數(shù)集介紹
本篇文章是對php中常用Output和ptions/Info函數(shù)集進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
解析php函數(shù)method_exists()與is_callable()的區(qū)別
本篇文章是對php中method_exists()與is_callable()的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
PHP框架Laravel插件Pagination實(shí)現(xiàn)自定義分頁
這篇文章主要為大家詳細(xì)介紹了PHP框架Laravel5.1插件Pagination實(shí)現(xiàn)自定義分頁的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05
PHP設(shè)計(jì)模式之簡單工廠和工廠模式實(shí)例分析
這篇文章主要介紹了PHP設(shè)計(jì)模式之簡單工廠和工廠模式,結(jié)合實(shí)例形式分析了php設(shè)計(jì)模式中工廠模式的實(shí)現(xiàn)方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-03-03
比file_get_contents穩(wěn)定的curl_get_contents分享
相信使用過file_get_contents函數(shù)的朋友都知道,當(dāng)獲取的$url訪問不了時(shí),會導(dǎo)致頁面漫長的等待,甚至還能導(dǎo)致PHP進(jìn)程占用CPU達(dá)100%,因此這個(gè)函數(shù)就誕生了2012-01-01
PHP實(shí)現(xiàn)可精確驗(yàn)證身份證號碼的工具類示例
這篇文章主要介紹了PHP實(shí)現(xiàn)可精確驗(yàn)證身份證號碼的工具類,涉及php針對字符串的截取、計(jì)算、判斷等相關(guān)操作技巧,并附帶說明了身份證號碼的原理,需要的朋友可以參考下2018-05-05

