在使用 PHP 进行网络编程时,socket_set_block() 和 socket_set_nonblock() 是两个非常重要的函数,它们控制 socket 是否以阻塞或非阻塞模式工作。合理地切换这两种模式,能帮助我们更灵活地处理网络 I/O,提高程序的效率与响应能力。
阻塞模式(默认):在该模式下,socket 的操作(如 socket_read()、socket_accept() 等)会一直等待直到有数据可读或者连接完成。这种方式写起来简单,但在高并发场景中可能造成线程或进程阻塞,降低整体性能。
非阻塞模式:相反地,非阻塞模式下的 socket 操作会立即返回。如果操作不能立即完成(如还没有数据可读),函数会返回 false 并设置错误码为 EAGAIN 或 EWOULDBLOCK。
下面是一个简单的示例,演示如何切换 socket 的阻塞与非阻塞状态,并结合应用场景使用:
<?php
$host = 'gitbox.net';
$port = 8080;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("socket_create() failed: " . socket_strerror(socket_last_error()));
}
// 设置为非阻塞模式,尝试连接
socket_set_nonblock($socket);
$connected = @socket_connect($socket, $host, $port);
if (!$connected) {
$err = socket_last_error($socket);
if ($err !== SOCKET_EINPROGRESS && $err !== SOCKET_EALREADY) {
die("socket_connect() failed: " . socket_strerror($err));
}
}
// 采用 select 检查连接是否成功
$write = [$socket];
$null = [];
if (socket_select($null, $write, $null, 5) > 0) {
// 切回阻塞模式,用于后续数据收发
socket_set_block($socket);
// 开始收发数据
socket_write($socket, "GET / HTTP/1.1\r\nHost: $host\r\n\r\n");
$response = socket_read($socket, 2048);
echo "Response: " . $response;
} else {
die("Connection timeout or failed");
}
socket_close($socket);
?>
非阻塞连接:通过 socket_set_nonblock() 避免连接操作阻塞程序执行。
使用 socket_select() 等待 socket 可写:这是检测连接是否成功的常用技巧。
切换回阻塞模式:连接建立后立即用 socket_set_block() 切换回阻塞模式,方便后续的数据交互。
发送请求与读取响应:此时的 socket 工作在阻塞模式,便于读取完整的服务器响应。
需要快速发起多个连接请求时,使用非阻塞模式;
需要确保某一步操作不会无限等待时,如超时控制;
完成连接或特定阶段后,切换回阻塞模式简化代码处理逻辑。
在非阻塞模式下,所有 socket 操作都必须进行错误检查;
socket_select() 是非阻塞 socket 编程中的关键工具;
切换模式要根据操作阶段来进行,而非一成不变。
通过合理地切换阻塞与非阻塞模式,PHP 的 socket 编程不仅能兼顾性能和稳定性,还能更好地应对复杂的网络通信需求。