在PHP 中實現一個基於Socket 的阻塞式服務器,可以幫助我們理解更底層的網絡通信原理。阻塞模式意味著服務器會在讀取或寫入數據前一直等待,直到有數據可以處理或連接已準備好。本文將一步步解析如何使用PHP 的socket擴展,從創建socket 到設置為阻塞模式,並構建一個簡單的阻塞式服務器。
首先,確認PHP 環境中啟用了socket 擴展。在php.ini中查找並取消下面這一行的註釋(如果有):
extension=sockets
使用php -m命令檢查擴展是否啟用:
php -m | grep sockets
如果能看到sockets ,說明擴展已啟用。
以下是創建socket 的基本步驟,並逐步介紹每個函數的作用。
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("socket_create() failed: " . socket_strerror(socket_last_error()) . "\n");
}
這裡我們創建的是一個基於IPv4( AF_INET )的TCP socket( SOCK_STREAM )。
可以設置一些socket 選項,如端口復用:
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
$host = '0.0.0.0';
$port = 9000;
if (!socket_bind($socket, $host, $port)) {
die("socket_bind() failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
if (!socket_listen($socket, 5)) {
die("socket_listen() failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
5表示最多掛起的連接請求數。
使用socket_set_block將socket 設置為阻塞模式。
if (!socket_set_block($socket)) {
die("socket_set_block() failed: " . socket_strerror(socket_last_error($socket)) . "\n");
}
一旦設置為阻塞模式,後續的socket_accept 、 socket_read等函數都會阻塞,直到操作完成。
echo "服務器已啟動,監聽端口 $port...\n";
while (true) {
$clientSocket = socket_accept($socket);
if ($clientSocket === false) {
echo "socket_accept() failed: " . socket_strerror(socket_last_error($socket)) . "\n";
continue;
}
$input = socket_read($clientSocket, 1024);
if ($input === false) {
echo "socket_read() failed: " . socket_strerror(socket_last_error($clientSocket)) . "\n";
} else {
$input = trim($input);
echo "收到消息: $input\n";
$response = "你發送了:$input\n";
socket_write($clientSocket, $response, strlen($response));
}
socket_close($clientSocket);
}
在需要退出時,別忘了關閉主socket:
socket_close($socket);
你可以使用命令行客戶端如telnet連接測試:
telnet gitbox.net 9000
或寫一個簡單的PHP 客戶端進行連接測試。
通過socket_create到socket_set_block ,我們構建了一個完整的阻塞式服務器。這個模式適合簡單的網絡通信應用,比如教學、調試或低並發場景。對於高並發場景,建議使用非阻塞模式或搭配select / poll實現更高效的事件循環。
通過本文的實踐步驟,你可以掌握在PHP 中構建阻塞式服務器的全過程,並為進一步深入網絡編程打下堅實基礎。