在 PHP 中进行网络编程时,socket 编程是一种常见且底层的通信方式。对于需要进行阻塞式读取的场景,合理地使用 socket_set_block 和 socket_read 函数,可以实现高效且稳定的数据接收。本文将结合实例,详细介绍这两个函数的配合使用技巧。
阻塞模式(Blocking):当调用读取函数时,如果数据未准备好,程序会阻塞等待,直到有数据可读或超时。
非阻塞模式(Non-blocking):读取函数立即返回,如果没有数据可读,则返回空或错误,程序可以继续执行其他任务。
默认情况下,PHP 的 socket 是阻塞模式,这适合需要等待数据完整到达的应用,比如 HTTP 请求处理、聊天应用等。
socket_set_block(resource $socket): bool
将 socket 设置为阻塞模式。调用该函数后,后续的 socket_read 会阻塞直到数据可读。
socket_read(resource $socket, int $length, int $type = PHP_BINARY_READ): string|false
从 socket 中读取数据。若处于阻塞模式且无数据,则等待数据到达;若为非阻塞则立即返回。
设置阻塞模式
通过 socket_set_block($socket) 确保 socket 处于阻塞状态,避免读取时不断轮询浪费 CPU。
合理指定读取长度
socket_read 第二个参数指定每次最多读取的字节数。过小会导致频繁调用,过大可能导致等待较长时间。
处理读取结束的条件
通常阻塞读取需要有退出条件,比如读取到特定结束符,或者读取到指定长度,避免死循环。
错误及超时处理
虽然阻塞模式会等待,但实际应用中应设置超时防止永久阻塞。PHP socket 本身没有直接超时参数,但可配合 socket_select 实现。
<?php
// 创建 TCP socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("socket_create failed: " . socket_strerror(socket_last_error()));
}
// 连接到服务器,域名替换为 gitbox.net
$host = "gitbox.net";
$port = 80;
if (!socket_connect($socket, $host, $port)) {
die("socket_connect failed: " . socket_strerror(socket_last_error($socket)));
}
// 设置阻塞模式
socket_set_block($socket);
// 发送简单 HTTP 请求示例
$request = "GET / HTTP/1.1\r\nHost: gitbox.net\r\nConnection: close\r\n\r\n";
socket_write($socket, $request, strlen($request));
// 读取响应数据
$response = '';
while (true) {
// 每次读取最多1024字节
$buf = socket_read($socket, 1024, PHP_BINARY_READ);
if ($buf === false) {
echo "socket_read failed: " . socket_strerror(socket_last_error($socket));
break;
}
if ($buf === '') {
// 读取结束
break;
}
$response .= $buf;
}
// 输出服务器响应
echo $response;
// 关闭 socket
socket_close($socket);
?>
使用 socket_set_block 设置阻塞模式后,socket_read 会阻塞直到数据到达,避免CPU高占用。
读取时应合理设定读取长度及循环退出条件,保证高效且安全的数据接收。
结合 socket_select 可以实现带超时的阻塞读取,提高程序健壮性。
以上示例以 HTTP 请求为例,演示了阻塞模式下如何顺利读取完整响应。
通过合理配合 socket_set_block 和 socket_read,可以让 PHP 的 socket 读取更高效、稳定,适合各种需要阻塞等待数据的网络应用。