在使用PHP 開發基於socket 的網絡應用時,開發者常常會接觸到socket_accept()和socket_set_block()這兩個函數。雖然這兩個函數都屬於PHP 的socket 擴展,但它們在使用上有著密切的關係,理解它們的協同工作機制,有助於我們更精準地控制socket 的行為,提升網絡通信的可靠性和效率。
在探討這兩個函數的協作前,我們先來了解它們的基本作用:
socket_accept(resource $socket): resource|false
此函數用於接受一個來自監聽socket 的連接請求。如果沒有連接請求,它的行為取決於socket 的阻塞模式。
socket_set_block(resource $socket): bool
此函數將一個socket 設置為阻塞模式。在阻塞模式下,相關的socket 操作(如socket_accept 、 socket_read等)會等待直到有數據或連接發生。
與socket_set_block()相對的是socket_set_nonblock() ,它會使得socket 操作立即返回,不論是否有數據或連接到達。
默認情況下,PHP 創建的socket 是阻塞模式的,這意味著socket_accept()會一直阻塞等待直到有客戶端連接。也就是說,如果你運行如下代碼:
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '0.0.0.0', 12345);
socket_listen($socket);
$client = socket_accept($socket);
echo "Connection accepted";
在沒有客戶端連接的情況下,程序會一直停在socket_accept()調用處,直到有連接發生。
現在我們引入socket_set_block() ,顯式地設置socket 為阻塞模式(雖然默認就是阻塞的,但顯式設置可以防止代碼因前面操作改變了模式而出錯):
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_block($socket); // 顯式設置為阻塞模式
socket_bind($socket, '0.0.0.0', 12345);
socket_listen($socket);
$client = socket_accept($socket);
echo "Connection accepted";
此時, socket_accept()的行為沒有變化:它仍然會阻塞直到有客戶端連接。
但如果我們在此處使用socket_set_nonblock() :
socket_set_nonblock($socket);
那麼調用socket_accept()時,如果沒有連接請求,它會立刻返回false ,並且可以通過socket_last_error()檢查是否是因為EAGAIN或EWOULDBLOCK ,從而實現非阻塞接收連接的邏輯。
socket_set_block()和socket_accept()之間的協作,本質上是通過設置socket 的工作模式,決定socket_accept()的阻塞行為。這個設置決定了程序的控制流程是同步的(阻塞式)還是異步的(非阻塞式)。
例如,在編寫高性能服務器時,你可能會使用非阻塞socket 並結合socket_select()來監聽多個連接請求:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_nonblock($socket);
socket_bind($socket, '0.0.0.0', 12345);
socket_listen($socket);
$clients = [];
while (true) {
$read = [$socket];
$write = $except = null;
if (socket_select($read, $write, $except, 1) > 0) {
$client = socket_accept($socket);
if ($client !== false) {
$clients[] = $client;
echo "Client connected\n";
}
}
// 處理其他邏輯
}
這種方式允許你在一個循環中同時處理多個socket,無需在socket_accept()上阻塞等待。
socket_set_block()決定了socket 是否阻塞工作,而socket_accept()是一個受此設置影響的具體操作。當socket 是阻塞模式時, socket_accept()會等待連接;而在非阻塞模式下,它會立即返回。兩者的協同使用讓我們可以根據實際需求編寫同步或異步的網絡服務程序,提供了更大的靈活性。
了解它們之間的關係,不僅有助於寫出穩定的socket 服務,也為構建高並發、低延遲的網絡應用奠定了基礎。
如需查看更多相關示例,可參考https://gitbox.net/socket-examples 。