Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

CoffeeScriptベストプラクティス集
ブラウザ向けJavaScript編(4)

CoffeeScriptによるモダンなWebアプリケーション開発 第10回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2012/05/25 14:00

 最近話題の新言語『CoffeeScript』をとりあげた連載「CoffeeScriptによるモダンなWebアプリケーション開発」。今回からはベストプラクティス編として、CoffeeScriptでブラウザ向けJavaScriptを開発する際によく使われる実用的な開発手法を4回に分けて紹介します。CoffeeScriptの歴史や概要については過去の連載も参照ください。

目次

WebSocketをクロスブラウザで使う

 Socket.IO(MITライセンス)は、WebSocket(注1)と同様の機能をクロスブラウザで提供する便利なライブラリです。サーバから任意のタイミングでブラウザにデータをプッシュしたい場合、WebSocketを使えば簡単に実現できますが、WebSocketをサポートしていないブラウザに対して代替手段を提供するのは非常に面倒な作業となります。Socket.IOは、そのようなブラウザごとの違いを吸収して共通のAPIを提供し、ほぼすべてのブラウザ上でWebSocketと同様の機能を提供してくれます。Socket.IOはサーバ側をNode.jsのWebサーバに組み込んで使います。

注1:WebSocket

 Webブラウザとサーバ間で1つのTCPソケットを使って双方向通信を行うための技術。通常80番ポートを利用するためHTTPとの親和性が高く、既存のWebサーバに組み込む形で利用できる。フォームの送信などとは異なり、ソケットをつなぎっぱなしにしてページ内でデータのやり取りを何回も行うことを意図して設計されている。

 従来はFlashやCometなどを使って実装しなければならなかったことが簡単なAPIで実現でき、またAPIやプロトコルの仕様も標準化されているため、誰でも広く利用できる(少し古いブラウザではサポートされていない)。

Socket.IOのインストール(サーバ側)

 サーバ側のプロジェクトのディレクトリにモジュールをインストールします。

$ npm install socket.io

Socket.IOの使い方(サーバ側)

 Node.jsのWebサーバにSocket.IOを組み込みます。連載第4回で紹介したExpress 3.0と組み合わせる場合はリスト1のように記述します。

[リスト1]Webサーバ兼Socket.IOサーバ
express = require 'express'

app = express()
app.configure ->
  # 静的ファイルをpublicディレクトリで提供
  app.use express.static "#{__dirname}/public"

# ここでExpressのルーティングなどを設定する
# app.get '/path', (req, res) ->
#   ...

# 3000番ポートで待ち受ける
server = app.listen 3000
io = require('socket.io').listen server

io.sockets.on 'connection', (socket) ->
  # クライアントが接続した時に実行される
  console.log "connected: #{socket.id}"

  socket.on 'data', (data) ->
    # クライアントからdataイベントが来た時に実行される
    console.log "data: #{data}"

    # 文字列を逆さまにして送り返す
    socket.emit 'data', data.split('').reverse().join('')

Socket.IOの使い方(クライアント側)

 リスト1を実行すると、サーバ上の/socket.io/socket.io.jsというパスでSocket.IOのJavaScriptライブラリが提供されるようになります。クライアント側のHTMLではこのJavaScriptを次のように<script>タグで読み込みます。HTMLはひとまず静的ファイルとして作り、前述のサーバ側のプロジェクト内にpublicディレクトリを作成してその中に配置します。

<script src="/socket.io/socket.io.js"></script>

 クライアント側のコードはリスト2のように記述します。

[リスト2]クライアントからサーバへ通信する
sock = io.connect 'http://localhost:3000'

sock.on 'data', (data) ->
  # サーバからdataイベントが来た時に実行される
  console.log data

# サーバにdataイベントを送信
sock.emit 'data', 'hello!'

 これをブラウザで実行すると「hello!」という文字列がサーバに送信され、サーバは受け取った文字列を逆さまにしてクライアントに送り返し、ブラウザは受け取った文字列「!olleh」をコンソールに出力します。上記の例ではイベント名としてdataを使っていますが、connectmessagedisconnect以外の任意の名前を使うことができます。

APIサーバの実装例

 リスト3、4は、Socket.IOを利用して簡単なAPIサーバを実装したものです。

[リスト3]Socket.IOを利用したAPIサーバ
express = require 'express'

# 接続中のソケット一覧
sockets = {}

app = express()
app.configure ->
  app.use express.static "#{__dirname}/public"

#app.get '/path', (req, res) ->
#    ...

server = app.listen 3000
io = require('socket.io').listen server

io.sockets.on 'connection', (socket) ->
  sockets[socket.id] = socket
  console.log "connected: #{socket.id}"

  socket.on 'request', (message) ->
    # リクエストを元にデータを送り返す
    switch message
      when 'weather'
        socket.emit 'weather', '快晴'
      when 'temperature'
        socket.emit 'temperature', 14
      else
        socket.emit 'request-error', "Unknown message: #{message}"

  socket.on 'disconnect', ->
    # クライアントとの接続が切れたらsocketsから削除する
    console.log "disconnected: #{socket.id}"
    delete sockets[socket.id]

# 接続中の全クライアントに1秒おきにサーバの時刻を送信
setInterval ->
  for id, socket of sockets
    socket.emit 'time', new Date() + ''
, 1000
[リスト4]Socket.IOを利用したAPIクライアント
sock = io.connect 'http://localhost:3000'

sock.on 'weather', (weather) ->  # weatherイベント
  console.log "天気: #{weather}"

sock.on 'temperature', (temp) ->  # temperatureイベント
  console.log "気温: #{temp}"

sock.on 'time', (time) ->  # timeイベント
  console.log "時刻: #{time}"

sock.on 'disconnect', ->
  # サーバとの接続が切れた(自動的に再接続される)
  console.log 'disconnected from server'

# リクエストを送信
sock.emit 'request', 'weather'

# 1秒後にリクエストを送信
setTimeout ->
  sock.emit 'request', 'temperature'
, 1000

 この例のように、WebSocketを使うと自由に双方向の通信を行うことができます。

再接続パラメータの設定

 Socket.IOのクライアントは、サーバとの接続が切れた時に自動的に再接続する機能を持っており、デフォルトで有効になっています。自動再接続を無効にしたい場合は、クライアント側で次のようにreconnectオプションを指定します。

sock = io.connect 'http://127.0.0.1:3000',
  reconnect: false  # 再接続しない

 また、再接続に関するパラメータを変更する場合はクライアント側で次のように指定します。

sock = io.connect 'http://127.0.0.1:3000',
  # 接続に失敗した後、再接続を試みるまでの待ち時間(ミリ秒)。
  # 失敗し続けるとだんだん長くなる。デフォルトは500。
  'reconnection delay': 100

  # 再接続に失敗し続けると待ち時間が長くなっていくが、その最大値。
  # デフォルトはInfinity、つまり制限なし。
  'reconnection limit': 100

  # 接続が切れてから何回再接続を試みるか。
  # この回数連続で失敗すると再接続が行われなくなる。デフォルトは10。
  'max reconnection attempts': 100

 その他、指定できるオプションの一覧はこちらのWebページ(英語)に掲載されています。

本番環境での推奨設定

 本番環境で動かす際には、サーバ側で以下のオプションを設定することが推奨されています。

io.configure ->
  io.enable 'browser client minification'  # JSを圧縮する
  io.enable 'browser client etag'  # etagによるキャッシュを有効にする
  io.enable 'browser client gzip'  # gzipして転送する
  io.set 'log level', 1   # ログ出力を減らす
  io.set 'transports', [  # すべての通信手段を有効にする
    'websocket'
    'flashsocket'
    'htmlfile'
    'xhr-polling'
    'jsonp-polling'
  ]

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 飯塚 直(イイヅカ ナオ)

    1984年東京都生まれ。 高校時代に趣味でPerlやJavaを使ってプログラミングを始める。 慶応大学湘南藤沢キャンパス卒業後、共同通信社にてニュースサイトの開発などを担当。 その後、面白法人カヤックにてソーシャルゲームの開発などを手がける。 2012年現在、カヤックを退社し...

バックナンバー

連載:CoffeeScriptによるモダンなWebアプリケーション開発

もっと読む

おすすめ記事

All contents copyright © 2006-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5