【问题标题】:Socket server + Event source magic disconnect套接字服务器+事件源魔术断开
【发布时间】:2014-01-03 23:23:21
【问题描述】:

我已经成功实现了下一个方案:一个 php 套接字服务器脚本,它向所有连接的客户端脚本广播消息,并且这些客户端脚本由前端脚本上的事件源监听。所以,基本上,方案是: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() 不返回任何值。也没有检测到错误...我不知道该怎么想,也找不到为什么会发生这种情况。请帮忙。

【问题讨论】:

  • 是“front.php”HTML 页面,包含 JavaScript 等。即出于这个问题的目的,它真的可以称为“front.html”吗? (或者,你的 HTML 客户端使用 EventSource 连接到的 front.php 是什么?)。换句话说,在你的浏览器运行的 JavaScript 中,你是 new EventSource("client.php") 还是 new EventSource("front.php")
  • 你是对的,是的,它可以很容易地称为front.html,而我是new EventSource("client.php")client.php 然后打开到server.php 的套接字连接。

标签: php sockets server-sent-events


【解决方案1】:

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

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

*:通常是“立即”。有时,如果套接字没有完全关闭,它可能会挂起一段时间(几秒钟,永远不会超过一分钟)。

我认为您对ob_flush() 的观察有点偏题;我的猜测是,如果不调用ob_flush(),就会有一些东西卡在缓冲区中,这意味着 Apache 会保持 PHP 进程处于活动状态,直到某个超时到期。顺便说一句,我使用@ob_flush();@flush() 成语; @ob_flush() 之前的 @ 基本上是在做你的 ob_get_level() 检查正在做的事情(但我隐约记得听说有一种情况,检查并不总是可靠的)。

【讨论】:

  • 哇,从您的回答中,我已经完全理解正在发生的事情......再次,您真的帮助我完成了我的项目。干杯:)
【解决方案2】:

使用最新版本的 Chrome/Firefox 将输出缓冲区刷新到浏览器(不再)无法正常工作。

您将需要某种register_shutdown_func(),它对于您想要的目的也不稳定。

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

我建议你在服务器端使用 PHP 命令行版本,在客户端使用 JavaScript。

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

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

尝试使用 JavaScript 中的 WebRTC / WebSockets,这样做会更好,并且可以更好地集成到任何 HTML5 网站中。

【讨论】:

  • 感谢您的回复;但是,我已经在各种浏览器(旧/新版本,包括 IE 8+)和两个不同的服务器(没有提到本地主机)上测试了这个方案。您误解了一点,因为我正在通过命令行运行server.php,而JavaScript 事件源front.php 用于启动并侦听client.php 输出的客户端。通过每 10 秒(可能更多)发送一些 data: 来保持连接处于活动状态。你说的都是合理的,但是时间很短——我不能用 WebSockets 重写所有的东西。真正困扰我的只是ob_flush 的小问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-02
  • 2012-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多