PHP socket初探 --- 一些零碎細節的拾漏補缺

原文:https://t.ti-node.com/thread/...

前面可以說是弄了一系列的php socket和多進程的一大坨內容,知識淺顯、代碼粗暴、風格簡陋,總的說來,還是差了一些細節。今天,就一些漏掉的細節補充一下。

  1. 一些有志青年可能最近手刃了Workerman源碼,對於裏面那一大坨stream_select()、stream_socket_server()表示疑惑,這個玩意和socket_create、socket_set_nonblock()有啥區別?其實,php官方手冊裏也提到過一嘴,socket系函數就是基於BSD Socket那一套玩意搞的,幾乎就是將那些東西簡單包裝了一下直接抄過來用的,抄到甚至連名字都和C語言操控socket的函數一模一樣,所以說socket系函數是一種比較低級(Low-Level,這裏的低級是指軟件工程中分層中層次的高低)socket操控方式,可以最大程度給你操作socket的自由以及細膩度。在php中,socket系本身是作爲php擴展而體現的,這個你可以通過php -m來查看有沒有socket,這件事情意味着有些php環境可能沒有安裝這個擴展,這個時候你就無法使用socket系的函數了。但stream則不同了,這貨是內建於php中的,除了能處理socket網絡IO外,還能操控普通文件的打開寫入讀取等,stream系將這些輸入輸出統一抽象成了流,通過流來對待一切。有人可能會問二者性能上差距,但是本人沒有測試過,這個我就不敢輕易妄言了,但是從正常邏輯上推演的話,應該不會有什麼太大差距之類的。
  2. 一定要分清楚監聽socket和連接socket,我們服務器監聽的是監聽socket,然後accept一個客戶端連接後的叫做連接socket。
  3. 關於“異步非阻塞”,這五個字到底體現在哪兒了。swoole我就不說了,我源碼也才閱讀了一小部分,我就說Workerman吧,它在github上稱:“Workerman is an asynchronous event driven PHP framework with high performance for easily building fast, scalable network applications.”,看到其中有asynchronous(異步)的字樣,打我臉的是我並沒有看到有non-block(非阻塞)的字樣,不過無妨,臉什麼的不重要,重要的是我文章裏那一坨又一坨的代碼裏哪裏體現了非阻塞、哪裏體現了異步。來吧,看代碼吧。

    看代碼前,你要理解異步和非阻塞的區別是什麼,因爲這二者在表現結果上看起來是有點兒相似的,如果你沒搞明白,那麼一定要通過這個來理解一下《PHP socket初探 --- 關於IO的一些枯燥理論》

<?php
// 創建一個監聽socket,這個一個阻塞IO的socket
$listen = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
socket_bind( $listen, '0.0.0.0', 9999 );
socket_listen( $listen );
while( true ){
     // socket_accept也是阻塞的,雖然有while,但是由於accpet是阻塞的,所以這段代碼不會進入無限死循環中
     $connect = socket_accept( $listen );
     if( $connect ){
       echo "有新的客戶端".PHP_EOL;
     } else {
       echo "客戶端連接失敗".PHP_EOL;
     }
}

將上面代碼保存了運行一下,然後用telnet可以連接上去。但是,這段代碼中有兩處是阻塞的,最主要就是監聽socket是阻塞的。那麼,非阻塞的監聽socket會是什麼感受?

<?php
// 創建一個監聽socket,將其設置爲非阻塞
$listen = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
socket_bind( $listen, '0.0.0.0', 9999 );
socket_listen( $listen );
// ⚠️⚠️⚠️⚠️⚠️⚠️ 這裏設置非阻塞!
socket_set_nonblock( $listen );
while( true ){
     $connect = socket_accept( $listen );
     if( $connect ){
       echo "有新的客戶端".PHP_EOL;
     } else {
       echo "客戶端連接失敗".PHP_EOL;
     }
}

將代碼保存了運行一下,告訴我:

來來來,分析一波兒,爲啥會出現這種現象。因爲監聽socket被設置成了非阻塞,我們知道非阻塞就是程序立馬返回,然後再過段時間回來詢問,用例子就是“等饅頭過程中,看下微博,擡頭問饅頭好了嗎?然後看下微信,擡頭問饅頭好了嗎?然後看下v2ex,擡頭問饅頭好了嗎?。。。 。。。”,這樣你是不是就能理解了?因爲並沒有客戶端連接進來,所以每當詢問一次socket_accept後得到的反饋都是“沒有連接”,所以就直接走到“客戶端連接失敗”的分支中去了,而且是不斷的不停的。這個時候,你用htop或者top命令查看服務器CPU,不出意外應該是100%,這是非阻塞的極大缺點。

緊接着是異步呢?異步體現在哪兒了?我們說異步,是你去阿梅那裏買饅頭,阿梅告訴你說“饅頭還沒好,你去幹別的吧,好了我打電話通知你”,然後你就專心去打遊戲去了,直到電話響了你去拿饅頭。Workerman的異步更多是體現在對一個完整請求的處理流上,而不是正兒八經的異步的定義概念,如果你沒聽明白,那也可能正常,慢慢理解。最後,我補充一句:epoll是同步的,而不是異步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章