在PHP 中,socket 編程是實現網絡通信的基礎手段之一。默認情況下,socket 可能是非阻塞的,這意味著在沒有數據的時候,讀取操作會立即返回而不是等待數據到達。而阻塞式Socket 則會讓程序在讀取數據時等待,直到有數據可讀,適合實現簡單的同步聊天程序。
本文將介紹如何使用PHP 的socket_set_block函數,將Socket 設置為阻塞模式,並基於此實現一個簡單的阻塞式聊天程序示例。
阻塞式Socket 是指當執行讀取或寫入操作時,如果沒有數據可用,程序會暫停等待,直到有數據到來或者操作完成後再繼續執行。這種方式實現起來簡單,邏輯清晰,非常適合初學者和小規模聊天應用。
相比之下,非阻塞式Socket 需要使用事件輪詢或異步操作,邏輯複雜但更適合高性能需求。
socket_set_block是PHP 中用於設置Socket 阻塞模式的函數。調用此函數後,Socket 將切換為阻塞狀態。
函數原型:
bool socket_set_block(resource $socket)
$socket :需要設置的socket 資源
返回值:成功返回true ,失敗返回false
下面是一個簡易的阻塞式Socket 聊天服務器示例,客戶端連接後可以發送消息,服務器接收後會簡單回顯。
<?php
$host = "0.0.0.0";
$port = 12345;
// 創建 Socket
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($sock === false) {
die("socket_create() 失敗: " . socket_strerror(socket_last_error()));
}
// 綁定地址和端口
if (!socket_bind($sock, $host, $port)) {
die("socket_bind() 失敗: " . socket_strerror(socket_last_error($sock)));
}
// 監聽端口
if (!socket_listen($sock, 5)) {
die("socket_listen() 失敗: " . socket_strerror(socket_last_error($sock)));
}
echo "服務器啟動,監聽 {$host}:{$port}...\n";
// 接受客戶端連接
$client = socket_accept($sock);
if ($client === false) {
die("socket_accept() 失敗: " . socket_strerror(socket_last_error($sock)));
}
// 設置客戶端 Socket 為阻塞模式
socket_set_block($client);
echo "客戶端連接成功,等待消息...\n";
while (true) {
// 阻塞讀取客戶端數據
$data = socket_read($client, 2048, PHP_NORMAL_READ);
if ($data === false) {
echo "socket_read() 失敗: " . socket_strerror(socket_last_error($client)) . "\n";
break;
}
$data = trim($data);
if ($data === '') {
continue;
}
echo "收到客戶端消息: $data\n";
if ($data === 'quit') {
echo "客戶端斷開連接\n";
break;
}
// 回顯消息給客戶端
$response = "服務器已收到: $data\n";
socket_write($client, $response, strlen($response));
}
// 關閉 Socket 連接
socket_close($client);
socket_close($sock);
?>
將上述代碼保存為server.php ,在命令行運行:
php server.php
使用telnet 或其他TCP 客戶端連接:
telnet gitbox.net 12345
(這裡示例中端口為12345,域名示例換成gitbox.net )
輸入消息,服務器會阻塞等待你的輸入,收到消息後返回確認。
輸入quit即可斷開連接。
使用socket_set_block可以非常方便地將Socket 設置為阻塞模式,從而實現簡單的同步聊天邏輯。阻塞式編程適合初學者快速上手網絡編程,但在高並發場景下,非阻塞或異步方式更為高效。
更多關於PHP Socket 編程的資料,可以訪問:
https://gitbox.net/manual/socket