玩轉 PHP 網路程式設計全套之 I/O 複用

勺顛顛發表於2020-04-25

PHP的socket擴充套件提供了一個多路I/O事件複用select

IO複用

I/O複用函式本身也是阻塞I/O,發起系統呼叫同樣的要被掛起,直到就緒事件發生,才會執行。所以也稱為同步I/O模型。
I/O複用函式一般用的有select,epoll,poll,kqueue,devpoll等PHP只提供了一個select。

同步I/O模型與非同步I/O模型

同步IO模型是嚮應用程式通知的是I/O就緒事件,而非同步I/O模型則是嚮應用程式通知的是完成事件,後續我們在編寫程式碼過程會進行模擬非同步I/O的實現。

測試原始碼

$ip = "0.0.0.0"; $port = $argv[1]; $sockefd = socket_create(AF_INET,SOCK_STREAM,0); socket_set_option($sockefd,SOL_SOCKET,SO_REUSEPORT,1); socket_bind($sockefd,$ip,$port); socket_listen($sockefd,5);   $connectionfds = []; $readfds = [$sockefd]; $writefds = [$sockefd]; $exceptionfds = [$sockefd]; $bufferMessage = "hello,world"; $recvMessage = ""; $remoteIP; $remoteAddr; fprintf(STDOUT,"server pid=%d\n",posix_getpid()); $i=0; while ("server") {          $read_fds      = $readfds;         $write_fds     = $writefds;         $exception_fds = $exceptionfds;          $connectNum = socket_select($read_fds, $write_fds, $exception_fds, NULL);          if (!$connectNum) {             break;         }          foreach ($read_fds as $fd) {              if ($fd == $sockefd) {                 $connfd = socket_accept($sockefd);                 socket_set_nonblock($connfd);                  socket_getpeername($connfd,$remoteIP,$remoteAddr);                 fprintf(STDOUT, "new connection %s ,ip is %s,port is %d\n", $connfd,$remoteIP,$remoteAddr);                 $readfds[]       = $connfd;                 $exceptionfds[]  = $connfd;                 $connectionfds[] = $connfd;             } else {                  $recvMessage = socket_read($fd, 8192);                 unset($readfds[array_search($fd, $readfds)]);                 if (strcasecmp("QUIT",$recvMessage)){                     socket_send($fd,"quit\n",6,0);                     socket_close($fd);                     unset($exceptionfds[array_search($fd, $exceptionfds)]);                     unset($writefds[array_search($fd, $writefds)]);                 }                 if (!$recvMessage) {                     socket_close($fd);                     unset($exceptionfds[array_search($fd, $exceptionfds)]);                     unset($writefds[array_search($fd, $writefds)]);                 } else {                     fprintf(STDOUT, "recv from client msg:%s\n", $recvMessage);                     array_push($writefds, $fd);                 }              }         }          foreach ($write_fds as $fd) {             $writeBytes = socket_write($fd, $bufferMessage, strlen($bufferMessage));             unset($writefds[array_search($fd, $writefds)]);             if (!$writeBytes) {                 socket_close($fd);                 unset($readfds[array_search($fd, $readfds)]);                 unset($exceptionfds[array_search($fd, $exceptionfds)]);             } else {                 array_push($readfds, $fd);             }         }          foreach ($exception_fds as $fd) {             socket_close($fd);             unset($readfds[array_search($fd, $readfds)]);             unset($exceptionfds[array_search($fd, $exceptionfds)]);             unset($writefds[array_search($fd, $writefds)]);         }   } socket_close($sockefd);

啟動服務

然後阻塞在selectI/O函式
玩轉 PHP 網路程式設計全套之 I/O 複用
玩轉 PHP 網路程式設計全套之 I/O 複用
玩轉 PHP 網路程式設計全套之 I/O 複用
玩轉 PHP 網路程式設計全套之 I/O 複用

第二個客戶端連線時
玩轉 PHP 網路程式設計全套之 I/O 複用

玩轉 PHP 網路程式設計全套之 I/O 複用

總結

select的基本功能:就是為了能監聽多個客戶端的連線,它本身還是並沒有特別的地方,但是它也是有缺點的,就是每次都得傳遞檔案描述符集合給它,同時返回的就緒檔案也要自己遍歷,如果監聽太多,效能也不怎麼樣,更關鍵的是它最多隻能監聽1024個檔案描述符,畢竟它在核心中是以位形式儲存。
原理圖:【實在找不到吊炸天的畫圖軟體了】
該原理圖請配合select函式親自測試並除錯
玩轉 PHP 網路程式設計全套之 I/O 複用

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

只會php crud的渣渣

相關文章