當前位置: 首頁> 最新文章列表> 如何通過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>