Sails 应用程序能够在客户端和服务器之间进行全双工实时通信。这意味着客户端(例如浏览器选项卡、Raspberry Pi 等)可以与 Sails 后端保持持久连接,并且可以随时从客户端发送消息到服务器(例如 AJAX)或从服务器发送消息到客户端(例如“彗星”)。实时通信的两种常见用途是实时聊天实现和多人游戏。Sails 使用 socket.io 库在服务器上实现实时通信,并使用 sails.io.js 库在客户端上实现。在整个 Sails 文档中,术语套接字和WebSocket通常用于指代 Sails 应用程序和客户端之间双向的持久通信通道。
通过套接字与 Sails 应用程序通信类似于使用 AJAX,因为这两种方法都允许网页在不刷新页面的情况下与服务器进行交互。但是,套接字与 AJAX 在两个重要方面有所不同:首先,套接字可以与服务器保持连接,只要网页处于打开状态,就可以保持状态(AJAX 请求,像所有 HTTP 请求一样,是无状态的)。其次,由于连接始终处于打开状态,因此 Sails 应用程序可以随时将数据发送到套接字(因此被称为“实时”),而 AJAX 仅允许服务器在收到请求时做出响应。
向 Sails 的 蓝图操作 发出请求的套接字会自动订阅有关通过 资源丰富的发布-订阅 API 获取的模型的实时消息。您也可以在自定义控制器操作中使用此 API 向对某些模型感兴趣的客户端发送消息。
将客户端套接字连接到服务器,订阅user
事件,并请求/user
以订阅当前和未来的 User 模型实例。
<!-- Simply include the sails.io.js script, and a client socket will be created for you -->
<script type="text/javascript" src="/js/dependencies/sails.io.js"></script>
<script type="text/javascript">
// The automatically-created socket is exposed as io.socket.
// Use .on() to subscribe to the 'user' event on the client.
// This event is sent by the Sails "create", "update",
// "delete", "add" and "remove" blueprints to any socket that
// is subscribed to one or more User model instances.
io.socket.on('user', function gotHelloMessage (data) {
console.log('User alert!', data);
});
// Using .get('/user') will retrieve a list of current User models,
// subscribe this socket to those models, AND subscribe this socket
// to notifications about new User models when they are created.
io.socket.get('/user', function gotResponse(body, response) {
console.log('Current users: ', body);
})
</script>
sails.sockets
进行自定义实时通信Sails 在客户端和服务器上都公开了一个丰富的 API,用于发送自定义实时消息。
以下是客户端代码,用于将套接字连接到 Sails/Node.js 服务器并侦听名为“hello”的套接字事件
<!-- Simply include the sails.io.js script, and a client socket will be created and auto-connected for you -->
<script type="text/javascript" src="/js/dependencies/sails.io.js"></script>
<script type="text/javascript">
// The auto-connecting socket is exposed as `io.socket`.
// Use `io.socket.on()` to listen for the 'hello' event:
io.socket.on('hello', function (data) {
console.log('Socket `' + data.id + '` joined the party!');
});
</script>
然后,同样在客户端,我们可以发送套接字请求。在这种情况下,我们将浏览器连接起来,以便在单击特定按钮时发送套接字请求
$('button#say-hello').click(function (){
// And use `io.socket.get()` to send a request to the server:
io.socket.get('/say/hello', function gotResponse(data, jwRes) {
console.log('Server responded with status code ' + jwRes.statusCode + ' and data: ', data);
});
});
同时,在服务器上...
要响应对GET /say/hello
的请求,我们使用一个操作。在我们的操作中,我们将请求套接字订阅到“funSockets”房间,然后向该房间中的所有套接字(不包括新的套接字)广播“hello”消息。
// In /api/controllers/SayController.js
module.exports = {
hello: function(req, res) {
// Make sure this is a socket request (not traditional HTTP)
if (!req.isSocket) {
return res.badRequest();
}
// Have the socket which made the request join the "funSockets" room.
sails.sockets.join(req, 'funSockets');
// Broadcast a notification to all the sockets who have joined
// the "funSockets" room, excluding our newly added socket:
sails.sockets.broadcast('funSockets', 'hello', { howdy: 'hi there!'}, req);
// ^^^
// At this point, we've blasted out a socket message to all sockets who have
// joined the "funSockets" room. But that doesn't necessarily mean they
// are _listening_. In other words, to actually handle the socket message,
// connected sockets need to be listening for this particular event (in this
// case, we broadcasted our message with an event name of "hello"). The
// client-side code you'd need to write looks like this:
//
// io.socket.on('hello', function (broadcastedData){
// console.log(data.howdy);
// // => 'hi there!'
// }
//
// Now that we've broadcasted our socket message, we still have to continue on
// with any other logic we need to take care of in our action, and then send a
// response. In this case, we're just about wrapped up, so we'll continue on
// Respond to the request with a 200 OK.
// The data returned here is what we received back on the client as `data` in:
// `io.socket.get('/say/hello', function gotResponse(data, jwRes) { /* ... */ });`
return res.json({
anyData: 'we want to send back'
});
}
}