Sails 應用程式能夠在客戶端和伺服器之間進行全雙工、即時的通訊。這表示客戶端 (例如:瀏覽器分頁、Raspberry Pi 等) 可以與 Sails 後端保持持久連線,並且可以隨時從客戶端發送訊息到伺服器 (例如:AJAX),或從伺服器發送到客戶端 (例如:"comet")。即時通訊的兩個常見用途是即時聊天功能和多人遊戲。Sails 在伺服器端使用 socket.io 函式庫實作即時功能,在客戶端使用 sails.io.js 函式庫。在整個 Sails 文件中,術語 socket 和 websocket 通常用於指 Sails 應用程式和客戶端之間雙向、持久的通訊管道。
透過 sockets 與 Sails 應用程式通訊,類似於使用 AJAX,因為這兩種方法都允許網頁與伺服器互動而無需重新整理。然而,sockets 與 AJAX 在兩個重要方面有所不同:首先,socket 可以保持與伺服器的連線,只要網頁開啟,使其能夠維護狀態 (AJAX 請求,如同所有 HTTP 請求,都是無狀態的)。其次,由於連線的持續性,Sails 應用程式可以隨時將資料發送到 socket (因此稱為 "即時"),而 AJAX 只允許伺服器在收到請求時才回應。
向 Sails 的 藍圖動作 發送請求的 Sockets 會自動訂閱關於它們透過 資源型發布-訂閱 API 檢索的模型之即時訊息。您也可以在自訂控制器動作中使用此 API,向對特定模型感興趣的客戶端發送訊息。
將客戶端 socket 連接到伺服器,訂閱 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,用於發送自訂即時訊息。
以下是在客戶端連接 socket 到 Sails/Node.js 伺服器並監聽名為 "hello" 的 socket 事件的程式碼。
<!-- 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>
然後,同樣在客戶端,我們可以發送socket 請求。在本例中,我們將設定瀏覽器在點擊特定按鈕時發送 socket 請求。
$('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
的請求,我們使用一個動作。在我們的動作中,我們將訂閱請求的 socket 到 "funSockets" 房間,然後向該房間中的所有 sockets (排除新的 socket) 廣播 "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'
});
}
}