Websockets - 获取数据(轮询?)

4

对于那些已经理解它的人来说,这可能是一个愚蠢的问题,也许我只需要更多的

问题:无论使用还是,似乎仍然存在一些轮询。这是正确的吗?

示例(非真实项目):我想关注一个文本文件。除非我漏掉了某些东西(需要更多咖啡?),不是我仍然必须要么a)向服务器询问是否有更新,或者b)告诉页面我有更新;通过在PHP代码上设置睡眠时间或在客户端设置setTimeout循环。

我理解的内容: 我已经看到了通过服务器和页面之间的交互所带来的好处。我看到我没有发送http请求。因此,我看到了好处。 细节: 我一直只使用,所以我决定去了解一下这个websocket的东西,因为我认为我理解的是实时向客户端发送数据,但是,就像上面所说的,除非我错过了什么或者逻辑有问题,否则似乎我仍然需要告诉以间隔检查数据,否则数据将在无限循环中被发送(想象一下调用mysql)。也许我的代码逻辑有问题。你可以查看一下。从我找到的所有例子中,每个人似乎都只在PHP中运行一个无限循环。

PHP(减去所有连接术语)

while(true) {
    // update once a second
    $this->send($client, file_get_contents('/my/file/test.txt'));
    sleep(1);
}

Javascript

var websocket = new WebSocket( "ws://mysite.com:12345" );

websocket.onmessage = function( str ) {
    console.log( str.data );
};

我只是不理解如何在没有轮询的情况下使其实时,也许这就是它应该工作的方式。
我确实明白,如果我从代码中删除sleep,事情会变得更加实时,但这似乎会无限期地轮询上面示例中的文件,这看起来不对。
编辑:为了澄清,我并不特别寻找观察文本文件的特定解决方案。如果你匆忙浏览问题,可能会这样想。
编辑:未来的访问者,解决方法是:当用户发送更改时,将更改发送到打开的连接,而不是专门观察更改。

Websocket/Socket的一般目的是保持一个始终打开的恒定连接,因此在聊天程序等事件中,客户端只有在数据可用时才接收数据,通过等待并触发接收事件,它们可以节省带宽,而不必每隔几秒钟轮询/发送到服务器以显示响应。关于监视服务器端文件,这需要轮询。但是服务器本身会轮询文件并向所有客户端发送更新,而不是所有客户端每隔几秒钟检查服务器,这里Websockets仍然可以节省带宽。 - Angry 84
正如所说,您仍然可以让PHP本地轮询文件,然后在检测到更改时通知客户端,至于如何轮询文件。这可能会有所不同,您可以使用CRON作业,但需要在另一个线程/PHP脚本上运行套接字服务器。可能使用http://php.net/manual/en/function.fam-monitor-file.php,否则我只需在套接字服务器的主要循环中运行像FAM函数之类的东西,如果检测到更改,则发送所有消息。 - Angry 84
请澄清一下,您是用“是”的回答来回答这部分“问题:无论是使用WebSockets还是Ajax,似乎仍然存在一些轮询。这正确吗?”吗? - Jesse
对于纯客户端和服务器通信,没有轮询。如果要使PHP能够检测到服务器端文件的更改,则需要进行某种形式的轮询。当1000个客户端都通过Ajax轮询您的服务器并且每个客户端都触发服务器检查时,将进行1000次文件检查。WebSocket只是服务器在其自己的循环中轮询并在条件成立时发送消息。因此,套接字在您的需求方面更好。至于文件,只需使用标准的PHP代码来检查文件是否已更改。 - Angry 84
虽然我知道问题的主题不同。但是很久以前我问过一个关于套接字和轮询的问题。我相信 Bradley 提供的答案可能会有所帮助。http://stackoverflow.com/questions/25456898/sockets-polling-socket-less-results 主要是为了让事情变得更加清晰。这可能是一个冒险,但可能会有回报。 - Daryl Gill
显示剩余6条评论
3个回答

0

我建议最初采用类似这样的方法。

$sFile = "/my/file/test.txt";
$timeMod = filemtime($sFile);
while(true) {
    if (filemtime("SomeFileHere.txt")!==$timeMod ) {
        $timeMod = filemtime($sFile);
        // File has changed, update variable with new timestamp
        $this->send($client, file_get_contents($sFile));
    } else {
        // No change, do nothing here.
    }
    sleep(1);
}

基本上,在循环之前,您会获取最后修改日期。如果在循环中它发生了变化,我们会更新变量并在那时发送警报。

如果要在现实世界中使类似于此的东西正常工作而没有任何负载问题,我将运行一个单独的PHP文件在一个循环中(可能是守护进程)... 它将像上面那样每秒监视文件。如果有更改,我将让它通知另一个PHP脚本/线程向所有客户端发送内容。

如果相同的文件发送到所有客户端,这不会太糟糕。因为您可以使用“发送到所有”功能。但是,如果每个客户端都不同(例如他们自己的历史记录/日志),则需要对每个客户端进行处理,这将稍微影响性能。

为了进一步未来证明这样的东西,您将记录客户端上次接收文件内容的时间,并限制多长时间才能再次发送。因此,客户端只在10秒钟后才会获得新副本,任何小于此时间的都会被忽略。

这适用于什么范围/规模和类型的项目?

目前我的Python服务器可以接收来自20,000个客户端的请求并处理他们的数据,并在每秒内回复所有请求...(GPS处理)。虽然每次发送的数据不超过1千字节,但使用Python中的线程和队列将使其成为更好的方法,并且可以在单个实例中处理这么多客户端。而且只使用约130MB的RAM,没有随时间推移而出现的内存泄漏。

我个人认为PHP不应该永远在循环中运行,这感觉不像它应该被使用的方式。


完全可以使用我的示例来解决这个问题(这仅适用于示例,不是我要执行的真实任务,它只是一个示例)。老实说,我认为你没有完全理解这个问题。 - Jesse
可以再解释一下吗?如果我们现在不在同一水平上,我可以理解。 - Angry 84
我并不特别寻求观看文本文件的具体解决方案。 - Jesse
PHP会等待任务完成后才继续执行下一行代码,它确实有线程能力,但不建议使用。基本上,对于这个例子,它可以工作...但是每个客户端都会排队,随着时间的推移会导致滞后。对于多客户端监视服务器,我使用了Python,它允许我使用真实的事件/控制来管理每个客户端与标准队列。PHP在这里非常有限,不是这种类型用途的理想语言/解决方案。(个人经验) - Angry 84

0

Websockets允许您完全避免轮询,只要您控制所有事件(或Sub/Pub到外部事件)。

至于您的示例,如果您控制写入文件的操作,则可以调用websocket“广播”或“发布”此事件。

通过这种方式,您完全避免了轮询。

由于我不喜欢使用PHP(无意冒犯,我只是已经厌倦了它),这里有一个快速的Ruby示例,使用Plezi实时框架

在这个例子中,我们使用一个简单的touch方法来执行一个动作。虽然我并没有真正触摸文件,但您可以体验到API的使用使我能够控制事件并向其他用户广播-没有涉及轮询

如果我订阅外部事件,情况也是如此。

要运行此示例,请使用[sudo] gem install plezi安装plezi gem(取决于您是否需要sudo和您的系统),并使用irb命令从终端打开IRB终端。然后粘贴以下代码:

需要 'plezi'

class RootController
    def index
        %{<html><head>
<script>
    var websocket = NaN;
    function connect() { websocket = new WebSocket( (window.location.protocol.match(/https/) ? 'wws' : 'ws') + '://' + window.location.hostname + (window.location.port == '' ? '' : (':' + window.location.port) ) + "/" ); }
    function init()
    {
        connect()
        websocket.onopen = function(evt) { WriteMessage("(Connected and waiting for messages)", "connection") };
        websocket.onclose = function(evt) { WriteMessage("(Disconnected. messages will be lost)", "connection");connect();  };
        websocket.onmessage = function(evt) {
            WriteMessage(evt.data, "");
        };
        websocket.onerror = function(evt) { WriteMessage(evt.data, 'error'); };
    }
    function WriteMessage( message, message_type )
    {
        if (!message_type) message_type = 'received'
        var msg = document.createElement("p");
        msg.className = message_type;
        msg.innerHTML = message;
        document.getElementById("output").appendChild(msg);
    }
    function Send(message)
    {
        WriteMessage(message, 'sent'); 
        websocket.send(message);
    }
    window.addEventListener("load", init, false);
  </script></head>
<body>
<p>Messages should show up here:</p>
<div id=output></div>
</body>
</html>
        }
    end
    def touch
        FileController.touch
        "You Touched the file, a message should be sent to the web browser windows."
    end
    def on_open
        subscribe :file_notifications
    end
    def on_message data

    end
    def self.push_update_event
        publish :file_notifications, "The file was updated."
        "Touched - Ok".freeze
    end
end

class FileController
    def self.touch
        puts "INFO: A file should be touched.. you can do whatever you feel like here..."
        RootController.push_update_event
    end
end

class APIController
    def touched
        RootController.push_update_event
    end
end

Plezi.route '/', RootController
Plezi.route '/api', APIController

exit # the server will start once you exit the irb terminal

现在在两个不同的浏览器窗口中访问:

或者,您甚至可以使用外部脚本“编辑文件”(虚拟),然后访问http://localhost:3000/api/touched以通知所有用户该操作(此处未显示身份验证,但应添加)。


1
我现在明白了。通过睡一晚上,并看到您有关在两个浏览器窗口中访问的响应,我想我一直在老式的一个用户一次地考虑它。而不是等待更改,当一个用户发送更改时,我会更新所有客户端。我知道我之前的想法是错误的。 - Jesse
我意识到我的回答可能已经过时了(因为我是5年前写的)...但是为什么在2020年11月(5年后)要对其进行投票贬低呢?而且为什么不留下评论,这样我就可以修正任何“错误”(如果有的话)... ‍♂️ - Myst
我只能假设有人正在使用带有PHP标签的websocket php内容进行搜索,并看到了无法复制粘贴的Ruby代码。 - Jesse

-4

这里需要事件驱动编程。使用一个处理事件的库会更容易。PHP不是这种类型编程的最佳工具,但仍然可能有适用的库。

一种解决方案是拥有一个nodejs/socket.io服务器,当发生有趣的事情时,您的php进程可以向其发送消息。然后,nodejs服务器将把消息传递给客户端。


4
我真的希望不要收到非 Node.js 问题的 Node.js 回复。我知道 Node.js,但这个问题是关于 PHP 而不是特定库的。具体而言,它涉及在 PHP 中如何轮询新内容,不需要使用任何库,也不涉及 Node.js。这更适合作为评论。 - Jesse
3
我无法忍受现在所有的东西都指向了Node.js...在PHP中制作一个WebSocket服务器很容易,只需要人们能够编程而不是安装另一个该死的库。Python最终成为我建立WebSocket服务器的选择,因为它更具控制性且需要更少的资源。 - Angry 84
3
@Mayhem - 我完全同意。这就像看到一个JavaScript问题,第一个答案是“你应该使用jQuery”。 - Jesse
你询问了一种不需要轮询处理“实时”数据的方法,我建议使用基于事件的模型。如果你只是需要监视一个文件,PHP确实有inotify的包装器。在这里轮询的最大问题是需要读取文件并访问磁盘,而IO操作可能会成为一个很大的瓶颈,相信你已经意识到了这一点。 - Jason Rice
Javascript是一种基于事件的编程语言,因此建议如此。 - Jason Rice
这个问题并不特定于一个文本文件(这就是为什么它被标记为示例(非真实项目),而是特定于PHP处理这个逻辑的方式。 - Jesse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接