<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-comment">// 本文件為教學用途,展示如何在 PHP 中使用 socket_select 檢查多個 Socket 是否可讀。</span></span><span>
</span><span><span class="hljs-comment">// 請確保已啟用 sockets 擴展。</span></span><span>
</span><span><span class="hljs-comment">// 建立示例 sockets(伺服器 socket 和幾個客戶端 socket),省略創建過程以聚焦核心邏輯。</span></span><span>
</span><span><span class="hljs-comment">// -------------------------------</span></span><span>
</span><span><span class="hljs-meta">?></span></span><span>
</span><span><span class="hljs-comment"># 怎麼用 socket_select 檢查多個 Socket 是否可讀?PHP 實用技巧講解</span></span><span>
在開發基於網絡通信的 PHP 應用時,我們常常需要同時處理多個連接,尤其是在構建聊天室、游戏伺服器或即时通信系统等场景中。這時,`</span><span><span class="hljs-title function_ invoke__">socket_select</span></span><span>()` 函數就顯得尤為重要。它允許你在多個 socket 之間進行非阻塞的可讀寫檢查,極大提高了效率。
本文將帶你深入了解如何使用 `socket_select` 檢查多個 socket 是否可讀,並給出一個簡潔實用的示例。
</span><span><span class="hljs-comment">## 什麼是 socket_select?</span></span><span>
`</span><span><span class="hljs-title function_ invoke__">socket_select</span></span><span>()` 是 PHP 對底層 `</span><span><span class="hljs-title function_ invoke__">select</span></span><span>()` 系統調用的封裝。它用於檢測一組 sockets 是否可以進行讀、寫或異常操作,並且可以設定超時。
```php
</span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-title function_ invoke__">socket_select</span></span><span>(</span><span><span class="hljs-keyword">array</span></span><span> &</span><span><span class="hljs-variable">$read</span></span><span>, </span><span><span class="hljs-keyword">array</span></span><span> &</span><span><span class="hljs-variable">$write</span></span><span>, </span><span><span class="hljs-keyword">array</span></span><span> &</span><span><span class="hljs-variable">$except</span></span><span>, </span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-variable">$tv_sec</span></span><span>, </span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-variable">$tv_usec</span></span><span> = </span><span><span class="hljs-number">0</span></span><span>)
</span></span>$read :你想監控是否可讀的sockets。
$write :你想監控是否可寫的sockets(可傳null)。
$except :你想監控是否有異常的sockets(可傳null)。
$tv_sec和$tv_usec :分別是秒和微秒級的超時時間。
以下是一個實際使用socket_select的PHP 代碼片段,它檢查多個客戶端socket 是否有數據到達:
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-comment">// 创建主伺服器 socket</span></span><span>
</span><span><span class="hljs-variable">$server</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_create</span></span><span>(AF_INET, SOCK_STREAM, SOL_TCP);
</span><span><span class="hljs-title function_ invoke__">socket_bind</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>, </span><span><span class="hljs-string">'127.0.0.1'</span></span><span>, </span><span><span class="hljs-number">8888</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">socket_listen</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-comment">// 設置非阻塞</span></span><span>
</span><span><span class="hljs-title function_ invoke__">socket_set_nonblock</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-variable">$clients</span></span><span> = [</span><span><span class="hljs-variable">$server</span></span><span>];
</span><span><span class="hljs-keyword">while</span></span><span> (</span><span><span class="hljs-literal">true</span></span><span>) {
</span><span><span class="hljs-variable">$readSockets</span></span><span> = </span><span><span class="hljs-variable">$clients</span></span><span>;
</span><span><span class="hljs-variable">$writeSockets</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
</span><span><span class="hljs-variable">$exceptSockets</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
</span><span><span class="hljs-comment">// 檢查哪些 socket 可讀(不阻塞,1 秒超時)</span></span><span>
</span><span><span class="hljs-variable">$numChangedSockets</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_select</span></span><span>(</span><span><span class="hljs-variable">$readSockets</span></span><span>, </span><span><span class="hljs-variable">$writeSockets</span></span><span>, </span><span><span class="hljs-variable">$exceptSockets</span></span><span>, </span><span><span class="hljs-number">1</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$numChangedSockets</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"socket_select 失敗:"</span></span><span> . </span><span><span class="hljs-title function_ invoke__">socket_strerror</span></span><span>(</span><span><span class="hljs-title function_ invoke__">socket_last_error</span></span><span>()) . PHP_EOL;
</span><span><span class="hljs-keyword">break</span></span><span>;
}
</span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$readSockets</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$sock</span></span><span>) {
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$sock</span></span><span> === </span><span><span class="hljs-variable">$server</span></span><span>) {
</span><span><span class="hljs-comment">// 有新的連接</span></span><span>
</span><span><span class="hljs-variable">$newClient</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_accept</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">socket_set_nonblock</span></span><span>(</span><span><span class="hljs-variable">$newClient</span></span><span>);
</span><span><span class="hljs-variable">$clients</span></span><span>[] = </span><span><span class="hljs-variable">$newClient</span></span><span>;
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"新客戶端已連接。"</span></span><span> . PHP_EOL;
} </span><span><span class="hljs-keyword">else</span></span><span> {
</span><span><span class="hljs-comment">// 有客戶端發來數據</span></span><span>
</span><span><span class="hljs-variable">$data</span></span><span> = @</span><span><span class="hljs-title function_ invoke__">socket_read</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>, </span><span><span class="hljs-number">1024</span></span><span>, PHP_NORMAL_READ);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$data</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span> || </span><span><span class="hljs-variable">$data</span></span><span> === </span><span><span class="hljs-string">""</span></span><span>) {
</span><span><span class="hljs-comment">// 客戶端斷開連接</span></span><span>
</span><span><span class="hljs-variable">$index</span></span><span> = </span><span><span class="hljs-title function_ invoke__">array_search</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>, </span><span><span class="hljs-variable">$clients</span></span><span>);
</span><span><span class="hljs-keyword">unset</span></span><span>(</span><span><span class="hljs-variable">$clients</span></span><span>[</span><span><span class="hljs-variable">$index</span></span><span>]);
</span><span><span class="hljs-title function_ invoke__">socket_close</span></span><span>(</span><span><span class="hljs-variable">$sock</span></span><span>);
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"客戶端斷開連接。"</span></span><span> . PHP_EOL;
} </span><span><span class="hljs-keyword">else</span></span><span> {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"收到數據:"</span></span><span> . </span><span><span class="hljs-title function_ invoke__">trim</span></span><span>(</span><span><span class="hljs-variable">$data</span></span><span>) . PHP_EOL;
}
}
}
}
</span><span><span class="hljs-meta">?></span></span><span>
</span></span>非阻塞socket :為避免阻塞主進程,所有socket 都應設置為非阻塞模式。
主socket 檢查連接請求:當socket_select檢測到主socket 可讀,說明有新的客戶端連接請求。
客戶端socket 檢查數據:當某個客戶端socket 可讀時,說明客戶端發送了數據。
注意斷連處理:如果讀取失敗或返回空字符串,應認為客戶端已斷開連接。
socket_select()是處理並發socket 通信的關鍵工具。相比每個連接都起一個線程或進程,使用select模型可以更輕量、可控地管理大量連接。熟練掌握它,會讓你的PHP 網絡應用更穩定、更高效。
希望本文能幫助你理解socket_select的使用場景與實踐方式,並能在你的項目中真正發揮作用。
<span></span>