溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

PHP+Socket中IO多路復(fù)用及實(shí)現(xiàn)web服務(wù)器的方法是什么

發(fā)布時(shí)間:2023-02-02 14:34:10 來(lái)源:億速云 閱讀:98 作者:iii 欄目:編程語(yǔ)言

本篇內(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有所成!

php原生socket之IO多路復(fù)用以及實(shí)現(xiàn)web服務(wù)器

多路復(fù)用

前文 通過(guò)原生 socket 實(shí)現(xiàn)了簡(jiǎn)單的服務(wù)端與客戶端通信,但當(dāng)有多個(gè)客戶端連接時(shí),服務(wù)端僅能處理第一個(gè)客戶端的請(qǐng)求,而無(wú)法對(duì)后續(xù)客戶端服務(wù)

PHP+Socket中IO多路復(fù)用及實(shí)現(xiàn)web服務(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è)不同的模型,即 selectepoll,常見的軟件中,Apache 使用了 select 模型,nginx 則使用 epoll 模型。在 php 中內(nèi)置了 select 模型,對(duì)應(yīng)的函數(shù)為 socket_select,多路復(fù)用是實(shí)現(xiàn) http 服務(wù)器的基礎(chǔ)

語(yǔ)法

在前文中我們介紹了 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

參數(shù)
  • 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

優(yōu)化

我們?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ù)邏輯。

PHP+Socket中IO多路復(fù)用及實(shí)現(xiàn)web服務(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 ,可以看到如下:

PHP+Socket中IO多路復(fù)用及實(shí)現(xiàn)web服務(wù)器的方法是什么

同時(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í)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI