4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Hapi.js with Socket.IO - Part1: Hello World!

Posted at

Hapi.jsのREST APIが一通り動くようになったので次はHapi.jsにSocket.IOサーバーを作成してみます。Using hapi.js with Socket.ioにとても良いSocket.IOの解説があります。著者のMatt Harrison氏はManningでHapi.js in ActionをMEAPで執筆中です。ブログが非常にわかりやすいので著書も期待できます。

リソース

Hapi.jsとSocket.IOの使い方は以下のサイトを参考にして勉強します。

プロジェクト

適当なディレクトリにプロジェクトを作成します。リポジトリはこちらです。

$ cd ~/node_apps/docker-hapi-socketio
$ tree -a -L 2
.
├── .dockerignore
├── .env
├── .gitignore
├── Dockerfile
├── README.md
├── app.js
├── docker-compose.yml
├── node_modules -> /dist/node_modules
├── npm-debug.log
├── package.json
└── templates
    └── index.html

以下のバージョンのパッケージを使います。

~/node_apps/docker-hapi-socketio/package.json
{
  "name": "docker-hapi-socketio",
  "description": "docker-hapi-socketio",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
      "hapi": "^8.6.1",
      "socket.io": "^1.3.5",
      "handlebars": "^3.0.3",
      "dotenv": "^1.1.0"
  },
  "scripts": {"start": "node app.js"}
}

サーバーサイド

hapi serverのlisnterはhttp server

hapi server(var server = new Hapi.Server();)にconnectionを作成(server.connection({ port: 4000 });)すると、内部で新しいNode.jsのhttp server(var listener = require('http').createServer(handler);)が作成されます。このhttp serverインスタンスはlistenerプロパティ(var listener = server.listener;)になります。

Socket.IOに渡すappもhttp server

Socket.IOをrequireするときに渡すvar io = require('socket.io')(app);のapp変数もhapi serverのlistenerと同様にhttp server(var app = require('http').createServer(handler);)です。ただし以下のようにhapi serverにconnectionを作成して作成した8080ポートのhapi server.listener(node http server)をSocket.IOに渡すとセットアップ処理でnode http serverのrequestイベントのリスナーがすべて削除されてしまいます。

var Hapi = require('hapi')
var server = new Hapi.Server();
server.connection({ port: 8080 });

var io = require('socket.io')(server.listener);

io.on('connection', function (socket) {
  console.log('connected');
});

server.start();

API用のhttp serverとSocket.IO用のhttp server

hapiはportを指定して内部で複数のnode http serverを起動することができます。下の例では1つのhapi serverでREST APIを8080ポートでLISTENするHTTPサーバーと、8080ポートでLISTENするSocket.IOサーバーの2つが起動しています。HTTPサーバーはいまのところ/からindex.htmlのエントリポイントを提供するだけでREST APIのRouteは実装していません。

~/node_apps/docker-hapi-socketio/app.js
'user strict';

var Hapi = require('hapi'),
    server = new Hapi.Server(),
    Path = require('path');
require('dotenv').load();

server.connection({ port: process.env.API_PORT, labels: ['api'] });
server.connection({ port: process.env.SOCKETIO_PORT, labels: ['twitter'] });

server.views({
    engines: {
        html: require('handlebars')
    },
    path: Path.join(__dirname, 'templates')
});

server.select('api').route({
    method: 'GET',
    path: '/',
    handler: function (request, reply) {
        reply.view('index',
                   { socketio_host: (process.env.PUBLIC_IP+':'
                                     +process.env.SOCKETIO_PORT)});
    }
});

var io = require('socket.io')(server.select('twitter').listener);
io.on('connection', function (socket) {
    console.log('connected!');
    var tweet = {text: 'hello world!'};
    var interval = setInterval(function () {
        socket.emit('tweet', tweet);
    }, 5000);

    socket.on('disconnect', function () {
        clearInterval(interval);
    });
});

server.start();

クライアントサイド

クライアントサイドはSPAで実装します。HTTPサーバー(8000)がserveするindex.htmlがエントリポイントになります。index.htmlからSocker.IOサーバー(8080)がserveするsocket.io.jsのクライアントライブラリをロードしてSocket.IOサーバー(8080)に接続します。socket変数のconnecttweetイベントにlistenerをアタッチします。

~/node_apps/docker-hapi-socketio/templates/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>tweet</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="http://{{socketio_host}}/socket.io/socket.io.js"></script>
    <script>
      $(function() {
        var socket = io.connect("http://{{socketio_host}}");
        socket.on("connect", function() {
          console.log("Connected!");
        });
        socket.on("tweet", function(tweet){
          console.log(tweet);
          $("#tweet").prepend(tweet.text + "<br>");
        });
      });
    </script>
  </head>
  <body>
    <div id="tweet"></div>
  </body>
</html>

このindex.htmlはHandlebars.jsのテンプレートです。{{socketio_host}}.envに定義した環境変数をdotenvを使ってロードした値が入っています。

プログラムの実行

Docker Composeからhapiサービスをupします。ブラウザから.envファイルに定義したIPアドレスとポートに接続するとconnected!のログが出力されます。

$ docker-compose up
Recreating dockerhapisocketio_hapi_1...
Attaching to dockerhapisocketio_hapi_1
hapi_1 |
hapi_1 | > docker-hapi-socketio@0.0.1 start /app
hapi_1 | > node app.js
hapi_1 |
hapi_1 | connected!

サーバーサイドでは5秒間隔でメッセージをemitしています。

~/node_apps/docker-hapi-socketio/app.js
    var tweet = {text: 'hello world!'};
    var interval = setInterval(function () {
        socket.emit('tweet', tweet);
    }, 5000);

ブラウザサイドにも5秒間隔でメッセージがappendされていきます。

hello-socketio.png

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?