Socket.ioのチュートリアルを実施

英語の元のページはこちら

前提条件としてnode.jsがインストールされていることが必要です。

node.jsのインストール

cd /tmp
curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
sudo yum -y install nodejs
sudo yum -y install gcc-c++ make

package.jsonの作成

cd
mkdir chat
cd chat
vi package.json

package.jsonに次の内容を入力します:

{
  "name": "socket-chat-example",
  "version": "0.0.1",
  "description": "my first socket.io app",
  "dependencies": {}
}

expressのインストール

次にフレームワークのexpressをインストールします。 package.jsondependenciesの編集を省略するためにnpm install --saveを実行します:

cd ~/chat
npm install --save express@4.15.2

index.jsの作成

次の内容でindex.jsを作成します:

var app = require('express')();
var http = require('http').Server(app);

app.get('/', function(req, res){
  res.send('<h1>Hello world</h1>');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

上記の内容ですが、 * appを、HTTPサーバに渡せる関数ハンドラとして初期化する * ルートハンドラ/を定義する。これはウェブページをアクセスした際に呼び出される * ポート3000でウェブサーバをlistenする

ウェブサーバの起動

node index.jsと実行すると、次の画面が出るはずです:

https://socket.io/assets/img/chat-1.png

ここでブラウザをhttp://localhost:3000に向けると、次のような画面が表示されます:

https://socket.io/assets/img/chat-2.png

HTMLを別ファイルに分ける

index.jsでは、res.sendに対してHTMLを文字列として渡しています。HTMLが長くなると見通しが悪くなりコードが管理しづらいので、index.htmlを作成してそこにHTMLを格納します:

app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});

index.htmlには次の内容を記述します:

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

ここで、node index.jsを再度実行し(ctrl-cで一旦殺してから、再度、実行)ブラウザからhttp://localhost:3000を アクセスすると次のような画面が出るはずです:

https://socket.io/assets/img/chat-3.png

Socket.IOの組み込み

Socket.IOは次の2つから構成されています:

  • Node.JSのHTTPサーバと連動して動くsocket.io
  • ブラウザ側で読み込まれるクライアントライブラリ sockiet.io-client

開発中においては、socket.ioがクライアントに対して自動的にサービスを提供してくれるため、現時点ではsocket.ioのみをインストールします:

npm install --save socket.io

これでsocket.ioがインストールされpackage.jsonが更新されます。次に、index.jsを編集しsocket.ioを読み込みます:

var app  = require('express')();
var http = require('http').Server(app);
var io   = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

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

http.listen(3000, function(){
  console.log('listening on *:3000');
});

上記ですが、httpオブジェクト(HTTPサーバ)を渡してsocket.ioインスタンスを初期化しています。 次にconnectionイベントを待ち受け、コンソールに出力しています。

ブラウザ側への組み込み

index.htmlを開いて、次のコードを</body>の直前に入れます:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

これだけでsocket.io-clientを読み込み、ioグローバルを露出し、接続します。 なお、io()を呼ぶ時URLを指定していませんが、これは、ページを配信したホストにデフォルトで接続するためです。

ここでブラウザを再読込するとコンソールが "a user connected"と出力するはずです。 複数のタブを開くと、次のように複数メッセージが出力されます:

https://socket.io/assets/img/chat-4.png

各ソケットは特殊なdisconnectイベントを発行します:

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

ブラウザでリロードを数回行うと、disconnectを確認できます:

https://socket.io/assets/img/chat-5.png

イベントの発行

Socket.IOのコンセプトとして、お好きなイベントの送信・受信ができ、その際、お好きなデータを送れます。JSONエンコードできるデータであればなんでもOKです。バイナリデータもサポートされています。

さて、ユーザがメッセージを入力したら、サーバがそれを受信してチャットメッセージのイベントを発行するようにしましょう。index.html<script>部分を次のようにします:

<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
  $(function () {
    var socket = io();
    $('form').submit(function(){
      socket.emit('chat message', $('#m').val());
      $('#m').val('');
      return false;
    });
  });
</script>

そしてindex.jsでは、chat messageイベントを出力します:

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    console.log('message: ' + msg);
  });
});

結果として次の動画のようになるはずです(元のサイトで動画をご覧ください)

ブロードキャスト

次に、サーバから他のユーザに対してイベントを発行します。 イベントを全員に送るには、socket.ioio.emitを利用します:

io.emit('some event', { for: 'everyone' });

もし、特定のソケットを除いてメッセージを送りたい場合、broadcastフラグを使います:

io.on('connection', function(socket){
  socket.broadcast.emit('hi');
});

注:原文の意味がよくわからないのですが、これを使うとメッセージを送信した本人以外にイベントが届きます

今回はわかりやすさのため、送信者を含む全員にメッセージを送ります:

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});

クライアント側ではchat messageイベントを受信し、ページ内に表示します:

<script>
  $(function () {
    var socket = io();
    $('form').submit(function(){
      socket.emit('chat message', $('#m').val());
      $('#m').val('');
      return false;
    });
    socket.on('chat message', function(msg){
      $('#messages').append($('<li>').text(msg));
    });
  });
</script>

これで、簡単なチャットが実現できました。

宿題

  • 誰かが接続・切断した場合に接続しているユーザに通知を送りましょう
  • ニックネーム機能を付けましょう
  • 送信者にはメッセージを送らないようにしましょう。代わりに、メッセージ入力後にDOMにすぐに追記します。
  • "○○さんが入力中です"の機能をつけましょう
  • 誰がオンラインであるかを表示する機能をつけましょう
  • 個人間のメッセージを付けましょう
  • 改良した内容をみんなと共有しましょう!

このチュートリアルを入手するには

GitHubこちらにあります。

$ git clone https://github.com/socketio/chat-example.git