You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:22:31