您好,登錄后才能下訂單哦!
本篇內容介紹了“PHP+Socket中IO多路復用及實現web服務器的方法是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
多路復用
前文 通過原生 socket 實現了簡單的服務端與客戶端通信,但當有多個客戶端連接時,服務端僅能處理第一個客戶端的請求,而無法對后續客戶端服務
產生這種情況的原因是因為IO模型是阻塞的,同一時刻只能由一個客戶端進行訪問,解決此問題主要有兩種解決方案:
多進程,即在服務端啟動多個進程監聽
IO多路復用機制,簡單來說實現了 N 個客戶端使用一根網線同時訪問
同時多路復用又分為兩個不同的模型,即 select
與 epoll
,常見的軟件中,Apache
使用了 select
模型,nginx
則使用 epoll
模型。在 php 中內置了 select
模型,對應的函數為 socket_select
,多路復用是實現 http 服務器的基礎
在前文中我們介紹了 php 原生 socket 內置了 socket_select
函數實現了 select
模型,其語法如下:
socket_select(
array &$read,
array &$write,
array &$except,
int $seconds [,
int $microseconds = 0]): int|false
read
服務端監聽的套接字資源,當他有變化(即收到新的消息或有客戶端連接、斷開)時,socket_select
函數才會返回(否則繼續阻塞),同時修改該變量為當前發生事件(收到消息或有客戶端連接、斷開)的套接字資源列表,并繼續向下執行。
write
監聽是否有客戶端寫數據,傳入 null
則代表不關心是否有寫變化
except
套接字內要排除的元素,傳入 null
是 「監聽」 全部
seconds
秒和微秒一起構成超時參數。如果傳入 null
則會阻塞,為 0 非阻塞,如果是 >0 則為最大阻塞時間
microseconds
我們在 上篇文章 簡單實現了 socket 服務端監聽與客戶端的連接,接下來我們在服務端監聽代碼的基礎上通過多路復用優化代碼:
<?php
// 創建套接字
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 設置 ip 被釋放后立即可使用
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
// 綁定ip與端口
socket_bind($socket, 0, 8888);
// 開始監聽
socket_listen($socket);
$sockets[] = $socket;
while (true) {
$tmp_sockets = $sockets;
socket_select($tmp_sockets, $write, $except, null);
foreach ($tmp_sockets as $sock) {
// 如果當前套接字等于 socket_create 創建的套接字,說明是有新的連接或有新的斷開連接
if ($sock == $socket) {
$conn_sock = socket_accept($socket);
$sockets[] = $conn_sock;
socket_getpeername($conn_sock, $ip, $port);
echo '請求ip: ' . $ip . '端口: ' . $port . PHP_EOL;
} else { // 否則說明是之前連接的客戶端發來消息
$msg = socket_read($sock, 10240);
socket_write($sock, strtoupper($msg));
echo $msg;
}
}
}
在本示例中 socket_select
函數會阻塞當前進程,當 $tmp_sockets
數組內的 socket 資源有新的客戶端連接或斷開或收到新消息時,會將 $tmp_sockets
數組修改為當前活躍的 socket 資源,隨后通過遍歷該數組處理業務邏輯。
使用socket實現簡易http服務器
http 協議是在 socket 的基礎上規定了指定的數據格式,所以我們只需在 socket_write
時按照格式發送數據,瀏覽器就可正常響應請求
<?php
// 創建套接字
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 設置 ip 被釋放后立即可使用
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
// 綁定ip與端口
socket_bind($socket, 0, 8888);
// 開始監聽
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);
}
}
}
在服務端運行此示例,隨后在瀏覽器訪問 ip:8888
,可以看到如下:
同時服務端會輸出如下內容:
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
該內容即為用戶端請求原始數據,可解析此數據并根據請求做出響應,比如使用 file_get_content
讀取指定文件內容返回給瀏覽器。
“PHP+Socket中IO多路復用及實現web服務器的方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。