44
48

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.

WebSocket and Web Application Framework

Last updated at Posted at 2015-10-24

WebSocket はネットワークの双方向通信の規格で、リアルタイム通信の用途で利用されています。
Web Application FrameworkPhoenixWebSocket を標準でサポートしています。
RailsAction CableWebSocket の利用がサポートされるようになります。

そこで Express を合わせて3つの FrameworkWebSocket のサンプルを作成しました。
( bash コマンドの brewMac OS X で利用できます。他OSは適宜のコマンドを利用してください。 )


Node.js on Express

logo-express

Express は JavaScript が言語の Web Application Framework です。
Socket.IO という WebSocket のライブラリが有名です。

Setup

  • Node.js をインストールすると npm のコマンドが利用できます。
  • npm コマンドで ExpressGenerator をインストールします。
$ brew install node
$ npm install express-generator -g
  • express コマンドで Web Application を作成します。
  • npm コマンドで Socket.IO のパッケージをインストールします。
$ express express-socketio-example
$ cd express-socketio-example && npm install
$ npm install socket.io --save

Making

Socket.IO の実装はこちらが参考になります。

Server

サーバーサイドに WebSocket の接続処理を記述します。

  • Express の実行ファイルの最後に Socket.IO の設定を追加します。
    • io.on は接続の処理です。
    • io.emit でメッセージが送信されます。
    • socket.on でメッセージが受信されます。

bin/www:

var io = require('socket.io')(server);
io.on('connection', function(socket){
  socket.on('message from client', function(message){
    io.emit('message from server', message);
  });
});

Client

フロントエンドにも WebSocket の接続処理を記述します。

  • View のファイルに Socket.IO の設定を追加します。
    • socket = ioSocket を設定します。
    • socket.emit でメッセージが送信されます。
    • socket.on でメッセージが受信されます。
  • DOM の操作は JQuery を利用します。
  • メッセージは <div id="messages"></div> に出力します。

views/index.jade:

extends layout

block content
  form
    input#content
    button Send
  #messages
  script(src='/socket.io/socket.io.js')
  script(src='http://code.jquery.com/jquery-1.11.3.min.js')
  script().
    var socket = io();
    $('form').submit(function(){
      socket.emit('message from client', $('#content').val());
      $('#content').val('');
      return false;
    });
    socket.on('message from server', function(message){
      $('#messages').append($('<p>').text(message));
    });

Start

  • アプリケーションを起動します。
$ DEBUG=express-socketio-example:* npm start

express
サンプルコード

これだけで簡単なチャットアプリケーションができます。


Elixir on Phoenix

logo-phoenix

PhoenixElixir が言語の Web Application Framework です。
WebSocket が標準の機能で利用することができます。
Elixir は Erlang VM で実行されます。外部DSLが簡単に実装できます。

Setup

  • Elixir をインストールすると mix のコマンドが利用できます。
  • mix コマンドで Phoenix をインストールします。
  • mix archive.installここから [URL] を取得します。
$ brew install elixir
$ mix local.hex
$ mix archive.install https://[URL]/phoenix_new-x.x.x.ez
  • デフォルトのデータベースに PostgreSQL が利用されます。
$ brew install postgresql
$ createuser -d -P postgres
$ postgres -D /usr/local/var/postgres
  • mix phoenix.new コマンドで Web Application を作成します。
  • mix phoenix.gen.html コマンドで MessageGenerator を実行します。
$ mix phoenix.new phoenix_channel_example
$ cd phoenix_channel_example
$ mix ecto.create
$ mix phoenix.gen.html Message messages content:text
  • RouterMessage を追加します。(よく忘れます。)

web/router.ex:

resources "/messages", MessageController

Making

Channels の実装はこちらが参考になります。

Server

  • Channels のルーティングを設定します。

web/channels/user_socket.ex:

channel "rooms:*", PhoenixChannelExample.RoomChannel
  • Channels の設定します。
    • join でチャンネルを設定します。
    • handle_out でメッセージが送信されます。
    • handle_in でメッセージが受信されます。

web/channels/room_channel.ex:

defmodule PhoenixChannelExample.RoomChannel do
  use Phoenix.Channel

  def join("rooms:lobby", auth_msg, socket) do
    {:ok, socket}
  end

  def join("rooms:" <> _private_room_id, _auth_msg, socket) do
    {:error, %{reason: "unauthorized"}}
  end

  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast! socket, "new_msg", %{body: body}
    {:noreply, socket}
  end

  def handle_out("new_msg", payload, socket) do
    push socket, "new_msg", payload
    {:noreply, socket}
  end
end

Client

  • Channels の設定します。
    • chan = socket.channel でチャンネルを設定します。
    • chan.push でメッセージが送信されます。
    • chan.on でメッセージが受信されます。

web/static/js/app.js:

import {Socket} from "deps/phoenix/web/static/js/phoenix"

let message_content   = $("#message_content")
let messagesContainer = $("#messages")

let socket = new Socket("/socket")
socket.connect()
let chan = socket.channel("rooms:lobby", {})

message_content.on("keypress", event => {
  if(event.keyCode === 13){
    chan.push("new_msg", {body: message_content.val()})
    message_content.val("")
    return false
  }
})

chan.on("new_msg", payload => {
  messagesContainer.append(`<br/>${payload.body}`)
})

chan.join().receive("ok", chan => {
  console.log("Welcome to Phoenix Chat!")
})
  • DOM の操作は JQuery を利用します。

web/templates/layout/app.html.eex:

<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
  • メッセージは <div id="messages"></div> に出力します。

web/templates/message/new.html.eex:

<div id="messages"></div>

Run

  • アプリケーションを起動します。
$ mix ecto.migrate
$ mix phoenix.server

phoenix
サンプルコード

手順は増えましたが、高度なチャットアプリケーションが簡単にできます。


Ruby on Rails

logo-rails

RailsRuby が言語の Web Application Framework です。
WebSocketActionCable の機能で利用することができます。
Ruby はもちろん、外部DSLが簡単に実装できます。

Setup

  • Ruby をインストールすると gem のコマンドが利用できます。
  • 今回は gem コマンドで Rails をインストールしません。
  • gem コマンドで Bundler をインストールします。
$ brew install ruby
$ gem install bundler
  • 最新バージョンの Rails をインストールします。
  • rails コマンドで Web Application を作成します。
  • --edge オプションを指定すると GemfileGitHub を参照します。
$ git clone https://github.com/rails/rails.git
$ cd rails
$ bundle install
$ cd ..
$ rails/railties/exe/rails new rails-actioncable-example --edge
$ cd rails-actioncable-example
  • ActionCableRedis を利用します。
$ brew install redis
  • Rails で利用するライブラリを Gemfile に記述します。

Gemfile:

gem 'actioncable', github: 'rails/actioncable'
gem 'puma'
gem 'foreman', require: false
  • rails generate コマンドで MessageGenerator を実行します。
$ bundle
$ bundle exec rails generate scaffold Message content:text

Making

ActionCable の実装はこちらが参考になります。

Server

  • 接続のスーパークラスを記述します。(ジェネレーターまだかなぁ)

app/channels/application_cable/connection.rb:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
  end
end
  • チャンネルのスーパークラスを記述します。(ジェネレーターまだかなぁ)

app/channels/application_cable/channel.rb:

module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end
  • チャンネルのSubscribe(購読)を設定します。

app/channels/messages_channel.rb:

class MessagesChannel < ApplicationCable::Channel
  def subscribed
    stream_from "messages"
  end
end
  • POST /messagesserver.broadcast でメッセージが送信されます。

app/controllers/messages_controller.rb:

def create
  ActionCable.server.broadcast 'messages', message: params[:message][:content]
  head :ok
end
  • ActionCableRack の設定を記述します。

config/racks/cable.ru:

require ::File.expand_path('../../environment',  __FILE__)
Rails.application.eager_load!

require "action_cable/process/logging"

run ActionCable.server
  • ActionCable 設定を yml 記述します。

config/redis/cable.yml:

production: &base
  :url: redis://localhost:6379
  :host: localhost
  :port: 6379
  :timeout: 1
  :inline: true
development: *base
test: *base
  • RailsActionCable の実行コマンドを記述します。

Procfile:

web: bundle exec rails server
cable: bundle exec puma -p 28080 config/racks/cable.ru

Client

  • App.cable = Cable.createConsumer でチャンネルを設定ます。

app/assets/javascripts/channels/index.coffee:

#= require cable
#= require_self
#= require_tree .

@App = {}
App.cable = Cable.createConsumer "ws://localhost:28080"
  • received でメッセージが受信されます。
  • DOM の操作は JQuery を利用します。

app/assets/javascripts/messages.coffee:

App.messages = App.cable.subscriptions.create 'MessagesChannel',
  received: (data) ->
    $('#messages').append($('<p>').text(data.message))
  • form_forremote: true でページリロードを抑制します。
  • Submit でメッセージが送信されます。

app/views/messages/_form.html.erb:

<%= form_for(message, remote: true) do |f| %>
  • メッセージは <div id="messages"></div> に出力します。

app/views/messages/new.html.erb:

<div id='messages'></div>

Run

  • アプリケーションを起動します。
$ rake db:migrate
$ bundle exec foreman start

rails
サンプルコード

かなり手順は増えましたが、このチャットアプリケーションはスケールアウトに対応しています。

Tips

ActionCable with ActiveJob

Node.jsElixir は非同期処理のメカニズムがあり、多くのリクエストを処理するのに向いています。
Ruby で非同期処理を実装するには EventMachine のライブラリを利用するのですが、さらに Rails では ActiveJob のライブラリを利用したり、 RedisPub/Sub を利用して非同期処理を実現しています。

  • まず、 sidekiq のライブラリを Gemfile に記述します。

Gemfile:

gem 'sidekiq'
  • 次に、メッセージを送信する処理を Job に記述します。

app/jobs/message_job.rb

class MessageJob < ApplicationJob
  queue_as :default

  def perform(*args)
    ActionCable.server.broadcast 'messages', message: args[0]
  end
end
  • さきほどの POST /messages に非同期の設定を記述します。

app/controllers/messages_controller.rb:

def create
  MessageJob.perform_later(params[:message][:content])
  head :ok
end

Enjoy WebSocket

44
48
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
44
48

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?