Sailsアプリは、クライアントとサーバー間の全二重リアルタイム通信が可能です。これは、クライアント(例:ブラウザータブ、Raspberry Piなど)がSailsバックエンドへの永続的な接続を維持でき、メッセージがクライアントからサーバー(例:AJAX)またはサーバーからクライアント(例:「comet」)へいつでも送信できることを意味します。リアルタイム通信の一般的な用途の2つは、ライブチャットの実装とマルチプレイヤーゲームです。Sailsは、サーバー側でsocket.ioライブラリを使用し、クライアント側でsails.io.jsライブラリを使用して、リアルタイムを実装します。Sailsドキュメント全体を通して、socketとwebsocketという用語は、Sailsアプリとクライアント間の双方向の永続的な通信チャネルを指すために一般的に使用されます。
ソケットを介してSailsアプリと通信することは、AJAXを使用することと似ています。どちらの方法も、Webページをリフレッシュすることなくサーバーとやり取りできるからです。ただし、ソケットはAJAXと2つの重要な点で異なります。1つ目は、ソケットはWebページが開いている限りサーバーに接続したままにすることができ、それにより状態を維持できます(すべてのHTTPリクエストと同様に、AJAXリクエストはステートレスです)。2つ目は、接続が常にオンであるため、Sailsアプリはいつでもソケットにデータを送信できます(したがって「リアルタイム」という名前です)。一方、AJAXはリクエストが行われた場合にのみサーバーが応答することを許可します。
Sailsのブループリントアクションにリクエストを行うソケットは、リソースフルなPub/Sub 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'
});
}
}