当前位置: 首页> 最新文章列表> 如何通过 stream_set_blocking 控制 PHP 中套接字的读取行为,提升网络应用性能?

如何通过 stream_set_blocking 控制 PHP 中套接字的读取行为,提升网络应用性能?

gitbox 2025-09-20
<span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-comment">// 以下内容与文章正文无关</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"这是一段示例 PHP 代码,用于展示与文章无关的前置部分。\n"</span></span><span>;

-------------------------------------------------------------------------------

<span class="hljs-comment">/**
 * 如何通过 stream_set_blocking 控制 PHP 中套接字的读取行为,提升网络应用性能?
 *
 * 在 PHP 网络编程中,套接字(socket)的读取行为对应用性能有直接影响。默认情况下,
 * PHP 的套接字操作是阻塞式的,即当调用读取函数(如 fread 或 stream_get_contents)时,
 * 如果没有数据到达,程序会一直等待,直到数据可用。这种行为在高并发或长连接场景下容易导致性能瓶颈。
 *
 * 为了解决这一问题,PHP 提供了 stream_set_blocking 函数,可以灵活地控制套接字的阻塞模式。
 *
 * 一、stream_set_blocking 的基本用法
 *
 * 函数原型:
 * bool stream_set_blocking ( resource $stream , int $mode )
 *
 * 参数说明:
 * - $stream: 套接字资源(stream 资源)。
 * - $mode: 阻塞模式,1 表示阻塞(blocking),0 表示非阻塞(non-blocking)。
 *
 * 示例:
 */</span>
</span><span><span class="hljs-variable">$socket</span></span><span> = </span><span><span class="hljs-title function_ invoke__">stream_socket_client</span></span><span>(</span><span><span class="hljs-string">"tcp://127.0.0.1:8080"</span></span><span>, </span><span><span class="hljs-variable">$errno</span></span><span>, </span><span><span class="hljs-variable">$errstr</span></span><span>, </span><span><span class="hljs-number">30</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$socket</span></span><span>) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"连接失败: <span class="hljs-subst">$errstr</span></span></span><span> (</span><span><span class="hljs-subst">$errno</span></span><span>)\n";
} </span><span><span class="hljs-keyword">else</span></span><span> {
    </span><span><span class="hljs-comment">// 设置非阻塞模式</span></span><span>
    </span><span><span class="hljs-title function_ invoke__">stream_set_blocking</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>, </span><span><span class="hljs-number">0</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__">fread</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>, </span><span><span class="hljs-number">8192</span></span><span>);
    </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-keyword">echo</span></span><span> </span><span><span class="hljs-string">"当前没有可读数据,程序继续执行。\n"</span></span><span>;
    } </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 class="hljs-subst">$data</span></span></span><span>\n";
    }
}
-------------------------------------------------------------------------------

<span class="hljs-comment">/**
 * 二、阻塞模式与非阻塞模式的差异
 *
 * 1. 阻塞模式(blocking)
 *    - fread 或 fgets 会等待数据到达才返回。
 *    - 适用于单线程、低并发的场景。
 *    - 简单,但可能导致长时间等待阻塞整个脚本。
 *
 * 2. 非阻塞模式(non-blocking)
 *    - fread 等函数立即返回,若无数据则返回空或 false。
 *    - 适合异步处理、高并发场景。
 *    - 可以结合 stream_select 监控多个套接字的可读状态,从而提高网络应用性能。
 *
 * 三、结合 stream_select 提升性能
 *
 * stream_select 可以监控多个流是否可读、可写或发生异常:
 *
 * 示例:
 */</span>
</span><span><span class="hljs-variable">$read</span></span><span> = [</span><span><span class="hljs-variable">$socket</span></span><span>];
</span><span><span class="hljs-variable">$write</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
</span><span><span class="hljs-variable">$except</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
</span><span><span class="hljs-variable">$timeout</span></span><span> = </span><span><span class="hljs-number">2</span></span><span>; </span><span><span class="hljs-comment">// 超时时间 2 秒</span></span><span>

</span><span><span class="hljs-variable">$num_changed_streams</span></span><span> = </span><span><span class="hljs-title function_ invoke__">stream_select</span></span><span>(</span><span><span class="hljs-variable">$read</span></span><span>, </span><span><span class="hljs-variable">$write</span></span><span>, </span><span><span class="hljs-variable">$except</span></span><span>, </span><span><span class="hljs-variable">$timeout</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$num_changed_streams</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">"监控套接字出错\n"</span></span><span>;
} </span><span><span class="hljs-keyword">elseif</span></span><span> (</span><span><span class="hljs-variable">$num_changed_streams</span></span><span> === </span><span><span class="hljs-number">0</span></span><span>) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"超时,暂无可读数据\n"</span></span><span>;
} </span><span><span class="hljs-keyword">else</span></span><span> {
    </span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$read</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$r</span></span><span>) {
        </span><span><span class="hljs-variable">$data</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fread</span></span><span>(</span><span><span class="hljs-variable">$r</span></span><span>, </span><span><span class="hljs-number">8192</span></span><span>);
        </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"接收到数据: <span class="hljs-subst">$data</span></span></span><span>\n";
    }
}
-------------------------------------------------------------------------------

<span class="hljs-comment">/**
 * 四、实际应用中的性能优化建议
 *
 * 1. 在高并发网络应用中,尽量使用非阻塞模式读取套接字,避免单个慢连接阻塞整个进程。
 * 2. 使用 stream_select 或类似事件循环机制(如 ReactPHP)来轮询多个套接字。
 * 3. 对于长连接,合理设置读取超时时间,避免无限阻塞。
 * 4. 避免在阻塞模式下处理大量数据,必要时可分块读取或使用缓冲。
 *
 * 通过合理使用 stream_set_blocking 和事件循环机制,可以显著提升 PHP 网络应用的响应速度和吞吐量。
 */</span>
</span><span><span class="hljs-meta">?&gt;</span></span><span>
</span></span>