說明
- 流,算是一種對不同事物,但有相同特性的抽象封裝,可能這樣說并不理解,但是我們早就使用過了,例如打開檔案
fopen等操作,其實就是用的流,fopen('abc.txt')實際上就是fopen('file://abc.txt'),或者是與app互動用到的php://input等獲取post資料也是流的一種 - php官方檔案可以看Streams API for PHP Extension Authors和Stream
- 我們只看流中與socket相關的封裝,上篇我們建立一個連接需要好幾個步驟,比較繁瑣,而stream中對此進行了簡化封裝,至于流的其它包裝過濾等功能,可自己去查詢資料,好像是在《Modern PHP》中也有章節對此做過講解,
- 相關的stream函式可以參照Workerman中具體的使用場景,Workerman中沒有使用上節的socket函式,而是呼叫的更加簡潔方便的stream函式,
相關函式
-
服務端函式
stream_socket_server( string $local_socket [, int &$errno [, string &$errstr [, int $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN [, resource $context ]]]] ) : resource- 創建socket服務端
- 此函式可以代替上節的
socket_create、socket_bind、socket_listen這三個函式,例如:$socket = stream_socket_server('tcp://0.0.0.0:8000');相當于$socket = socket_create(AF_INET, SOCK_STREAM, 0);+socket_bind($socket, '0.0.0.0', 8000);+socket_listen($socket); - $flags引數,如果是用udp通信的話,
STREAM_SERVER_LISTEN是不需要的,$context則是背景關系,后面有單獨的函式來生成此型別,還有需要注意的是,stream_socket_server和socket_create的回傳值雖然都是resource,但兩個不能通用,socket擴展中有單獨的函式來轉換這兩者,
stream_socket_accept( resource $server_socket [, float $timeout = ini_get("default_socket_timeout") [, string &$peername ]] ) : resource- 接收客戶端的連接(udp通信時,不需要用到此函式)
- 類似
socket_accept,需要注意的是第一個引數,只能是stream_socket_server的回傳值,
-
客戶端函式
stream_socket_client( string $remote_socket [, int &$errno [, string &$errstr [, float $timeout = ini_get("default_socket_timeout") [, int $flags = STREAM_CLIENT_CONNECT [, resource $context ]]]]] ) : resource- 創建socket客戶端,連接到服務端
- 此函式可以替代上節的
socket_create、socket_connect這兩個函式,例如$socket = stream_socket_client('tcp://0.0.0.0:8000'')相當于$socket = socket_create(AF_INET, SOCK_STREAM, 0);+$result = socket_connect($socket, '0.0.0.0', 8000); - $flags引數主要有兩個選項
STREAM_CLIENT_CONNECT和STREAM_CLIENT_ASYNC_CONNECT,第二個引數是設定異步,與阻塞又不太相同,挺讓人費解的,而且網上也基本搜不到相關資料,當設定此引數時,將不會檢測地址埠是否真的能連通,都為立即回傳正常的資源resource,那如何來確定服務端是否真的能鏈接上呢?目前個人所知的方式是用stream_select類似的多路IO復用來檢測,當真正鏈接或鏈接失敗后內核會通知到read或者write或者except(具體通知到哪個windows和linux不一致,需要區別處理),下面的stream_select函式和后期IO多路復用時會講到,
-
讀寫函式
fread( resource $handle , int $length ) : string- 讀取資料
fwrite( resource $handle , string $string [, int $length ] ) : int- 寫入資料
- 上面兩個函式和我們平常讀取檔案一樣,正如我們說的,操作檔案也是操作一種流,所以方法通用
stream_socket_recvfrom( resource $socket , int $length [, int $flags = 0 [, string &$address ]] ) : string- 接收資料,最后引數是參考,用于獲取遠端鏈接的地址
stream_socket_sendto( resource $socket , string $data [, int $flags = 0 [, string $address ]] ) : int- 發送資料
- 上面兩個函式和
fread和fwrite基本一致,但功能更多一些,$flags引數可以設定發送OOB資料,而$address引數則多是用于udp通信,由于udp通信時不使用stream_socket_accept,所以無法獲取到新的resource,那就無法向指定的客戶端中寫資料,所以一般先用stream_socket_recvfrom獲取最后參考引數$address即為客戶端的地址,然后再用stream_socket_sendto設定$address,來向指定的客戶端發送,
-
其它常用函式
stream_select( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] ) : int- 呼叫系統select()相關IO模型
- 類似上節的
socket_select,詳細的在后面的IO模型中會總結
stream_set_blocking( resource $stream , bool $mode ) : bool- 設定阻塞與非阻塞,false為非阻塞,true為阻塞
- 類似上節的
socket_set_block和socket_set_nonblock,但是還是有些區別的,上節我們知道socket的阻塞影響的函式為:socket_connect、socket_accept以及各種socket讀寫函式,而stream的阻塞影響的函式僅僅為stream相關的讀寫函式,stream_socket_accept不受影響,一直是阻塞,stream_socket_client更不會受影響,因為stream_socket_client的回傳值,才能作為stream_set_blocking的第一個引數,所以上方說stream_socket_client的引數flags設定為異步,算是彌補了這一問題,
fclose( resource $handle ) : bool- 關閉鏈接
stream_socket_shutdown( resource $stream , int $how ) : bool- 關閉鏈接
- 上面兩個函式功能基本一樣,
stream_socket_shutdown還可以控制只關閉讀或者只關閉寫 socket_import_stream( resource $stream ) : resource- 將stream資源轉為socket資源
socket_export_stream( resource $socket ) : resource- 將socket資源轉為stream資源
- 上面也提到過,socket擴展和stream兩者鏈接產生的resource是不互通的,上面兩個函式就是將兩者轉換的,而這兩個函式是定義在上節所說的socket擴展中的,使用前需要確定socket擴展有打開
stream_context_create- 背景關系創建
stream_socket_server和stream_socket_client最后一個引數就是背景關系,相關的引數需要經過此函式的組裝后才能傳入,我們可能早就接觸過,例如我們經常用到的file_get_content可以模擬post來獲取資料,就需要用到它的第三個背景關系引數,
示例
- 服務端
$socket = stream_socket_server('tcp://127.0.0.1:8888', $errno, $errstr); while ($conn = stream_socket_accept($socket)) { fwrite($conn, "hello\n"); fclose($conn); } fclose($socket); - 客戶端
$socket = stream_socket_client('tcp://127.0.0.1:8888', $errorno, $errstr); while (!feof($socket)) { echo fread($socket, 1024); } fclose($socket); - 上方是簡單的示例代碼,更多的可以查看官網各函式下的示例代碼
- stream與socket相比是更簡潔了,但是原理是一樣的,同樣的只能連接一個客戶端的局限性并沒有解決,還是需要后續的完善
關聯
- 上篇:php socket網路編程基礎知識(二):socket函式
- 下篇:php socket網路編程基礎知識(四):多行程
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/5060.html
標籤:PHP
