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函式
第二個客戶端連線時
總結
select的基本功能:就是為了能監聽多個客戶端的連線,它本身還是並沒有特別的地方,但是它也是有缺點的,就是每次都得傳遞檔案描述符集合給它,同時返回的就緒檔案也要自己遍歷,如果監聽太多,效能也不怎麼樣,更關鍵的是它最多隻能監聽1024個檔案描述符,畢竟它在核心中是以位形式儲存。
原理圖:【實在找不到吊炸天的畫圖軟體了】
該原理圖請配合select函式親自測試並除錯
本作品採用《CC 協議》,轉載必須註明作者和本文連結