在 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