WebSocket Communication

WebSocket勉強会

WebSocket Communication

自己紹介

アジェンダ

私のセッションでは難しい話は抜きにしてWebSocketを使ったデモを中心にお送りしたいと思います。

と、事前資料公開時は考えていましたが、デモだけで30分もたないのでWebベースの実装についての解説なども

WebSocketとは?

今日、何度も出てきたと思いますが、リアルタイムに双方向通信を可能にするプロトコルです。

WebSocketが(現時点で)使えるブラウザ

PC
Chrome4 / Safari5
iPhone / iPad
iOS 4.2.1 Safari

WebSocket実装状況

ちなみにFireFox4 beta7 / Opera 10.2.0ではWebSocketを利用出来ましたが、FireFox4 / Opera11 ではWebSocketは無効化されています。

参考
WebSocket実装状況
Firefox 4 では Websocket を無効化します « Mozilla Developer Street (modest)
https://dev.mozilla.jp/hacksmozillaorg/websockets-disabled-in-firefox-4/
こてさきAjax:WebSocketがデフォルトdisableとなった件
http://blog.livedoor.jp/kotesaki/archives/1600864.html

HTTPプロトコルイメージ

Ajax(httpリクエスト)は都度コネクションを貼り、レスポンスを待つ
httpプロトコル

WebSocketプロトコルイメージ

WebSocketは一度コネクションを貼ると、通信をそのWebSocketコネクション上でやり取りする
websocketプロトコル

都度コネクションを貼る必要が無くなるので、結果通信ロスが減る

アンケート

WebSocketについて、どれに興味がありますか?

  1. クライアント実装
  2. サーバーサイド実装
  3. その他

クライアント実装

ブラウザからのWebSocket通信

クライアント実装

  1. 一度httpプロトコル(要は普通にブラウザ)で画面表示
  2. javascriptからwebsocketオブジェクトをURL指定で生成
  3. httpの場合の接続先:『ws://exsample.com』
  4. httpsの場合の接続先:『wss://exsample.com』
  5. クライアント側はこれだけでサーバとの双方向通信が可能になります
  6. (今後の仕様しだいでは認証が必要)

クライアント実装(JavaScript)

<script type="text/javascript">
var protocol = ( location.protocol == "https:" ) ? "wss" : "ws";
var url = protocol + "://" + "exsample.com";
var websocket = new WebSocket( url );

//WebSocketのイベントの登録
websocket.addEventListener( "open", openFunc, false );
websocket.addEventListener( "close", closeFunc, false );
websocket.addEventListener( "message", receiveMessage, false );

//送信イベント
getElementById("button").addEventListener( "click", sendMessage, false );

//message送信
function sendMessage()
{
	websocket.send( "送信する文字列とかJSONとか" );
}

//message受信
function receiveMessage( msg )
{
	alert ( msg.data );
}
</script>

SyncSlideでのクライアント実装

ブラウザ間でスライドコマンドを共有
iPhone/iPadでは jQueryを使ってタッチイベントをコマンドとして処理

クライアント実装(タッチイベント処理)

  //iphoneタッチ処理
  var slider =  document.getElementById( "slide" );
  slider.addEventListener( "touchstart", touchEvent, false );
  slider.addEventListener( "touchmove", touchEvent, false );
  slider.addEventListener( "touchend", touchEvent, false );

  function touchEvent( e ){
    //スワイプ伝播キャンセル
    e.preventDefault();
                   
    //シングルタッチのみ取り出す
    var touch = e.touches[0];
    if ( e.type == "touchstart" ){
      //移動開始場所の設定と移動距離の初期化
    }
    else if ( e.type == "touchmove" ){
      //移動距離を計算
    }
    else if ( e.type == "touchend" ){
      //縦横判定からoperetionを設定
      var operation = "top";
      
      //処理送信
      sendMessage( operation );
    }
  }

jQueryライブラリを使ったマルチタッチイベント

当初は自前で移動距離等で判定していましたが、今回からjQueryのライブラリを使いました。

TouchSwipe
http://plugins.jquery.com/project/touchSwipe

TouchSwipe

//シングルタッチ定義
var singleSwipeOptions = {
  swipe:singleSwipe,	//関数名
  threshold:50,		//移動距離閾値
  fingers:1 			//タッチ本数
}
//ダブルタッチ定義
var doubleSwipeOptions = {
  swipe:doubleSwipe,
  threshold:50,
  fingers:2
}
//iphone用スワイプイベント
$( "#slide" ).swipe( singleSwipeOptions );
$( "#slide" ).swipe( doubleSwipeOptions );

//一本指スワイプ
function singleSwipe( event, direction )
{
  var message = "";
  if( direction === "up" )		message = "first";
  else if( direction === "left" )	message = "prev";
  else if( direction === "right" )	message = "next";
  else if( direction === "down" )	message = "last";
  if( message !== "" ) sendMessage( message );
}
//二本指スワイプ
function doubleSwipe( event, direction )
{
  if( direction === "up" )		hoge();
  else if( direction === "left" )	fuga();
  else if( direction === "right" )	hogohoge();
  else if( direction === "down" )	fugafuga();
}

サーバーサイド実装

WebSocketエコーサーバー

WebSocketが使えるサーバ

Python
Apache + mod_pywebsocket
Java
Jetty
JavaScript
node.js + socket.io

今回使用したWebサーバー

Jetty

Jetty

Jetty
http://www.eclipse.org/jetty/
Javaで実装されたServletコンテナ / HTTPサーバ
ライセンス
Apache License Version 2.0のもとオープンソースソフトウェアとして公開
特徴
軽量で高速に動作する

ちなみにGAE/J(Google App Engine / Java)では Jettyが採用されています。
WebSocketは使えませんが、サーバからPushを行う仕組みとしてChannel APIが提供されています。

サーバーサイド実装

Jetty7を使ったWebSocket通信

言語 / サーバ
Java / Jettty7
実装
  • サイトのディレクトリごとにコネクショングループを作る
  • 受け取ったスライドコマンド(JSON)をグループ全員に送信する
  • クライアントから送られたメッセージをそのまま他のクライアントに渡す簡単なエコーサーバ
  • JSONメッセージの処理にはJSONICというライブラリを利用しています。

WebSocketイベント処理

WebSocketインターフェース

クライアントの操作に応じて"onHogeHoge"が呼ばれる(イベント駆動)

※jetty-websocket-7.4.1.v20110513.jarより

package org.eclipse.jetty.websocket;

import java.io.IOException;

public interface WebSocket
{
    void onOpen(Connection connection);
    void onClose(int closeCode, String message);

    interface OnTextMessage extends WebSocket
    {
        void onMessage(String data);
    }

    interface OnBinaryMessage extends WebSocket
    {
        void onMessage(byte[] data, int offset, int length);
    }
    
    interface OnControl extends WebSocket
    {
        boolean onControl(byte controlCode,byte[] data, int offset, int length);
    }

    interface OnFrame extends WebSocket
    {
        boolean onFrame(byte flags,byte opcode,byte[] data, int offset, int length);
        
        void onHandshake(FrameConnection connection);
    }

    public interface Connection
    {
        String getProtocol();
        void sendMessage(String data) throws IOException;
        void sendMessage(byte[] data, int offset, int length) throws IOException;
        void disconnect();
        boolean isOpen();

        void setMaxTextMessageSize(int size);
        void setMaxBinaryMessageSize(int size);

        int getMaxTextMessageSize();
  
        int getMaxBinaryMessageSize();
    }
    
    public interface FrameConnection extends Connection
    {
        boolean isMessageComplete(byte flags);
        void close(int closeCode,String message);
        byte binaryOpcode();
        byte textOpcode();
        byte continuationOpcode();
        byte finMask();
        
        boolean isControl(byte opcode);
        boolean isText(byte opcode);
        boolean isBinary(byte opcode);
        boolean isContinuation(byte opcode);
        boolean isClose(byte opcode);
        boolean isPing(byte opcode);
        boolean isPong(byte opcode);
        
        void sendControl(byte control,byte[] data, int offset, int length) throws IOException;
        void sendFrame(byte flags,byte opcode,byte[] data, int offset, int length) throws IOException;
    }
}

JSONICを使ったJSON→POJO(Plain Old Java Object)変換サンプル

public void sample(){
  //ブラウザから渡されたJSON文字列
  String jsonMsg = "{hoge:\"fuga\",foo:\"bar\",name:\"kamiyam\",num:5}";

  //Informetionクラスに変換
  Informetion info = JSON.decode( jsonMsg, Informetion.class );

  //逆も簡単にできます (infoオブジェクトからJSON文字列に変換)
  String temp = JSON.encode( info );
  System.out.println( temp );
  //{hoge:\"fuga\",foo:\"bar\",name:\"kamiyam\",num:5}
}

jsonic

まとめ

ご清聴ありがとうございました

〜 お疲れ様でした 〜

Special thanks! mukiSlide is made by Yusuke Nakanishi