Websockets -获取数据(轮询?)


Websockets - Getting Data (Polling?)

对于那些已经在思考这个问题的人来说,这可能是一个愚蠢的问题,也许我只是需要更多的咖啡。

问题:是否使用websockets或ajax,似乎仍然有一些轮询发生。这是正确的吗?

示例 (不是真正的项目):我想关注一个文本文件。除非我错过了一些东西(更多的咖啡?),我是不是仍然必须要么a)询问服务器是否有更新,或b)告诉页面我有更新;通过在一段时间内休眠PHP代码或在客户端使用setTimeout循环。

我确实理解的事情:我肯定已经看到了在服务器和页面之间来回通信的好处。我看到我没有发送http请求。所以我看到了好处。

细节:我一直只是使用xmlhttprequest所以我决定看看这整个websockets我认为我理解,是实时数据发送给客户端,但是,像上面提到的,除非我失踪或一些逻辑的东西,似乎我还告诉php或javascript检查间隔的数据,否则数据被发送在一个无限循环(想象调用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 );
};

我只是没有掌握这个逻辑,我如何能使它实时没有某种轮询。也许这就是它应该如何工作的。

我明白,如果我从php代码中删除sleep,事情会变得更加实时,太多了,但这似乎会在上面的例子中无限轮询文件,这似乎是不对的。

编辑:澄清一下,我不是专门寻找一个特定的解决方案来观看文本文件。如果你浏览一下这个问题,你可能会想到这个。

Edit:未来的访问者,这个问题的答案是:当用户发送更改时,不是专门监视更改,而是将更改发送给打开的连接。

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

对于你的例子,如果你控制写入文件的动作,那么你可以调用websocket "broadcast"或"publish"这个事件。

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

由于我讨厌使用PHP(无意冒犯,我已经受够了),这里有一个使用Plezi实时框架的快速Ruby示例。

在这个例子中,我们使用一个简单的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/
  • http://localhost: 3000/碰

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

我最初会建议类似的事情。

$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文件在循环中运行(可能是daameon)。它将像上面那样以一个简单的循环每秒监视文件。如果有变化,我会让它通知另一个PHP脚本/线程发送的内容到所有的客户端。

如果同一个文件被发送到所有的客户端,这不会太糟糕。因为你可以使用send to all函数。但如果每个客户端都不一样(比如他们自己的历史/日志)…它将需要每个客户端处理,这将对性能造成一点影响。

为了进一步证明这样的东西,你可以记录下客户端最后一次接收文件内容的时间,并限制它可以再次发送的时间。所以客户端只在10秒后得到一个新的副本,少于10秒,它就忽略了。

到什么范围/规模&这是什么类型的项目?

目前我的python服务器可以接收到来自20,000个客户端的请求,并处理他们的数据并在一秒钟内响应。(GPS处理)。虽然我不会每次发送超过1000字节的内容,但python中的线程和队列功能将使其成为一种更好的方法,并且可以在单个实例中处理这么多客户端。只使用大约130mb的ram,并且随着时间的推移没有内存泄漏

PHP我个人认为不应该永远在循环中运行,它只是感觉不应该如何使用。

这里需要基于事件的编程。使用为您处理事件的库要容易得多。PHP并不是这类编程的最佳工具,但是仍然有一个库。

一个解决方案是使用nodejs/socket。IO服务器,PHP进程可以在发生有趣的事情时向它发送消息。然后nodejs服务器将其传递给客户端。