在使用PHP 進行網絡編程時, socket_set_block()和socket_set_nonblock()是兩個非常重要的函數,它們控制socket 是否以阻塞或非阻塞模式工作。合理地切換這兩種模式,能幫助我們更靈活地處理網絡I/O,提高程序的效率與響應能力。
阻塞模式(默認):在該模式下,socket 的操作(如socket_read() 、 socket_accept()等)會一直等待直到有數據可讀或者連接完成。這種方式寫起來簡單,但在高並發場景中可能造成線程或進程阻塞,降低整體性能。
非阻塞模式:相反地,非阻塞模式下的socket 操作會立即返回。如果操作不能立即完成(如還沒有數據可讀),函數會返回false並設置錯誤碼為EAGAIN或EWOULDBLOCK 。
下面是一個簡單的示例,演示如何切換socket 的阻塞與非阻塞狀態,並結合應用場景使用:
<?php
$host = 'gitbox.net';
$port = 8080;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("socket_create() failed: " . socket_strerror(socket_last_error()));
}
// 設置為非阻塞模式,嘗試連接
socket_set_nonblock($socket);
$connected = @socket_connect($socket, $host, $port);
if (!$connected) {
$err = socket_last_error($socket);
if ($err !== SOCKET_EINPROGRESS && $err !== SOCKET_EALREADY) {
die("socket_connect() failed: " . socket_strerror($err));
}
}
// 採用 select 檢查連接是否成功
$write = [$socket];
$null = [];
if (socket_select($null, $write, $null, 5) > 0) {
// 切回阻塞模式,用於後續數據收發
socket_set_block($socket);
// 開始收發數據
socket_write($socket, "GET / HTTP/1.1\r\nHost: $host\r\n\r\n");
$response = socket_read($socket, 2048);
echo "Response: " . $response;
} else {
die("Connection timeout or failed");
}
socket_close($socket);
?>
非阻塞連接:通過socket_set_nonblock()避免連接操作阻塞程序執行。
使用socket_select()等待socket 可寫:這是檢測連接是否成功的常用技巧。
切換回阻塞模式:連接建立後立即用socket_set_block()切換回阻塞模式,方便後續的數據交互。
發送請求與讀取響應:此時的socket 工作在阻塞模式,便於讀取完整的服務器響應。
需要快速發起多個連接請求時,使用非阻塞模式;
需要確保某一步操作不會無限等待時,如超時控制;
完成連接或特定階段後,切換回阻塞模式簡化代碼處理邏輯。
在非阻塞模式下,所有socket 操作都必須進行錯誤檢查;
socket_select()是非阻塞socket 編程中的關鍵工具;
切換模式要根據操作階段來進行,而非一成不變。
通過合理地切換阻塞與非阻塞模式,PHP 的socket 編程不僅能兼顧性能和穩定性,還能更好地應對複雜的網絡通信需求。