<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>