当用户关闭页面时,如何阻止服务器发送的事件脚本运行? PHP
Posted
技术标签:
【中文标题】当用户关闭页面时,如何阻止服务器发送的事件脚本运行? PHP【英文标题】:How can i stop a server sent event script from running when the user closes the page? PHP 【发布时间】:2015-03-04 09:37:04 【问题描述】:我试图在 php 后端使用 SSE 向用户发送消息。
后端代码的功能部分看起来像-
<?php
set_time_limit(0);
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
ignore_user_abort(0);
@session_start();
ob_start();
echo "retry: 1000\n";
ob_flush();
flush();
require_once 'sql_connection_class.php';
require 'chatbox_class.php';
$chatbox=new chatboxclass($_GET['friendid']);
function recursivesenddata()
global $chatbox;
$jsonobj=$chatbox->jsonloadunreadmessages();
//append json object
if($jsonobj!=-1)
> echo "data:$jsonobj\n\n";
> ob_flush();
> flush();
> else
> //don't send anything
>
while(true)
recursivesenddata();
session_write_close();
sleep(1);
?>
当用户关闭页面时,ignore_user_abort(0) 似乎没有做任何事情。
在 $chatbox->jsonloadunreadmessages() 中有一个函数应该只在页面打开时执行,它会更新 mysql 数据库中的内容。但是即使页面关闭,这个脚本也会继续在服务器上运行!
当用户关闭页面以退出无限循环时,服务器端是否有检查?
【问题讨论】:
为什么要做无限循环?据我了解,只有在有更新时才应调用代码。你如何发起服务器调用? 只要用户在页面上,我就需要它继续运行,我可以在 x 次睡眠后退出循环,如果没有更优雅的解决方案就发送重试跨度> 我在这里可能不正确,但你不能做一个 吗? 哦,抱歉,我没有指定这个 php 文件是使用 javascript 调用的,例如 =>var source = new EventSource("demo_sse.php");该脚本挂在服务器上,仅在有消息时才发送消息,因此是无限循环。但是一旦启动就无法从客户端阻止它:( 【参考方案1】:应在连接套接字关闭后立即终止 PHP 脚本。如果用户关闭页面后脚本继续运行,则说明您的 Apache/PHP 配置存在严重问题。
当客户端显式调用EventSource.close()
、由于网络问题导致连接丢失、客户端关闭页面或您的PHP脚本终止时,可能会发生套接字关闭。
ignore_user_abort(false)
什么都不做;这是默认行为(脚本在连接关闭时终止)。将true
作为参数传递会使您的脚本在连接中继续存在,但这不会解决您的问题。
您的retry: 1000
没有任何作用,因为您从不关闭服务器端的套接字。
当客户端激活EventSource
对象并仅在客户端请求(或网络出现故障)时终止时,应调用您的 PHP 脚本,因此任何初始数据库调整都应仅在每个聊天连接发生一次。这是假设客户端不做任何会关闭连接的事情。
顺便说一句,这会给服务器带来很大压力:在整个聊天期间,每个聊天客户端都会运行一个 PHP 进程,并且轮询周期太小了大约 10 倍(10 秒绰绰有余)。让客户端每 30 秒左右轮询一次新消息会减少浪费。
【讨论】:
您好,感谢您的回复。我的 Apache/PHP 配置肯定有问题,我在 xampp btw 上测试代码。它到了当我关闭 sse 脚本并在 phpmyadmin 中手动将一行插入 sql 时,该行会自动更新,就好像 sse 仍在运行一样,并且只有当我断开并重新连接 xampp 的 Apache 和 MySQL 时,该行停止更新。重试 1000 只是放在那里,所以当传输过程中发生意外错误时,重试将是 1 秒,而不是默认的 3 秒。 另外,30 秒对于聊天应用程序来说并不是很实时。如果每次发生数据库调整时都需要调用重试,这与 ajax 长轮询有何不同?我只是在我的 while 循环中添加了一个条件,如果没有发送任何内容并调用重试,则使其在 30 秒后超时,尽管我必须在聊天类中重写很多代码才能成为解决方案。抱歉听起来很业余,我对 php 和服务器端的东西很陌生 在你的情况下,长轮询对我来说似乎是最好的解决方案。至于 30 秒的延迟,如果您的客户是无法忍受挫折的小孩,您可以延迟 10 秒,但恕我直言,这肯定会极大地浪费带宽、计算能力和电力。【参考方案2】:ignore_user_abort() 在 IIS 系统上被忽略,您的配置可能会阻止它。
尝试在你的while循环中使用connection_aborted():
while ( connection_aborted() == 0 )
recursivesenddata();
session_write_close();
sleep(1);
【讨论】:
感谢您的回复,即使关闭选项卡,connection_aborted() 似乎也会继续返回 0,我已经更改了 php.ini 文件上的 ignore_user_abort= Off 没有运气。以上是关于当用户关闭页面时,如何阻止服务器发送的事件脚本运行? PHP的主要内容,如果未能解决你的问题,请参考以下文章
PHP,暂停脚本执行,直到另一个正在运行的脚本发出事件信号?
Angular 5 - 当用户关闭浏览器窗口时如何进行 API 调用?