套接字服务器+事件源魔术断开

时间:2013-12-16 14:21:04

标签: php sockets server-sent-events

我已经成功实现了下一个方案:一个php套接字服务器脚本,它向所有连接的客户端脚本广播消息,这些客户端脚本由前端脚本上的event-source监听。所以,基本上,方案是:server.php - > client.php - > front.php

但是我一直在努力从client.php

开始
while(TRUE)
{
    $read   = array();
    $read[] = $client_socket;

    if( socket_select($read, $write, $except, 10) === FALSE ){
        $errorcode = socket_last_error();
        $errormsg = socket_strerror($errorcode);
        file_put_contents("select_result.log", "Could not listen on socket : [$errorcode] $errormsg".PHP_EOL);
    }

    if( !($client_message = @socket_read($client_socket, 1024)) ){
        $errorcode = socket_last_error();
        $errormsg = socket_strerror($errorcode);
        file_put_contents("read_result.log", "Couldn't read socket: [$errorcode] $errormsg".PHP_EOL, FILE_APPEND);
    }

    if($client_message == FALSE) {
        $client_message  = "event: keep_alive" . PHP_EOL
                         . "data: keep_alive" . PHP_EOL . PHP_EOL;
    }

    echo $client_message;

    if( ob_get_level() > 0 ) ob_flush();
    flush();
}

在浏览器中打开front.php脚本(使用 event-source )时,它可以正常工作,但是当我关闭时它也会以某种方式检测到断开连接或在浏览器窗口中重新加载front.php。以下是检测断开连接的server.php的一部分:

if( !($client_input = @socket_read($client, 1024))) {
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    echo "$errorcode".PHP_EOL;
}

if ( $client_input === FALSE ) {
    $socket_key = array_search($client, $client_sockets);
    socket_getpeername($client, $client_address, $client_port);
    socket_close($client);//?!!!
    unset($client_sockets[$socket_key]);
    echo "[$client_address : $client_port disconnected]".PHP_EOL;
}

如您所见,当无法从客户端读取数据时会发生这种情况。从理论上讲,当我关闭front.php时,client.php应该仍然在背景上无休止地工作,因为我没有任何循环中断,但是 - 惊喜! - 它停止了实验上,我发现由于ob_flush()函数而发生这种情况。在评论时,server.php未检测到断开连接(浏览器窗口关闭/重新加载)。怎么会这样?根据手册,ob_flush()不会返回任何值。没有检测到错误......我不知道该怎么想,我也找不到为什么会发生这种情况。请帮忙。

2 个答案:

答案 0 :(得分:1)

我假设您在client.php前面使用Apache或其他东西。当front.php运行var es = new EventSource('client.php')时,会发生在浏览器和Apache之间创建专用TCP / IP套接字的情况。 Apache运行PHP,告诉它加载并运行client.php。 (然后client.php创建一个套接字来监听来自server.php的消息。)

当浏览器关闭时(或者您调用es.close(),或者由于某种原因,浏览器和Apache之间的套接字连接丢失),Apache将立即(*)关闭PHP进程(运行客户端的进程) .PHP)。当PHP进程消失时,client.php和server.php之间的套接字将立即关闭(或者下次server.php尝试读/写)。

*:通常是“立即”。有时如果插座没有干净地关闭,它可能会暂停一段时间(几秒钟,不会超过一分钟)。

我认为你的ob_flush()观察有点红鲱鱼;我的猜测是,通过不调用ob_flush(),缓冲区中存在某些东西,这意味着Apache会使PHP进程保持活动状态,直到超时为止。顺便说一句,我使用@ob_flush();@flush()成语; @之前的ob_flush()基本上是在进行ob_get_level()检查所做的事情(但我依旧记得听说有一种情况,检查并不总是可靠的。)

答案 1 :(得分:0)

将输出缓冲区刷新到浏览器不再像预期的那样使用最新版本的Chrome / Firefox。

你需要某种register_shutdown_func(),它不能稳定以及你想要的目的。

由于HTTP协议的设计,将客户端/服务器放在通过浏览器加载的PHP脚本中通常非常不稳定。它不是为保持连接存活而设计的,而是以IRC为例的方式发送消息。由于许多原因,HTTP连接可能会在30-60秒后关闭。

我建议您将服务器和JavaScript的PHP命令行版本用于客户端。

你加载PHP的方式(apache中的mod_php?)会影响整个事情。你打开了多少个PHP / Apache线程/分支,每个进程有多少个连接,...

有些网络服务器会在页面完成加载几秒后终止任何脚本,很难调试并且很难稳定。

尝试使用JavaScript中的WebRTC / WebSockets,它可以更好地集成到任何HTML5网站中。