【发布时间】:2010-11-28 18:47:21
【问题描述】:
典型的 PHP 套接字功能是同步的,并在等待传入连接和数据时暂停线程。 (例如socket_read 和socket_listen)
我如何异步执行相同的操作?所以我可以在数据接收事件中响应数据,而不是轮询数据等。
【问题讨论】:
标签: php sockets scripting asynchronous webserver
典型的 PHP 套接字功能是同步的,并在等待传入连接和数据时暂停线程。 (例如socket_read 和socket_listen)
我如何异步执行相同的操作?所以我可以在数据接收事件中响应数据,而不是轮询数据等。
【问题讨论】:
标签: php sockets scripting asynchronous webserver
“异步”一词在网络编程中经常被误用。对于 I/O,异步通常只是用作非阻塞的另一个词。这意味着该过程能够在网络 api 上的调用完成传输之前继续。
对于一般的流程执行,异步意味着能够一次(同时)计算多条指令。
换句话说,异步 I/O 不是真正的异步,除非使用多个线程来允许同时发生多个读/写/接受 - 如果有数据,所有套接字都必须等待同步非阻塞调用被读/写或不会阻塞,如果不中断,读/写一个大文件仍然需要几秒钟甚至几分钟。请注意,这将需要客户端和服务器之间的完美流,否则 TCP 本身会中断传输。例如,服务器发送速度快于客户端下载速度会导致写入阻塞。
所以从严格的角度来看,PHP 不能执行异步网络,只能执行非阻塞。简而言之,当网络调用能够有效地读/写等时,进程的进程将停止。但是,当调用无法有效地读/写或以其他方式阻塞时,进程将继续。在真正的异步系统中,进程将继续进行,并且读/写将在不同的线程中完成。请注意,如果在不同的线程中完成,阻塞 I/O 仍然可以异步完成。
此外,如果没有安装支持它的扩展,PHP 无法进行事件驱动 I/O。否则,您将需要进行某种形式的轮询,以便在 PHP 中进行非阻塞 I/O。如果使用 socket_select,来自 Chaos 的代码将是一个功能性非阻塞读取示例。
话虽如此,select 函数仍将允许 PHP 中的真正非阻塞行为。在 C 语言中,轮询服务比事件驱动有性能损失,所以我相信 PHP 也是如此。但是这种损失在纳秒到微秒内,具体取决于套接字的数量,其中从非阻塞调用节省的时间通常是毫秒,如果调用等待则甚至是几秒。
【讨论】:
我如何异步执行相同的操作? 所以我可以响应数据中的数据 收到事件,而不是轮询 数据等
您需要执行脚本并发出 stream_select 以检查是否有任何数据要接收。处理并发送回数据。
【讨论】:
AFAIK PHP 是严格单线程的,这意味着您不能异步执行此操作,因为脚本执行始终是线性的。
我已经有一段时间没有这样做了,但据我记得,您只能打开套接字,并让脚本在接收到数据后继续执行。
【讨论】:
是的,这就是socket_set_nonblock() 的用途。考虑到错误代码 11、EWOULDBLOCK 和 115、EINPROGRESS 所假定的特殊含义,您的套接字交互代码需要以不同方式编写。
根据要求,这是来自 PHP 同步套接字轮询循环的一些虚构的示例代码:
$buf = '';
$done = false;
do {
$chunk = socket_read($sock, 4096);
if($chunk === false) {
$error = socket_last_error($sock);
if($error != 11 && $error != 115) {
my_error_handler(socket_strerror($error), $error);
$done = true;
}
break;
} elseif($chunk == '') {
$done = true;
break;
} else {
$buf .= $chunk;
}
} while(true);
【讨论】: