set_time_limit对fsockopen无效,代码无法按时终止的解决方案咨询
解决VOIP事件读取脚本60秒自动终止的问题
核心原因
set_time_limit() 和 fsockopen 的超时参数之所以无效,是因为PHP的最大执行时间仅统计CPU运行时间,socket的IO等待(比如fgets()阻塞等待数据)不会计入这个时间,导致脚本可以长时间运行。
可行解决方案
1. 手动跟踪脚本运行时长(最可靠,无需扩展)
在脚本启动时记录时间戳,每次循环都检查是否超过60秒,达到时长就主动终止循环或脚本:
set_time_limit(60); $startTime = time(); // 记录脚本启动时间 echo ini_get('max_execution_time');// 返回60 $socket = fsockopen("192.168.10.4","5038", $errno, $errstr,60); if (!$socket){ echo " 222 $errstr ($errno)\n"; }else{ fputs($socket, "Action: Login\n"); fputs($socket, "UserName: ariacrm\n"); fputs($socket, "Secret: ************\n\n"); stream_set_timeout($socket, 60); $event = ""; include_once('config.php'); $db = db(); while($ret = fgets($socket)){ // 检查是否已运行60秒,达到则终止循环 if (time() - $startTime >= 60) { fputs($socket, "Action: Logoff\n\n"); // 主动登出VOIP服务 fclose($socket); exit("脚本已运行60秒,主动终止"); } if(substr($ret,0,6) == "Event:"){ $e = explode(':', $ret); $event = trim($e[1]); } if($event == "DeviceStateChange"){ $data = explode(':', $ret); // 剩余业务代码 } } }
2. 使用stream_select监听socket并设置超时
通过stream_select可以设置socket的监听超时,避免fgets()无限阻塞:
set_time_limit(60); $socket = fsockopen("192.168.10.4","5038", $errno, $errstr,60); if (!$socket){ echo " 222 $errstr ($errno)\n"; }else{ fputs($socket, "Action: Login\n"); fputs($socket, "UserName: ariacrm\n"); fputs($socket, "Secret: ************\n\n"); $event = ""; include_once('config.php'); $db = db(); $startTime = time(); while(true){ // 设置监听超时为1秒,每隔1秒检查一次运行时长 $read = [$socket]; $write = null; $except = null; if(stream_select($read, $write, $except, 1) > 0){ $ret = fgets($socket); if(substr($ret,0,6) == "Event:"){ $e = explode(':', $ret); $event = trim($e[1]); } if($event == "DeviceStateChange"){ $data = explode(':', $ret); // 剩余业务代码 } } // 检查总运行时长 if(time() - $startTime >=60){ fputs($socket, "Action: Logoff\n\n"); fclose($socket); exit("脚本超时终止"); } } }
3. 使用pcntl_alarm(需CLI模式+pcntl扩展)
如果脚本在CLI模式下运行,且PHP启用了pcntl扩展,可以设置闹钟信号,60秒后自动终止脚本:
// 注册信号处理函数 pcntl_signal(SIGALRM, function() { global $socket; fputs($socket, "Action: Logoff\n\n"); fclose($socket); exit("收到闹钟信号,脚本终止"); }); // 设置60秒后发送SIGALRM信号 pcntl_alarm(60); set_time_limit(60); $socket = fsockopen("192.168.10.4","5038", $errno, $errstr,60); // 后续业务代码同原脚本
注意事项
- 无论哪种方法,终止前建议主动发送
Logoff指令给VOIP服务,避免无效连接残留。 stream_set_timeout()仅设置socket单次读写的超时,无法控制脚本总运行时长,必须结合时间戳检查。
内容的提问来源于stack exchange,提问作者golsa goli
相关产品推荐
相关产品推荐

