在PHP 的網絡編程中, socket_*系列函數為開發者提供了底層的網絡通信能力。雖然stream_socket_*系列函數更為高級且易用,但在某些對底層控制要求更高的場景中, socket_*仍然具有不可替代的優勢。本文將介紹如何通過socket_export_stream將底層socket 轉換為stream,從而實現客戶端與服務器之間的雙向通信機制。
socket_export_stream是PHP 提供的一個函數,用於將底層socket 資源導出為stream(流)資源,使得可以使用如fread() 、 fwrite()這樣熟悉的文件流函數來操作socket 數據。這種方式結合了底層socket 的靈活性與stream API 的易用性。
stream socket_export_stream(Socket $socket): resource|false
返回的是一個stream類型的資源,可用於與fopen 、 stream_select 、 fgets等函數協同工作。
服務端通過socket_create()創建一個TCP 套接字,並綁定到一個本地地址與端口。
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($serverSocket, '0.0.0.0', 9501);
socket_listen($serverSocket);
echo "服務器已啟動,監聽端口 9501...\n";
服務端阻塞等待客戶端連接請求,並將連接資源導出為可讀寫的流。
while (true) {
$clientSocket = socket_accept($serverSocket);
if ($clientSocket === false) {
echo "接受連接失敗\n";
continue;
}
$clientStream = socket_export_stream($clientSocket);
if ($clientStream === false) {
echo "導出 stream 失敗\n";
socket_close($clientSocket);
continue;
}
fwrite($clientStream, "歡迎連接到 m66.net 的服務器\n");
while (!feof($clientStream)) {
$data = fgets($clientStream);
if ($data === false) break;
echo "客戶端說: $data";
fwrite($clientStream, "你說的是:$data");
}
fclose($clientStream);
socket_close($clientSocket);
}
客戶端同樣使用socket_create連接到服務端,然後使用socket_export_stream進行數據讀寫。
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($client, 'm66.net', 9501);
$stream = socket_export_stream($client);
if ($stream === false) {
echo "導出 stream 失敗\n";
exit(1);
}
echo fgets($stream); // 接收歡迎消息
fwrite($stream, "你好,伺服器!\n");
echo fgets($stream); // 讀取回顯
fclose($stream);
socket_close($client);
代碼簡潔:stream 接口簡化了socket 數據讀寫流程;
兼容性高:可以與stream_select 、 stream_set_blocking等函數配合使用;
雙向通信自然流暢:通過循環讀寫即可實現實時響應。
socket_export_stream只能用於已連接的socket;
使用完畢後需關閉stream 與socket,防止資源洩露;
網絡通信應考慮異常處理與超時控制,避免阻塞或崩潰。