玩轉 PHP 網路程式設計全套阻塞與非阻塞 IO

勺顛顛發表於2020-04-25

扯蛋

上一篇我們擼了不咋樣的TCP server,然後還扯了半天的口水,現在呢我們來繼續擼客戶端

TCP Server

$ip = "0.0.0.0";
$port = $argv[1];
$sockefd = socket_create(AF_INET,SOCK_STREAM,0);
socket_bind($sockefd,$ip,$port);
socket_listen($sockefd,5);
while (1){
    $connfd = socket_accept($sockefd);
    if ($connfd){
        socket_write($connfd,"hello,php 是世界上是好的語言");
        while (1){
 if(($recv=socket_read($connfd,4098,PHP_BINARY_READ))){
                socket_write($connfd,"server:$recv");
            }
        }
    }

}
socket_close($sockefd);
socket_close($connfd);

TCP Client

$ip = $argv[1];
$port = $argv[2];
$sockfd = socket_create(AF_INET,SOCK_STREAM,0);
socket_connect($sockfd,$ip,$port);
while (1){
    if(($recv = socket_read($sockfd,4098))){
        echo $recv;
    }
    $data = fread(STDIN,2014);
    if ($data){
        socket_write($sockfd,$data,strlen($data));
    }
}
socket_close($sockfd);

現在我們來分別執行server/client來簡單的通訊

玩轉 PHP 網路程式設計全套之二客戶端
ok,以上通訊沒問題

現在用telent工具來折騰下它

啟動服務時
我們看到客戶端與伺服器端是可以正常來往通訊的,我們看到伺服器執行時它是處於accept阻塞程式的。
連線服務時
玩轉 PHP 網路程式設計全套之二客戶端
客戶連線時,accept立馬執行【程式被喚醒】同時返回一個TCP連線,並且這個連線通過為5,程式就執行到socket_read處阻塞了,同時我們列出/proc/PID/fd時生成了如下檔案
玩轉 PHP 網路程式設計全套之二客戶端
當客戶傳送字串”hello”時socket_read函式執行並讀取到內容及讀取的資料長度
玩轉 PHP 網路程式設計全套之二客戶端

以上說了半天,就是在告訴你
socket_accept,socket_read,socket_write這些函式是阻塞IO!!!

阻塞IO與非阻塞IO

我覺得光解釋不行的,你得擼上程式碼去體驗
阻塞IO【阻塞的檔案描述符】:
執行系統呼叫【讀寫請求】時不會立即返回,需要等待就緒事件【讀寫事件】發生,作業系統會讓程式掛起來【SLEEPING】
非阻塞IO:
執行系統呼叫後,程式會立即返回,如果事件沒有發生會返回-1,同時會帶有出錯資訊如EAGAIN,EWOULDBLOCK,EINPROGRESS

那我們下面來寫一個非阻塞的TCP服務

非阻塞的IO【非阻塞的檔案描述符】

設定非阻塞的函式
ps:在c語言裡一般用fcntl去控制檔案描述符

socket_set_nonblock($socket);
stream_set_blocking( resource $stream , int $mode )
$ip = "0.0.0.0";
$port = $argv[1];
$sockefd = socket_create(AF_INET,SOCK_STREAM,0);

socket_bind($sockefd,$ip,$port);
socket_listen($sockefd,5);

$counter = 0;
while (1){

    $counter++;
    //$sockfd 我們最好讓它阻塞,不然立馬返回的話 $connfd可不是資源即連線就會導致socket_set_nonblock報錯的
    $connfd = socket_accept($sockefd);
    echo "accept \n"; 
    //得到客戶端連線設定為非阻塞IO模式
    socket_set_nonblock($connfd);
    if ($connfd){
        socket_write($connfd,"hello,php 是世界上是好的語言");
        echo "write once\n";  
        //要是這裡不加while它就跑一次完事了
        //加上while【忙輪詢】就是讓socket_read一直不停的問有沒有資料
        while (1){
            if(($recv=socket_read($connfd,4098,PHP_BINARY_READ))){
                socket_write($connfd,"server:$recv");
            }
            //上面的socket_read不管有沒有讀取到資料
            //都立馬返回並且執行echo 
            //如果是阻塞模式,則該句就得等上面的socket_read執行了這裡才執行,否則就得等,等的結果就是SLEEP,cpu就會幹其它事情了
            echo "不阻塞一直不停的執行socket_read問核心有沒有資料\n";
            sleep(1);
        }
    }

}
socket_close($sockefd);
socket_close($connfd);

測試的結果【非常建議動手擼,不然沒法體會阻塞與非阻塞
玩轉 PHP 網路程式設計全套之二客戶端
同樣的我們不設定為非阻塞模式時,它預設是阻塞IO

本作品採用《CC 協議》,轉載必須註明作者和本文連結

只會php crud的渣渣

相關文章