从服务器向客户端发送通知以响应服务器事件

16

如何使用PHP从服务器向客户端发送消息,避免不必要的Ajax调用。

以下是实现方法:

  1. 用户Alice做出更改并将其发送到服务器。

  2. 然后,服务器检查哪些用户不是最新的,如果不是,则调用一些代码将与更改相关的信息发送给Bob(在这种情况下不是最新的用户)。

如何向Bob发送消息?


2
如果您对Stack Exchange使用的确切工具和技术感兴趣,那么Meta Stack Exchange上的这个问题将对您有所帮助。至于实际实现具有类似功能的东西,这个问题太过宽泛。 - animuson
2
@MatthewStrawbridge 请阅读这篇文章,我会讲解为什么那样做不起作用。 - user3765372
3
你需要的是 WebSockets(如果你希望支持古老浏览器,可以回退到使用 Ajax 的长轮询) 。如果你不能重新打开你的问题,我建议你研究一下这个,这样你的问题可能会变得更具体、更容易提问。请注意,这些实现细节仍需自行考虑。 - Tim Post
7
我重新打开了这个问题,因为我认为它可以回答,并且是一个绝佳的机会让某人全面概述Websockets以及如何在它们不可用时退回到长轮询(我原本以为我们已经有了,但我找不到了)。不过,如果社区反对我的决定也可以(关于这个问题的元讨论请参见http://meta.stackoverflow.com/questions/261305/open-ended-question-rejected)。 - Tim Post
2
@TimPost 感谢您提供有关Web Sockets的信息!那正是我所需要的! - user3765372
显示剩余7条评论
3个回答

23
你可以使用服务器发送事件 (Server Sent Events)。它们是轻量级并且单向的WebSocket被忽视的表亲。它们基本上允许客户端等待来自服务器的消息(然后您可以通过不同的通道,如AJAX进行响应)。
示例客户端代码:
var source = new EventSource('/newFile');

source.addEventListener('message', function(e) {
  // Use AJAX and pull new file here
}, false);

不幸的是,似乎(当然)没有IE支持。可以使用诸如EventSource HQ之类的库来支持跨浏览器服务器发送事件。它将抽象出需要处理魔鬼浏览器的细节。


http://www.w3.org/TR/eventsource/的Polyfills - https://github.com/Yaffle/EventSource和https://github.com/remy/polyfills/blob/master/EventSource.js。当然,还有一些其他的,但以上这些可以在IE中很好地工作。 - Mahesh

15
你所寻找的是现在相当常见的东西,有时被称为“实时网络”。这是指能够让服务器端代码将内容实时地推送给连接的客户端的能力。这是通过一些新机制(受最新浏览器和服务器支持)以及一些其他并非为此而设计的机制实现的,但可以用作“hack”来使其正常工作。
首先,你必须明白的是,你所要求的(服务器向客户端“推送”消息)并不是网络(或至少是HTTP)本来的工作方式。经典网络是无状态、请求-响应、半双工的:客户端发起通信,打开与服务器的连接,服务器提供服务,然后关闭连接。过去没有办法让服务器向Web客户端发送消息:客户端(即你的浏览器)应该支持反向模型(提供一个监听的端点),从而实际上成为服务器。
这就是像WebSockets这样的新标准所提供的(另一个值得一提的标准是Server-sent events (SSE);然而,它的支持甚至比WebSockets还要少,它们似乎更适合“流式”内容而不是发送单个消息)。
与HTTP不同,WebSocket提供了全双工通信(任何一方都可以发起),这正是您所寻找的。Firefox 6、Safari 6、Google Chrome 14、Opera 12.10和Internet Explorer 10实现了WebSocket协议的正确版本(某些浏览器存在旧版错误实现)。
那么,如果您的浏览器不支持WebSockets怎么办?您必须使用我之前提到的那些“技巧”。这些“技巧”属于Push technologies的范畴。
特别是,一种常见的技术是长轮询。正如其名称所示,它不是“推送”;长轮询是一种“拉取”技术,但它允许模拟推送机制。使用长轮询时,客户端从服务器请求信息,就像普通的 AJAX 调用一样,只是它以更慢的频率发出 HTTP/S 请求(轮询)。连接后,如果已经有可用的更新,服务器(您的服务器、您的 API:一个 servlet、一个 HTTP 处理程序、一个 REST 控制器等等!通常与提供标准 AJAX 调用支持的机制相同)会发送一些信息;如果没有新内容,则服务器不会发送空响应,而是保持请求打开并等待响应信息变为可用。您有一个“传统”的客户端发起请求,但是该请求被“暂停”,直到服务器要向您通信为止。一旦有了某些内容,服务器通过 HTTP 通道(仍然打开!)向客户端发送响应,完成打开的 HTTP 请求。

其他技术(例如使用插件提供的套接字——Silverlight、Java小程序、Flash等)也可以使用,但是为了利用它们,您需要正确的客户端(浏览器/插件组合)。

当然,你也可以自己实现所有这些东西。我鼓励你尝试作为学习练习。

对于你的生产代码,最好使用一个封装了所有这些概念和技术的库,例如 SignalR(用于ASP.NET)、Ratchet(用于PHP)、Signal.IO(用于node.js)、Faye(用于Ruby)……其中一些库将实现多种技术,根据客户端选择最佳技术,自动回退到其他技术。它们真的能够节省你很多麻烦!


8
如评论中所述,你需要的技术是WebSockets。它们可以让你在服务器和Web浏览器客户端之间建立一个套接字(Unix术语,基本上是双向管道)。
虽然可以使用原始websocket api进行工作,但我更喜欢使用像Socket.IO这样的库来为我抽象出不好的细节。
安装包后,Socket.IO会提供许多不同的简单方法,例如事件和广播,可以轻松地集成到您的应用程序中。您也可以将其仅用作跨浏览器websocket(对于不支持WebSockets的浏览器,它实现了回退长轮询)。
对于您的情况,您希望在服务器上的文件更改时向客户端发送消息。
在服务器端(Node.js):
var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.on('message', function () { });
  socket.on('disconnect', function () { });

  // more stuff here
  if(somethingChanged) {
    socket.send(JSON.stringify({changed: true, file: 'file1.txt', newContent: 'Im fresh off the press yo!'});
  }
});

对于客户端:

<script>
  var socket = io('http://localhost/');
  socket.on('connect', function () {
    socket.send('anything-new');

    socket.on('message', function (msg) {
      if(JSON.parse(msg).changed) {
        // Do stuff here
      }
    });
  });
</script>

虽然这只是一个非常简单的例子,但希望足以让你入门。


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