您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“PHP+Socket中IO多路復(fù)用及實(shí)現(xiàn)web服務(wù)器的方法是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
多路復(fù)用
前文 通過(guò)原生 socket 實(shí)現(xiàn)了簡(jiǎn)單的服務(wù)端與客戶端通信,但當(dāng)有多個(gè)客戶端連接時(shí),服務(wù)端僅能處理第一個(gè)客戶端的請(qǐng)求,而無(wú)法對(duì)后續(xù)客戶端服務(wù)
產(chǎn)生這種情況的原因是因?yàn)镮O模型是阻塞的,同一時(shí)刻只能由一個(gè)客戶端進(jìn)行訪問(wèn),解決此問(wèn)題主要有兩種解決方案:
多進(jìn)程,即在服務(wù)端啟動(dòng)多個(gè)進(jìn)程監(jiān)聽
IO多路復(fù)用機(jī)制,簡(jiǎn)單來(lái)說(shuō)實(shí)現(xiàn)了 N 個(gè)客戶端使用一根網(wǎng)線同時(shí)訪問(wèn)
同時(shí)多路復(fù)用又分為兩個(gè)不同的模型,即 select
與 epoll
,常見的軟件中,Apache
使用了 select
模型,nginx
則使用 epoll
模型。在 php 中內(nèi)置了 select
模型,對(duì)應(yīng)的函數(shù)為 socket_select
,多路復(fù)用是實(shí)現(xiàn) http 服務(wù)器的基礎(chǔ)
在前文中我們介紹了 php 原生 socket 內(nèi)置了 socket_select
函數(shù)實(shí)現(xiàn)了 select
模型,其語(yǔ)法如下:
socket_select(
array &$read,
array &$write,
array &$except,
int $seconds [,
int $microseconds = 0]): int|false
read
服務(wù)端監(jiān)聽的套接字資源,當(dāng)他有變化(即收到新的消息或有客戶端連接、斷開)時(shí),socket_select
函數(shù)才會(huì)返回(否則繼續(xù)阻塞),同時(shí)修改該變量為當(dāng)前發(fā)生事件(收到消息或有客戶端連接、斷開)的套接字資源列表,并繼續(xù)向下執(zhí)行。
write
監(jiān)聽是否有客戶端寫數(shù)據(jù),傳入 null
則代表不關(guān)心是否有寫變化
except
套接字內(nèi)要排除的元素,傳入 null
是 「監(jiān)聽」 全部
seconds
秒和微秒一起構(gòu)成超時(shí)參數(shù)。如果傳入 null
則會(huì)阻塞,為 0 非阻塞,如果是 >0 則為最大阻塞時(shí)間
microseconds
我們?cè)?上篇文章 簡(jiǎn)單實(shí)現(xiàn)了 socket 服務(wù)端監(jiān)聽與客戶端的連接,接下來(lái)我們?cè)诜?wù)端監(jiān)聽代碼的基礎(chǔ)上通過(guò)多路復(fù)用優(yōu)化代碼:
<?php
// 創(chuàng)建套接字
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 設(shè)置 ip 被釋放后立即可使用
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
// 綁定ip與端口
socket_bind($socket, 0, 8888);
// 開始監(jiān)聽
socket_listen($socket);
$sockets[] = $socket;
while (true) {
$tmp_sockets = $sockets;
socket_select($tmp_sockets, $write, $except, null);
foreach ($tmp_sockets as $sock) {
// 如果當(dāng)前套接字等于 socket_create 創(chuàng)建的套接字,說(shuō)明是有新的連接或有新的斷開連接
if ($sock == $socket) {
$conn_sock = socket_accept($socket);
$sockets[] = $conn_sock;
socket_getpeername($conn_sock, $ip, $port);
echo '請(qǐng)求ip: ' . $ip . '端口: ' . $port . PHP_EOL;
} else { // 否則說(shuō)明是之前連接的客戶端發(fā)來(lái)消息
$msg = socket_read($sock, 10240);
socket_write($sock, strtoupper($msg));
echo $msg;
}
}
}
在本示例中 socket_select
函數(shù)會(huì)阻塞當(dāng)前進(jìn)程,當(dāng) $tmp_sockets
數(shù)組內(nèi)的 socket 資源有新的客戶端連接或斷開或收到新消息時(shí),會(huì)將 $tmp_sockets
數(shù)組修改為當(dāng)前活躍的 socket 資源,隨后通過(guò)遍歷該數(shù)組處理業(yè)務(wù)邏輯。
使用socket實(shí)現(xiàn)簡(jiǎn)易http服務(wù)器
http 協(xié)議是在 socket 的基礎(chǔ)上規(guī)定了指定的數(shù)據(jù)格式,所以我們只需在 socket_write
時(shí)按照格式發(fā)送數(shù)據(jù),瀏覽器就可正常響應(yīng)請(qǐng)求
<?php
// 創(chuàng)建套接字
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 設(shè)置 ip 被釋放后立即可使用
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
// 綁定ip與端口
socket_bind($socket, 0, 8888);
// 開始監(jiān)聽
socket_listen($socket);
$sockets[] = $socket;
while (true) {
$tmp_sockets = $sockets;
socket_select($tmp_sockets, $write, $except, null);
foreach ($tmp_sockets as $sock) {
if ($sock == $socket) {
$conn_sock = socket_accept($socket);
$sockets[] = $conn_sock;
} else {
$msg = socket_read($sock, 10240);
var_dump($msg);
if ($msg == '') return;
$output = '<h2>this is php worker</h2>';
$len = strlen($output);
$response = "HTTP/1.1 200 OK\r\n";
$response .= "content-type: text/html\r\n";
$response .= "server: php socket\r\n";
$response .= "Content-Length: {$len}\r\n\r\n";
$response .= $output;
socket_write($sock, $response);
}
}
}
在服務(wù)端運(yùn)行此示例,隨后在瀏覽器訪問(wèn) ip:8888
,可以看到如下:
同時(shí)服務(wù)端會(huì)輸出如下內(nèi)容:
GET / HTTP/1.1
Host: 124.222.**.**:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: jenkins-timestamper-offset=-28800000; _ga=GA1.1.1403944751.1652010033; _ga_2GM6102E19=GS1.1.1652802985.7.1.1652803014.0
該內(nèi)容即為用戶端請(qǐng)求原始數(shù)據(jù),可解析此數(shù)據(jù)并根據(jù)請(qǐng)求做出響應(yīng),比如使用 file_get_content
讀取指定文件內(nèi)容返回給瀏覽器。
“PHP+Socket中IO多路復(fù)用及實(shí)現(xiàn)web服務(wù)器的方法是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。