<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://organizeseries.com/"
	>

<channel>
	<title>WebSocket &#8211; HTML5Experts.jp</title>
	<atom:link href="/tag/websocket/feed/" rel="self" type="application/rss+xml" />
	<link>https://html5experts.jp</link>
	<description>日本に、もっとエキスパートを。</description>
	<lastBuildDate>Sat, 07 Jul 2018 03:14:05 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.7.19</generator>
	<item>
		<title>シグナリングサーバーを動かそう ーWebRTC入門2016</title>
		<link>/mganeko/20013/</link>
		<pubDate>Thu, 14 Jul 2016 00:45:36 +0000</pubDate>
		<dc:creator><![CDATA[がねこまさし]]></dc:creator>
				<category><![CDATA[最新動向]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[WebRTC]]></category>
		<category><![CDATA[WebSocket]]></category>

		<guid isPermaLink="false">/?p=20013</guid>
		<description><![CDATA[連載： WebRTC入門2016 (3)こんにちは！ 2014年に連載した「WebRTCを使ってみよう！」シリーズのアップデート記事も3回目となりました。今回は、前回の「手動」で行ったP2P通信の準備を、自動で行えるよう...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/webrtc2016/" class="series-380" title="WebRTC入門2016" data-wpel-link="internal">WebRTC入門2016</a> (3)</div><p>こんにちは！ 2014年に連載した<a href="https://html5experts.jp/series/webrtc-beginner/" target="_blank" data-wpel-link="internal">「WebRTCを使ってみよう！」シリーズ</a>のアップデート記事も3回目となりました。今回は、前回の「手動」で行ったP2P通信の準備を、自動で行えるようにしてみましょう。</p>

<h2>シグナリングサーバーを立てよう</h2>

<p><a href="https://html5experts.jp/mganeko/19814/" target="_blank" data-wpel-link="internal">前回は手動でコピー＆ペーストを行い</a>、WebRTCのP2P通信を始めるために次の情報を交換しました。</p>

<ul>
<li>SDP</li>
<li>ICE candidate</li>
</ul>

<p>今回はこれを仲介するサーバー（シグナリングサーバー）を動かしてみましょう。方法として次の2つをご用意しました。</p>

<ul>
<li>Node.jsを使ったシグナリングサーバー</li>
<li>Chromeアプリ</li>
</ul>

<h3>Node.jsを準備しよう</h3>

<p>まず、WebSocketを使ってシグナリングを行う方法をご紹介します。WebSocketの扱いやすさから、ここではNode.jsを使います。（もちろん他の言語を使っても同様にシグナリングサーバーを作ることができます）<a href="https://nodejs.org/en/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">こちらの公式サイト</a>から、プラットフォームに対応したNode.jsを入手してインストールしてください。今回私は 4.4.7 LTSを使いました。</p>

<p>Node.jsのインストールが完了したら、次はWebSocketサーバー用のモジュールをインストールします。コマンドプロンプト/ターミナルから、 次のコマンドを実行してください。 ※必要に応じて、sudoなどをご利用ください。
</p><pre class="crayon-plain-tag">npm install ws</pre><p> 
※<a href="https://html5experts.jp/mganeko/5349/" target="_blank" data-wpel-link="internal">以前の連載</a>ではsocket.ioを使いましたが、今回はよりプリミティブなwsを使っています。</p>

<h3>シグナリングサーバーを動かそう</h3>

<p>次のコードを好きなファイル名で保存してください。（例えば signaling.js)
</p><pre class="crayon-plain-tag">"use strict";

let WebSocketServer = require('ws').Server;
let port = 3001;
let wsServer = new WebSocketServer({ port: port });
console.log('websocket server start. port=' + port);

wsServer.on('connection', function(ws) {
  console.log('-- websocket connected --');
  ws.on('message', function(message) {
    wsServer.clients.forEach(function each(client) {
      if (isSame(ws, client)) {
        console.log('- skip sender -');
      }
      else {
        client.send(message);
      }
    });
  });
});

function isSame(ws1, ws2) {
  // -- compare object --
  return (ws1 === ws2);     
}</pre><p> 
ポート番号は必要に応じて変更してください。起動するにはコマンドプロンプト/ターミナルから、 次のコマンドを実行します。</p><pre class="crayon-plain-tag">node signaling.js</pre><p>
シグナリングサーバーの動作はシンプルで、クライアントからメッセージを受け取ったら他のクライアントに送信するだけです。</p>

<h3>Chromeアプリを使う場合は</h3>

<p>場合によってはNode.jsをインストールして動かすのは、ハードルが高くて難しいケースもあるかもしれません。そんな人のために、Chromeアプリで「<a href="https://chrome.google.com/webstore/detail/simple-message-server/bihajhgkmpfnmbmdnobjcdhagncbkmmp" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">simple message server</a>」というものを作ってみました。
<a href="https://html5experts.jp/wp-content/uploads/2016/07/simple_message_server_store.png" data-wpel-link="internal"><img src="/wp-content/uploads/2016/07/simple_message_server_store-300x183.png" alt="simple_message_server_store" width="300" height="183" class="alignnone size-medium wp-image-20028" srcset="/wp-content/uploads/2016/07/simple_message_server_store-300x183.png 300w, /wp-content/uploads/2016/07/simple_message_server_store.png 640w, /wp-content/uploads/2016/07/simple_message_server_store-207x126.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></a><br />
Chromeを利用したアプリとしてインストールし、アプリタブから起動して利用します。デスクトップ用のChromeが動く環境（Windows, MaxOS X, Linux, ChromeOS）で動くはずです。</p>

<p>起動すると、 ws://localhost:3001/ でクライアントからの接続を待ち受けます。※実装があまいので時々不安定になります。その場合は[restart]ボタンを押してリセットし、ブラウザもリロードして接続しなおしてください。</p>

<h2>シグナリング処理を変更しよう</h2>

<p>それでは前回の手動シグナリングのコードを、少しずつ変更していきましょう。まずWebSocketで用意したシグナリングサーバーに接続します。JavaScriptに次の処理を追加してください。（URLは使っているポートに合わせて修正してください）
</p><pre class="crayon-plain-tag">let wsUrl = 'ws://localhost:3001/';
  let ws = new WebSocket(wsUrl);
  ws.onopen = function(evt) {
    console.log('ws open()');
  };
  ws.onerror = function(err) {
    console.error('ws onerror() ERR:', err);
  };</pre><p></p>

<p>次に、WebSocketでメッセージを受け取った場合の処理を追加します。
</p><pre class="crayon-plain-tag">ws.onmessage = function(evt) {
    console.log('ws onmessage() data:', evt.data);
    let message = JSON.parse(evt.data);
    if (message.type === 'offer') {
      // -- got offer ---
      console.log('Received offer ...');
      textToReceiveSdp.value = message.sdp;
      let offer = new RTCSessionDescription(message);
      setOffer(offer);
    }
    else if (message.type === 'answer') {
      // --- got answer ---
      console.log('Received answer ...');
      textToReceiveSdp.value = message.sdp;
      let answer = new RTCSessionDescription(message);
      setAnswer(answer);
    }
  };</pre><p> 
JSONテキストからオブジェクトを復元し、typeに応じて前回用意したsetOffer()/setAnswer()を呼び出し、RTCPeerConnectionに渡しています。</p>

<h3>SDPの送信</h3>

<p>Offer/AnswerのSDPの送信も、WebSocket経由で行います。前回要したsendSdp()を次のように変更します。
</p><pre class="crayon-plain-tag">function sendSdp(sessionDescription) {
    console.log('---sending sdp ---');

    textForSendSdp.value = sessionDescription.sdp;
    /*--- テキストエリアをハイライトするのを止める
    textForSendSdp.focus();
    textForSendSdp.select();
    ----*/

    // --- シグナリングサーバーに送る ---
    let message = JSON.stringify(sessionDescription);
    console.log('sending SDP=' + message);
    ws.send(message);
  }</pre><p> 
SDPをJSONテキストに変換してWebSocketでシグナリングサーバーに送信しています。</p>

<h2>実際に動かしてみよう</h2>

<p>シグナリングサーバーを起動して、ChromeかFirefoxのウィンドウを2つ開いて修正したHTMLを読み込んでください。ChromeとFirefoxの間で通信することもできます。</p>

<p>Webサーバーを立てるのが難しい場合は、GitHub Pages にもサンプルを公開しているので、そちらで試すこともできます。その場合でもシグナリングサーバーは自分で用意する必要があるのでご注意ください。</p>

<ul>
<li>GitHub Pages で試す <a href="http://mganeko.github.io/webrtcexpjp/basic2016/ws_signaling_1to1_vanilla.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ws_signaling_1to1_vanilla.html</a>

<ul>
<li>※http://～/ なのでFirefoxのみ</li>
</ul></li>
<li>GitHub でソースを見る <a href="https://github.com/mganeko/webrtcexpjp/blob/master/basic2016/ws_signaling_1to1_vanilla.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ws_signaling_1to1_vanilla.html</a></li>
</ul>

<h4>(1) カメラの取得</h4>

<p>両方のウィンドウで[Start Video]ボタンをクリックします。カメラのアクセスを許可すると、それぞれリアルタイムの映像が表示されます。<br />
<a href="https://html5experts.jp/wp-content/uploads/2016/07/ws_signaling_startvideo.png" data-wpel-link="internal"><img src="/wp-content/uploads/2016/07/ws_signaling_startvideo-300x161.png" alt="ws_signaling_startvideo" width="300" height="161" class="alignnone size-medium wp-image-20032" srcset="/wp-content/uploads/2016/07/ws_signaling_startvideo-300x161.png 300w, /wp-content/uploads/2016/07/ws_signaling_startvideo.png 640w, /wp-content/uploads/2016/07/ws_signaling_startvideo-207x111.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></a><br /></p>

<h4>(2) 通信開始</h4>

<p>どちらかのウィンドウで[Connect]ボタンを押します。(3)SDP（ICE candidateを含む）が自動で交換され、(4)ビデオ通信が始まります。<br />
<a href="https://html5experts.jp/wp-content/uploads/2016/07/ws_signaling_connect.png" data-wpel-link="internal"><img src="/wp-content/uploads/2016/07/ws_signaling_connect-300x162.png" alt="ws_signaling_connect" width="300" height="162" class="alignnone size-medium wp-image-20033" srcset="/wp-content/uploads/2016/07/ws_signaling_connect-300x162.png 300w, /wp-content/uploads/2016/07/ws_signaling_connect.png 640w, /wp-content/uploads/2016/07/ws_signaling_connect-207x112.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p>手動シグナリングに比べて操作がずっと簡単になりました。これなら実際に利用できそうですね。</p>

<h2>Trickle ICE を使ってみよう</h2>

<p>コピー＆ペーストを手動で行う必要がなくなったので、ICE candidateを発生するたびに交換するTrickle ICE を使ってみましょう。流れはこのような形になります。<br />
<a href="https://html5experts.jp/wp-content/uploads/2016/06/hand2016_trickle.png" data-wpel-link="internal"><img src="/wp-content/uploads/2016/06/hand2016_trickle-300x224.png" alt="hand2016_trickle" width="300" height="224" class="alignnone size-medium wp-image-19863" srcset="/wp-content/uploads/2016/06/hand2016_trickle-300x224.png 300w, /wp-content/uploads/2016/06/hand2016_trickle.png 640w, /wp-content/uploads/2016/06/hand2016_trickle-207x155.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></a><br />
すべてのICE candidateが出そろう前にP2P通信が確立する（ことがある）メリットがあります。（※<a href="https://html5experts.jp/mganeko/5349/" target="_blank" data-wpel-link="internal">2014年の記事</a>では「すべてのICE candidateの交換が終わるとP2P通信が始まる」と書いていましたが、これは誤りです）</p>

<h3>SDPをすぐに送信する</h3>

<p>Offer SDP/Answer SDPを生成したら、すぐに相手に送るように変更します。
</p><pre class="crayon-plain-tag">function makeOffer() {
    peerConnection = prepareNewConnection();
    peerConnection.createOffer()
    .then(function (sessionDescription) {
      console.log('createOffer() succsess in promise');
      return peerConnection.setLocalDescription(sessionDescription);
    }).then(function() {
      console.log('setLocalDescription() succsess in promise');

      // -- Trickle ICE の場合は、初期SDPを相手に送る -- 
      sendSdp(peerConnection.localDescription);　// &lt;--- ここを加える

      // -- Vanilla ICE の場合には、まだSDPは送らない --
    }).catch(function(err) {
      console.error(err);
    });
  }

  function makeAnswer() {
    console.log('sending Answer. Creating remote session description...' );
    if (! peerConnection) {
      console.error('peerConnection NOT exist!');
      return;
    }
    
    peerConnection.createAnswer()
    .then(function (sessionDescription) {
      console.log('createAnswer() succsess in promise');
      return peerConnection.setLocalDescription(sessionDescription);
    }).then(function() {
      console.log('setLocalDescription() succsess in promise');

      // -- Trickle ICE の場合は、初期SDPを相手に送る -- 
      sendSdp(peerConnection.localDescription);　// &lt;--- ここを加える

      // -- Vanilla ICE の場合には、まだSDPは送らない --
    }).catch(function(err) {
      console.error(err);
    });
  }</pre><p></p>

<h3>ICE candidateも、すぐに交換する</h3>

<p>ICE candidateを収集した際も、すぐに送るように変更します。</p>

<p></p><pre class="crayon-plain-tag">function prepareNewConnection() {
    // ... 省略 ...

    // --- on get local ICE candidate
    peer.onicecandidate = function (evt) {
      if (evt.candidate) {
        console.log(evt.candidate);

        // Trickle ICE の場合は、ICE candidateを相手に送る
        sendIceCandidate(evt.candidate); // &lt;--- ここを追加する

        // Vanilla ICE の場合には、何もしない
      } else {
        console.log('empty ice event');

        // Trickle ICE の場合は、何もしない
        
        // Vanilla ICE の場合には、ICE candidateを含んだSDPを相手に送る
        //sendSdp(peer.localDescription); // &lt;-- ここをコメントアウトする
      }
    };

    // ... 省略 ....
  }

  function sendIceCandidate(candidate) {
    console.log('---sending ICE candidate ---');
    let obj = { type: 'candidate', ice: candidate };
    let message = JSON.stringify(obj);
    console.log('sending candidate=' + message);
    ws.send(message);
  }</pre><p></p>

<p>合わせてICE candidateをWebSocket経由で受け取った場合の処理も追加しましょう。相手からICE candidateを受け取ったら、その度にRTCPeerConnection.addIceCandidate()で覚えさせます。
</p><pre class="crayon-plain-tag">ws.onmessage = function(evt) {
    console.log('ws onmessage() data:', evt.data);
    let message = JSON.parse(evt.data);
    if (message.type === 'offer') {
      // -- got offer ---
      console.log('Received offer ...');
      textToReceiveSdp.value = message.sdp;
      let offer = new RTCSessionDescription(message);
      setOffer(offer);
    }
    else if (message.type === 'answer') {
      // --- got answer ---
      console.log('Received answer ...');
      textToReceiveSdp.value = message.sdp;
      let answer = new RTCSessionDescription(message);
      setAnswer(answer);
    }
    else if (message.type === 'candidate') { // &lt;--- ここから追加
      // --- got ICE candidate ---
      console.log('Received ICE candidate ...');
      let candidate = new RTCIceCandidate(message.ice);
      console.log(candidate);
      addIceCandidate(candidate);
    }
  };

  function addIceCandidate(candidate) {
    if (peerConnection) {
      peerConnection.addIceCandidate(candidate);
    }
    else {
      console.error('PeerConnection not exist!');
      return;
    }
  }</pre><p> 
さあ、これで修正は完了です。</p>

<h3>Trickle ICEを実行しよう</h3>

<p>手順はVanilla ICEの場合と同じです。シグナリングサーバーを起動して、ChromeかFirefoxのウィンドウを2つ開いて修正したHTMLを読み込んでください。あとは同様に[Start Video]→[Connect]です。</p>

<p>見た目も特に変わりはありません。もしかしたら人によっては早く繋がるのを実感できるかもしれません。</p>

<p>GitHub Pages/GitHubも用意しています。</p>

<ul>
<li>GitHub Pages で試す <a href="http://mganeko.github.io/webrtcexpjp/basic2016/ws_signaling_1to1_trickle.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ws_signaling_1to1_trickle.html</a>

<ul>
<li>※http://～/ なのでFirefoxのみ。シグナリングサーバーは別途localhost上で起動しておく必要があります</li>
</ul></li>
<li>GitHub でソースを見る <a href="https://github.com/mganeko/webrtcexpjp/blob/master/basic2016/ws_signaling_1to1_trickle.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ws_signaling_1to1_trickle.html</a></li>
</ul>

<h2>2台のPC間の通信</h2>

<p>ここまできたら、せっかくなので2台の別々のPCで通信してみたくなります。同じネットワークに属するPC同士ならば通信できるはずです。例として次のような状況を考えてみましょう。</p>

<ul>
<li>IPアドレスが 192.168.0.2 と、 192.168.0.3 の2台のPCがある</li>
<li>前者(192.168.0.2)のポート:8080でWebサーバー、ポート:3001でNode.jsのシグナリングサーバーが動いている
<br /><a href="https://html5experts.jp/wp-content/uploads/2016/07/2pc_firefox.png" data-wpel-link="internal"><img src="/wp-content/uploads/2016/07/2pc_firefox-300x227.png" alt="2pc_firefox" width="300" height="227" class="alignnone size-medium wp-image-20049" srcset="/wp-content/uploads/2016/07/2pc_firefox-300x227.png 300w, /wp-content/uploads/2016/07/2pc_firefox.png 640w, /wp-content/uploads/2016/07/2pc_firefox-207x157.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></a><br /></li>
</ul>

<p>Firefoxの場合は、接続するURLを変更すれば問題なく動きます。やっかいなのはChromeの場合です。</p>

<ul>
<li>Chromeでは、カメラやマイクにアクセスするためのgetUserMedia()が、原則としてhttp://～では許可されていない/</li>
<li>http://localhost/～ は例外的な扱いで許可されている
<br /><a href="https://html5experts.jp/wp-content/uploads/2016/07/2pc_chrome.png" data-wpel-link="internal"><img src="/wp-content/uploads/2016/07/2pc_chrome-300x227.png" alt="2pc_chrome" width="300" height="227" class="alignnone size-medium wp-image-20048" srcset="/wp-content/uploads/2016/07/2pc_chrome-300x227.png 300w, /wp-content/uploads/2016/07/2pc_chrome.png 640w, /wp-content/uploads/2016/07/2pc_chrome-207x157.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></a><br /></li>
</ul>

<p>きちんと対処すると、次のような対策が必要です。ちょっと試すにはハードルが高いですよね。</p>

<ul>
<li>証明書を取得して https://～/ でアクセスするように、Webサーバーに設定</li>
<li>合わせて、シグナリングサーバーも wss://～ の暗号化通信を使うように設定</li>
</ul>

<p>そこで実験的に無理やり動かすには、次のような方法があります。Webサーバーとシグナリングサーバーは同一である必要はなく、また異なるWebサーバーでも構わないことを利用しています。
<br /><a href="https://html5experts.jp/wp-content/uploads/2016/07/2pc_chrome_force.png" data-wpel-link="internal"><img src="/wp-content/uploads/2016/07/2pc_chrome_force-300x226.png" alt="2pc_chrome_force" width="300" height="226" class="alignnone size-medium wp-image-20050" srcset="/wp-content/uploads/2016/07/2pc_chrome_force-300x226.png 300w, /wp-content/uploads/2016/07/2pc_chrome_force.png 640w, /wp-content/uploads/2016/07/2pc_chrome_force-207x156.png 207w" sizes="(max-width: 300px) 100vw, 300px" /></a><br /></p>

<p>お勧めはしませんが、どうしてもやりたい場合の参考としてどうぞ。</p>

<h2>次回は</h2>

<p>今回はNode.jsとWebSocketを使ったシグナリングを実現しました。残念ながら今回の仕組みでは、1対1の通信しか行うことができません。次回はこれを拡張し、複数人で同時に通信できるようにしたいと思います。</p>

<h2>オマケ：WebRTCの仕様の差分のおさらい</h2>

<p>オマケとして、今回のWebRTC再入門2016シリーズで取り上げているWebRTC関連仕様の変更箇所について、おさらいしおきましょう。(2016年6月現在）</p>

<h3>getUserMeida</h3>

<ul>
<li>navigator.mediaDevices.getUserMedia() が新しく用意された

<ul>
<li>旧APIの navigator.getUserMedia()は Firefoxでは非推奨</li>
</ul></li>
<li>ベンダープレフィックスが取れた</li>
<li>コールバックではなくPromiseベースになった</li>
<li>Firefox, Edge で利用可能。Chromeではフラグ指定が必要</li>
</ul>

<h3>ベンダープレフィックスの除去</h3>

<ul>
<li>Firefoxでは、主要なオブジェクトのベンダープレフィックスが取れた。mozプレフィックス付は非推奨に

<ul>
<li>新：RTCPeerConnection, RTCSessionDescription, RTCIceCandidate</li>
<li>旧：mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate　（非推奨）</li>
</ul></li>
<li>ただしChromeでは、一部ベンダープレフィックス付のまま

<ul>
<li>プレフィックス有り： webkitRTCPeerConnection</li>
<li>プレフィックス無し： RTCSessionDescription, RTCIceCandidate</li>
</ul></li>
</ul>

<h3>RTCPeerConnection</h3>

<ul>
<li>主要なメソッドがPromiseベースになった

<ul>
<li>createOffer(), createAnswer()</li>
<li>setLocalDescription(), setRemoteDescription()</li>
</ul></li>
<li>メディアストリーム処理の新しいイベントハンドラontrack()が追加、onaddstream()は非推奨

<ul>
<li>Firefoxではサポート済、Chromeでは未サポート</li>
</ul></li>
</ul>

<p><a href="https://www.w3.org/TR/webrtc/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">仕様</a>は常に更新されていますし、ブラウザの実装状況も異なります。最新の情報もご確認ください。</p>
]]></content:encoded>
		
		<series:name><![CDATA[WebRTC入門2016]]></series:name>
	</item>
		<item>
		<title>WebGLとWebSocketによる3Dオンラインレースゲーム「JS-Racing」の全て！（後編）</title>
		<link>/knockknockjp/10481/</link>
		<pubDate>Tue, 02 Sep 2014 00:00:00 +0000</pubDate>
		<dc:creator><![CDATA[西田慎吾]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[WebSocket]]></category>
		<category><![CDATA[Webアプリ]]></category>

		<guid isPermaLink="false">/?p=10481</guid>
		<description><![CDATA[連載： HTML5 Japan Cup 特集 (6)WebGLとWebSocketによる3Dオンラインレースゲーム「JS-Racing」の全て！（後編） 前回に引き続きHTML5 Japan Cup 2014にてWebG...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/5jcup-2/" class="series-207" title="HTML5 Japan Cup 特集" data-wpel-link="internal">HTML5 Japan Cup 特集</a> (6)</div><p>WebGLとWebSocketによる3Dオンラインレースゲーム「JS-Racing」の全て！（後編）</p>

<p><a href="https://html5experts.jp/knockknockjp/10226/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">前回</a>に引き続き<a href="https://5jcup.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">HTML5 Japan Cup 2014</a>にて<strong>WebGL賞</strong>と<strong>優秀賞</strong>をいただいたオンラインレースゲーム、<a href="http://js-racing.knockknock.jp/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">JS-Racing</a>の技術解説をさせていただきます。</p>

<p><img src="/wp-content/uploads/2014/08/img14.jpg" alt="img14" width="630" height="484" class="alignnone size-full wp-image-10498" srcset="/wp-content/uploads/2014/08/img14.jpg 630w, /wp-content/uploads/2014/08/img14-300x230.jpg 300w, /wp-content/uploads/2014/08/img14-207x159.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<h2>サーバサイドの使用技術</h2>

<p>サーバサイドの技術として<a href="http://nodejs.jp/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Node.js</a>を使用しています。Node.jsはサーバーサイドで動作するJavaScriptで、<strong>ノンブロッキングI/Oというモデルを採用しています。非同期処理でデータベースへのアクセスとWebページの表示を別々に行ってくれる</strong>ので、ストレスなく大量のページの表示が出来ます。また、Socket.ioというライブラリを扱うことで、WebSocketを使用したリアルタイム通信を実現するができます。</p>

<p>Node.jsでWebアプリケーションを構築する場合、実験的にローカルのみの開発の場合は問題ありませんが、公開するとなると、Node.jsをインストールして実行できるサーバが必要になります。そのためには共有サーバのレンタルではなく、<strong>管理者権限 (root) が付与される専用サーバや、仮想専用サーバ（VPS）のレンタルが必要</strong>になると思います。管理者権限のため自由に環境をセットアップする事ができるために、そのWebアプリケーションに合わせた環境構築が可能になります。</p>

<h3>ExpressによるWebアプリケーションフレームワークの利用</h3>

<p>Node.jsで作成したWebアプリケーションの場合、リクエストURIの解析からファイルの配信など、HTTPサーバの機能を実装しなくてはいけません。これらの<strong>基本機能が備わったWebアプリケーションフレームワークを利用することで、非常に手軽にWebアプリケーションを作成</strong>できます。今回は、多数開発されているWebフレームワークのなかでも、有名で多く利用されている<a href="http://expressjs.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Express</a>を利用しました。</p>

<p></p><pre class="crayon-plain-tag">var express = require("express");
var app = express();
// HTMLエンジンとしてjadeを使用
app.set("view engine", "jade");
app.set("views", __dirname + "/views");
app.use(express.static(__dirname + "/public"));
// js-racing.knockknock.jp/にアクセスがあった場合、
// /views/index.jadeに「title=JS Racing」と「version=ver1.2」の値を渡して、HTMLを表示
app.get("/", function(req, res) {
    res.render("index", {
        title: "JS Racing",
        version: "ver1.2"
    });
});
// js-racing.knockknock.jp/controller.htmlにアクセスがあった場合、
// /views/controller.jadeに「title=JS Racing」と「version=ver1.2」と「id=URLパラメータのidの値」の値を渡して、HTMLを表示
app.get("/controller.html", function(req, res) {
    res.render("controller", {
        title: "JS Racing",
        version: "ver1.2",
        id: req.query.id
    });
});
app.set("port", process.env.PORT || 3000);
var server = require("http").createServer(app);
server.listen(app.get("port"), function(){
    console.log("Express server listening on port " + app.get("port"));
});</pre><p></p>

<p>22行目の<code>req.query.id</code>は<code>http://js-racing.knockknock.jp:3000/controller.html?id=000000</code>として渡された、URLパラメータのidの値が参照できます。この値は、ソケット通信時に発行されたソケットIDの値で、PCから発行されたスマフォ用のURL（QRコードか短縮URL）にURLパラメータとして付加されたもので、この値を元にPCとスマートフォンとのペアリングを行います。</p>

<p><strong>ExpressはHTMLのテンプレートエンジンとして<a href="http://jade-lang.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Jade</a>か、<a href="http://embeddedjs.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ejs</a>を選択</strong>することができます。Jadeはインデントが必須です。閉じタグを省略できる代わりに、DOMの入れ子構造に沿って適切にインデントを使用する必要があります。基本的な記法がHTMLとは違うために、HTMLに親しんだマークアップエンジニアからすれば、慣れるまで違和感があるものだと思います。ejsはJadeとは違い、基本的な記法はHTMLをベースにしていますので、ローカルでくみ上げたHTMLをejsに組み込むことが容易です。今回はより楽にコード量を少なくHTMLを構築したいという点で、閉じたタグなしで記述できるJadeを選択しています。</p>

<p></p><pre class="crayon-plain-tag">// オープニングタイトル表示部分
section#sceneOpening.scene-opening(style="display: none;")
    div.scene-opening__inner
        div.scene-opening__box5
            h1.scene-opening__ttl
                // expressから受け取ったパラメータ、titleの値を挿入
                span.scene-opening__ttl__txt #{title}
                    br
                // expressから受け取ったパラメータ、versionの値を挿入
                span.scene-opening__ttl__txt2 &amp;nbsp;#{version}</pre><p></p>

<p>Jadeはこのようにインデントによって、要素の入れ子構造を定義しています。そのため閉じタグが不要で、<strong>HTMLを非常に簡略化</strong>して記述することが可能になります。HTMLを簡略化できるだけがJadeの特徴ではありません。<strong>HTMLをコンポーネント化して再利用</strong>したり、条件分岐をさせたり、繰り返し処理をさせたり、これまでサーバサイドで行っていたようなことがNode.jsで可能になります。ちなみにCSSはSass（Compass）を使い、記法にはBEMという命名規則を採用しています。</p>

<h3>Socket.ioによるリアルタイム通信</h3>

<p>車の同時走行と、スマートフォンからの車の操作を実現するためには、ブラウザとサーバ双方から、任意のタイミングでデータを送受信する必要があります。このリアルタイム通信を実現するために、<a href="http://socket.io/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Socket.io</a>を利用しています。Socket.ioは<strong>WebSocketなどのリアルタイム通信技術をラップして、シンプルなAPIを提供しているため、通信部分の煩雑さを意識することなく、リアルタイム通信を簡単に構築</strong>することができます。</p>

<p>スマートフォンからの車の操作を実現するために、接続時に発行されたソケットIDを共有することで、スマートフォンとPCをペアリングして相互通信しています。サーバを介しての通信なので、若干のタイムラグが発生する懸念もあったのですが、現状ではタイムラグを感じることはありませんでした。環境に依存することですので、あくまで今回の場合はということですが。</p>

<p></p><pre class="crayon-plain-tag">var socketArr = [];
io.sockets.on("connection", function(socket){
    // Socket.IDをPCに送信
    socket.emit("emit_id_form_server", socket.id);
    // PCから車の状態を受信
    socket.on("emit_carcondition_form_client", function(data){
        // 受信した車の状態
        var info = {
            id: socket.id,
            name: data.name,
            x: data.x,
            y: data.y,
            bodyAngle: data.bodyAngle,
            wheelAngle: data.wheelAngle,
            speed: data.speed,
            colorBody: data.colorBody,
            colorWing: data.colorWing,
            colorDriver: data.colorDriver
        };
        // 接続しているクライアントごとの情報を更新
        var flg = true;
        var i = 0, max;
        for (i = 0, max = socketArr.length; i &lt; max; i = i + 1) {
            if (socketArr[i].id == info.id) {
                socketArr[i] = info;
                flg = false;
            }
        }
        if (flg) {
            socketArr.push(info);
        }
    });
    // スマートフォンからの操作情報を受信
    socket.on("emit_controller_data_form_client", function(data){
        var id = data.id;
        var event = data.event;
        var value = data.value;
        var flg = false;
        var sockets = io.sockets.sockets;
        var i = 0, max;
        for (i = 0, max = sockets.length; i &lt; max; i = i + 1) {
            // ソケットIDが一致したクライアントに操作情報を送信
            if (sockets[i].id == id) {
                sockets[i].emit("emit_controller_data_from_server", {
                    id: id,
                    event: event,
                    value: value
                });
                flg = true;
            }
        }
        // ソケットIDが一致したクライアントが存在しなかった場合はスマートフォンに接続解除イベントを送信
        if (!flg) {
            socket.emit("emit_disconnect_client_from_server");
        }
    });
    // 接続しているクライアントの情報を1秒間に3回、接続しているクライアント全てに送信
    setInterval(function(){
        socket.emit("emit_other_carcondition_from_server", socketArr);
    }, 1000 / 3);
});</pre><p></p>

<p>接続している全てのクライアントに、接続している全てのクライアントの情報を送信するイベントは、ネットワークの負荷を考えて、1秒間に3回と制限をかけています。つまり3FPSのタイミングで同期をとることになりますが、クライアント側では30FPSでゲームが進行しているために、タイムラグが発生してしまいます。タイムラグによって発生する違和感をなくすために、クライアント側ではサーバ側から配信されるクライアント情報を元に、前回配信された情報との差分を10で割った値を算出して、クライアント側の1FPS毎の値として反映させ、違和感を解消しています。</p>

<h3>MongoDBでラップタイムの保存と走行データの保存</h3>

<p>ラップタイムを保存したり、走行データを保存するのにデータベースとして<a href="http://www.mongodb.org/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">MongoDB</a>を利用しています。MongoDBは、<strong>オープンソースのドキュメント指向データベース</strong>です。RDBMSのようにレコードをテーブルに格納するのではなく、ドキュメントと呼ばれる構造的データをオブジェクト形式でデータを管理します。
ちなみにNode.jsから<a href="http://mongoosejs.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Mongoose</a>を利用し、MongoDBに接続しています。Mongooseでは、Schemaインスタンスを通してModelを定義する事ができます。以下のようなオブジェクト形式でデータを管理出来るのが、MongoDBの特徴です。</p>

<p></p><pre class="crayon-plain-tag">var UserSchema = new mongoose.Schema({
    id:String, // ソケットID
    name:String, // 名前
    date:String, // 登録日
    time:Number, // ラップタイム
    comment: String, // コメント
    color: {
        body: String, // ボディカラー
        wing: String, // ウィングカラー
        driver: String // ドライバーカラー
    },
    runningPath: Array // 走行データ
});
var Users = db.model("user", UserSchema);</pre><p></p>

<p>走行データは1秒間に30回、車の位置と角度を記録した配列ですので、全てのユーザーの分だけ保存すると、データ量が肥大してしまう可能性があります。データ量を押さえるために、ラップタイムでソートして10位圏外の走行データは保持していません。</p>

<p>サーバサイドではNode.jsのおかげで、Webアプリケーションを構築する環境がとても整ってきていると思います。
基本的な言語はJavaScriptですので、サーバサイドもフロントエンドエンジニアが押さえるべき領域になっているのではと思います。</p>

<h2>その他の技術的トピック</h2>

<p>サーバサイド、クライアントサイドの技術それぞれの紹介を軸として、説明させていただきましたが、これ以外にもいくつか工夫した、技術的なポイントがありますので、紹介します。</p>

<h3>コースデータの共有</h3>

<p>3D表現を利用するために使用したthree.jsと、ゲームエンジンとして使った2D物理エンジンBox2DJSで、コースデータを共有するために2次元配列を使用しました。各値には数値を格納して、数値によってthree.jsでは壁、道路、芝、タイヤ、木、等の3Dモデルが配置され、Box2DJSでは3Dモデルの形状に沿った障害物を配置します。ちなみに開発当初、コースエディット機能や、SNSでコース共有機能等を考えていましたので、テキストデータとして扱いやすい2次元配列をコースデータとして採用したという経緯があります。</p>

<p></p><pre class="crayon-plain-tag">module imjcart.logic.map.value {
    export class MapConst {
        // コースデータ用の2次元配列
        static MAP:any = [           
　　　　　　[1,1,1,1,1,1,1,1,1,　〜省略〜　1,1,1,1,1,1,1,1,1,1,1], 
　　　　　　[1,4,4,4,4,4,4,4,4,　〜省略〜　4,4,4,4,4,4,4,4,4,4,1],
           〜省略〜　　　　　　
　　　　　　[1,1,1,1,1,1,1,1,1,　〜省略〜　1,1,1,1,1,1,1,1,1,1,1]
        ]
        // コースデータ配列内の各値が、何を表しているかの定数
        static MAP_KEY_NONE:number = 0; // アスファルト
        static MAP_KEY_WALL:number = 1; // 外壁
        static MAP_KEY_BLOCK:number = 2; // ブロック
        static MAP_KEY_TIRE:number = 3; // タイヤ
        static MAP_KEY_GRASS:number = 4; // 芝
        static MAP_KEY_TREE:number = 5; // 木
        static MAP_KEY_CAR_START_POSITION:number = 6; // 車のスタート位置
        static MAP_KEY_LAP_MEDIAN_CENTER_02:number = 7; // ゴールライン（逆走制御）
        static MAP_KEY_LAP_MEDIAN_CENTER_01:number = 8; // ゴールライン（逆走制御）
        static MAP_KEY_LAP_START_POINT:number = 9; // ゴールライン
        static MAP_KEY_SAND:number = 10; // 砂地
    }
}</pre><p></p>

<p>ただ、このような膨大な量の2次元配列を、テキストエディタで作成、編集するのは非常に非効率だと考えて、途中からExcelによって管理する方法に切り替えました。Excelでは条件付き書式を設定して、セルの値によって、わかりやすいように色づけするように設定しています。データ書き出しの際には、ExcelからCSVデータとしてテキストデータを書き出して、CSVデータをテキストエディタで置換して2次元配列にしています。</p>

<p><img src="/wp-content/uploads/2014/08/img12.gif" alt="img12" width="630" height="300" class="alignnone size-full wp-image-10487" /></p>

<h3>コースの装飾</h3>

<p>コースは直線と直角だけではなく、カーブ等の曲線も再現できなくてはいけません。2次元配列をコースデータとして利用する場合は、この点を補完する必要があります。また、コースというのは大抵、両脇のシケインとの境目にラインが引いてあります。こういったアスファルトや芝生といった要素以外の装飾を、周りの要素の配置条件に応じて追加することで、よりコースらしい外観を作る事ができます。下のキャプチャが装飾やカーブの補完をかけている物と、コースデータをそのまま表示したものとの違いです。</p>

<p><img src="/wp-content/uploads/2014/08/img13.jpg" alt="img13" width="630" height="254" class="alignnone size-full wp-image-10492" srcset="/wp-content/uploads/2014/08/img13.jpg 630w, /wp-content/uploads/2014/08/img13-300x120.jpg 300w, /wp-content/uploads/2014/08/img13-207x83.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<h2>まとめ</h2>

<p>以上が今回作成したJS-Racingの主な技術解説になります。Webアプリケーションを構築するには、今回解説したように多くの技術が必要となります。ご紹介した技術は、たくさんある中から必要なものを取捨選択した結果ですので、当然コンテンツが変われば、必要となる技術も変わります。このコンテンツで使用した主な技術の全体図をまとめました。</p>

<p><img src="/wp-content/uploads/2014/08/img02.jpg" alt="使用技術概要" width="630" height="400" class="alignnone size-full wp-image-10250" srcset="/wp-content/uploads/2014/08/img02.jpg 630w, /wp-content/uploads/2014/08/img02-300x190.jpg 300w, /wp-content/uploads/2014/08/img02-207x131.jpg 207w" sizes="(max-width: 630px) 100vw, 630px" /></p>

<p>オンラインレースゲームというシンプルな内容のコンテンツですが、プラットフォームとしてWebブラウザを使用している点に注目していただけると嬉しいです。今までご紹介した技術は、オンラインゲームを作るだけの技術ではなく、Webコンテンツを作る上で、大きな可能性を持っている技術になります。私はゲームを作る上でこれらの技術を利用していますが、Web上の様々なサービスと連携をとることで、もっと広がりを持つコンテンツを作ることができると思います。ぜひ皆さんも、HTML5とJavaScript（クライアントサイド、サーバサイド含む）を使って、Webアプリケーションを作ってみてください。</p>
]]></content:encoded>
		
		<series:name><![CDATA[HTML5 Japan Cup 特集]]></series:name>
	</item>
		<item>
		<title>NUCで手のひらサイズの格安WebSocketサーバーを立ててみた(アプリ起動編)</title>
		<link>/toshirot/4786/</link>
		<pubDate>Thu, 30 Jan 2014 23:50:59 +0000</pubDate>
		<dc:creator><![CDATA[高橋 登史朗]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[NUC]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[WebSocket]]></category>
		<category><![CDATA[Webアプリ]]></category>

		<guid isPermaLink="false">/?p=4786</guid>
		<description><![CDATA[連載： NUCでWebSocketサーバを立ててみた (3)前回まででNUCのハードを組み立てて、OSやJavaScript実行環境Node.jsなど、基本的なソフトのインストールを準備しました。 今回はいよいよWebS...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/nuc-try/" class="series-157" title="NUCでWebSocketサーバを立ててみた" data-wpel-link="internal">NUCでWebSocketサーバを立ててみた</a> (3)</div><p><a href="https://html5experts.jp/toshirot/4595/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">前回</a>まででNUCのハードを組み立てて、OSやJavaScript実行環境Node.jsなど、基本的な<a href="https://html5experts.jp/toshirot/4718/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ソフトのインストールを準備</a>しました。</p>

<p>今回はいよいよWebSocketサーバーを立ち上げ、アプリを動かします。</p>

<p>WebSocketサーバーといっても、今回はWebSocketサーバーそのものを作るわけではありません。サーバーにはNode.jsのnpmパッケージからwsというWebSocket用モジュールを利用します。</p>

<p>このモジュールはあの有名なSocket.IOで、WebSocketのコアの部分にも使われている高速なWebSocketサーバーを持っています。アイディア次第で、たとえば、チャットサーバーにでも、ゲームデータサーバーにでも、あるいは、ニュース配信サーバーにでも何にでも使えます。</p>

<p>「WebSocket」自体は双方向通信のプロトコルとAPIというだけですので、その使い方次第で用途は無数に考えられるわけです。</p>

<h2>今回作るもの</h2>

<p>今回は、NUCを使って、とりあえずWebSocketサーバーを立ち上げてみようという短期連載ですので、その雰囲気がわかる程度の簡単なものでやってみます。</p>

<p>恐縮ですが拙著「Node.jsプログラミングガイド」のサンプルの中から、WebSocketを使ったリアルタイムチャートを作ってみます。ここでのWebSocketサーバーは、チャートへデータを連続的にプッシュし続ける機能を担います。</p>

<p>まず最初に前回もチラ見した、仕上がりをもう一度見てみましょう。</p>

<p>下の赤い「Play」ボタンをクリックすると動きます。</p>

<p>(invalid jsdo.it code)</p>

<p>上記チャートは<a href="http://ccchart.com/test/ws2.htm" title="ccchart" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ccchart</a>ですが、チャートが変化し続けるためのデータがサーバー側からリアルタイムにプッシュされ続けています。Ajaxなどのように、ブラウザ側からユーザーがリクエストする必要はありません。</p>

<p>このケースでは、すべての接続しているブラウザのチャートへデータが自動的にプッシュされ続けます。</p>

<p>この仕組みを使えば、たとえば、社内の毎時変化するデータなどを関係者全員がリアルタイムに閲覧するといったシステムを作ることも簡単です。</p>

<p>ちなみに参考までですが、ccchartのメリットはCanvasベースなので高速でSVGよりもプラットフォーム依存が少ないことと、インターネットにつながっている必要もないのでイントラネットでも使えることなどでしょうか。</p>

<p>(※今回は解説できませんが、もし、このサンプルが動かない環境なら、ポートを80や443などにする必要があるかもしれません。ただし、Linuxでは80などのwell-known ポートは、今回のようなユーザー権限ではなくroot権限で起動する必要があり、セキュアにすることも考慮すると別途少し手間がかかります)</p>

<p>では、これをNUC内に置いてみます。</p>

<h2>ソースを取得する</h2>

<p>まず、今回使うソースを手に入れます。「Node.jsプログラミングガイド」の<a href="http://jsgt.org/np/code/doc/01.htm" title="Node.jsプログラミングガイド　サンプル" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">サンプルページ</a>
1-Basic Knowledge 基礎知識 から
02-wsserver2にある2つのファイル
<code>
chart.htm
wsserver.js
</code>
をコピーします。クライアント側のHTMLとJavaScript、つまりchart.htmのコードは、下記にもありますが jsdo.itのサンプルからもご確認いただけます。</p>

<h2>アプリ用のディレクトリを用意する</h2>

<p>ファイルを作る場所はお好みですが、今回は、ユーザー名を仮にhogeとして、ホームディレクトリ(/home/hoge) 配下へ前回作っておいた公開用ディレクトリ /home/hoge/public_html を利用して、Apacheで公開してみます。</p>

<p>作成するディレクトリツリーは、次のようになります。</p>

<p><code>
/home/hoge
   ├──public_html/
   │    └─mychart<br />
   │         └─chart.htm 
   └──wsserver.js
</code></p>

<p>まず、ホームディレクトリのpublic_html下にchart.htmを置くためのディレクトリmychartを作ります。
</p><pre class="crayon-plain-tag">cd /home/hoge/public_html
mkdir mychart
cd mychart</pre><p>
mychartディレクトリができたら、そこへ chart.htm をFTPでアップロードしておきましょう。wsserver.jsは、/home/hoge直下へ置きます。</p>

<p>次に、そのひとつ上の非公開ディレクトリ /home/hoge/ 内に node_modules というディレクトリを作ります。
</p><pre class="crayon-plain-tag">cd /home/hoge
mkdir node_modules</pre><p>
これは、Nodeのモジュールが格納される場所で、ディレクトリ/home/hoge配下でインストールするローカルなモジュールは、(インストールコマンドnpmの引数-gを指定しなければ) ここに入ります。</p>

<p>また、wsserver.jsはサーバー側ですので一般的には、公開ディレクトリpublic_htmlではなく非公開な場所(今回は/home/hoge直下)へアップロードします。</p>

<h2>WebSocket用モジュールをインストール</h2>

<p>では次にNodeのWebSocket用モジュールwsを入れてみます。これには高速なWebSocketサーバーとクライアントが同梱されています。</p>

<p>では入れてみましょう。
</p><pre class="crayon-plain-tag">npm i ws</pre><p>
これだけです。(npm install wsと同じ意味です)
では、node_modules下に本当に入ったのかlsコマンドで見てみましょう。
</p><pre class="crayon-plain-tag">ls -l /home/tato/node_modules</pre><p>
すると、下記のように表示されるのでwsが入っていることがわかります。
<code>
total 4
drwxrwxr-x 11 hoge hoge 4096  1月 15 18:36 ws
</code>
作成されたディレクトリツリーは、次のようになっているはずです。</p>

<p><code>
/home/hoge
   ├──public_html/
   │    └─mychart<br />
   │         └─chart.htm 
   ├──wsserver.js
   └──node_modules/ ←追加された
        └─ ws/
</code></p>

<p>これで、設置完了です。簡単でしょう？
でも、ここでhttp://192.168.1.180/~hoge/chart.htmをブラウザで開いても動きません。</p>

<p>サーバーが起動していないからです。</p>

<p>では、動かしましょう。</p>

<h2>WebSocketサーバーを起動する</h2>

<p>以下のおまじないを打ち込みます。ホームディレクトリ/home/hoge/下のwsserver.jsをNodeで動かすという意味です。
</p><pre class="crayon-plain-tag">node ~/wsserver.js</pre><p>
node /home/hoge/wsserver.jsでも同じ意味になります。</p>

<p>もし、ここでエラーが出なければ、同一ネットワーク内の他のパソコンのブラウザで http://192.168.1.180/~hoge/chart.htm を開いてみてください。</p>

<p>動いてますね。さらに、別のパソコンで開いても見ることができます。</p>

<h2>永続化</h2>

<p>「node ~/wsserver.js」でサーバーを起動しましたが、このままでは、sshの接続を切るとサーバーが止まってしまいます。</p>

<p>そこで、永続化やNUCの再起動時も自動で立ち上がるための仕掛けが必要です。</p>

<p>永続化には、foreverというモジュールがよく使われます。再起動時の自動起動は、/etc/init.dへの登録などの方法がありますが、残念ながら紙面の都合で書ききれませんので、不明な方はこれらのキーワードで調べてみてください。</p>

<p>この連載のアクセスが多ければ続きを書けるかもしれません(笑)。
でも、それを待つよりも、もしわからなければ、自分で調べると身につきますよ。</p>

<h2>サーバー側のコード</h2>

<p>参考までにサーバー側ソースは次のようなものです。</p>

<p>手短に言うと、指定したポートへクライアントから接続があると、0.3秒ごとに生成しているデータをすべてのクライアントへプッシュしています。詳しくはソースとコメントをご覧ください。</p>

<p></p><pre class="crayon-plain-tag">//WebSocket Serverモジュールを読み込む
var WsServer = require('ws').Server;
//port: 3001でリッスンするサーバーを作成する
var ws = new WsServer({ port: 3001 });
//タイマーID
var tid;

broadCast();//データ配信開始

function broadCast(){
  //0.3秒ごとにデータを送信する
  tid = setInterval (function(){
    //データを作る
    var dataAry = mkData();
    //すべてのクライアントへ送信する
    ws.clients.forEach(function(client) { 
      client.send(JSON.stringify(dataAry));
    });
  }, 300);
}

// 送信用ランダムデータを作成する(実際には必要なデータを与える)
function mkData(){
  //送信データ形式の雛形
  var data = [
      ["時間"],
      ["s2"],
      ["s3"]
    ];

  //時間の文字列を作成する
  var now = new Date();
  var H = now.getHours();
  var M = now.getMinutes();
  var S = now.getSeconds();
  H = (H &lt; 10)?&#039;0&#039;+H:H;
  M = (M &lt; 10)?&#039;0&#039;+M:M;
  S = (S &lt; 10)?&#039;0&#039;+S:S;
  
  //送信データを作成する
  data[0]=H +&#039;:&#039; + M +&#039;:&#039; + S;
  data[1]=Math.floor(Math.random(10) * 96 );
  data[2]=32 + Math.floor(Math.random(10) * 18);
  return data;
}</pre><p></p>

<h2>クライアント側のコード</h2>

<p>ccchart自体の使い方は今回は紙面の都合で触れませんが、ポイントは、ccchartのwsメソッドの引数でサーバー側のIPアドレスとポートを指定している部分です。そこから流れてきたデータを自動的にconfigで指定した形式にチャート化します。</p>

<p></p><pre class="crayon-plain-tag">&lt;script src="http://ccchart.com/js/ccchart.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;canvas id="hoge"&gt;&lt;/canvas&gt;
&lt;script&gt;

var chartdata = {
 //チャートの設定
 "config": {
    "title": "WebSocket TEST",
    "subTitle": "列データをリアルタイム受信描画 受信パターンはoneColAtATime",
    "type": "bezi2",//チャートタイプベジェ曲線 他にline, barなど
    "width" : 465,//幅
    "lineWidth": 2,//線の太さ
    "minY": 0,//最小Y値
    "xScaleSkip": 5,//垂直目盛のスキップ
    "colorSet": //カラーセット
          ["#DDA0DD","#3CB000"]
  },
  //データの雛形
  "data": [
    ["時間"],
    ["s2"],
    ["s3"]
  ]
};

ccchart
  .init('hoge', chartdata) //要素hogeへチャートデータchartdataを出力する
  .ws('ws://192.168.1.180:3001') //このURLをサーバー側のIPとポートに合わせる
  .on('message', ccchart.wscase.oneColAtATime);//このパターンで受信する
        // oneColAtATimeは、WebSocketの受信パターン関数
        // 一度に1列ずつ [["2013"],[435],[600]] といった配列で届く場合用
        // 参照: http://ccchart.org/test/someCols/test-2.htm
&lt;/script&gt;</pre><p></p>

<h2>最後に</h2>

<p>以上、駆け足で手のひらサイズのWebSocketサーバーを組み立ててみました。</p>

<p>ほんの10年ほど前までは、一般的には、手のひらサイズのサーバーなどほとんど考えることもありませんでしたが、今、それはすでに目の前にあります。</p>

<p>一方に社外クラウドサーバーを利用する大きな流れがありますが、同時に、目の前には、わずか数万円でモニタの裏にも貼れるような小さな内製サーバーを作れるという選択肢も生まれたわけです。</p>

<p>エクセルを使って仕事の効率を上げるように、ちょっとした業務には、どこにでも持っていける小さな内製サーバーを使って効率を上げるということも可能な時代になりつつあります。</p>

<p>多くの業務と製品がネットワーク経由のサービスと無縁ではなくなりつつある今、たとえば、機動性の高いサービスをより現場に近い場所で、NUCを使って次々と立ち上げるといった未来が、文字通り、今、私たちの手の上にあります。</p>

<p>NUC 自体の用途は限定されていません。さて、どんな未来を作りましょうか？</p>

<p>＜関連レポート＞<br>
<a href="https://html5experts.jp/toshirot/4595/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><strong>NUCで手のひらサイズの格安WebSocketサーバーを立ててみた(ハード組立編)を読む⇒</strong></a><br>
<strong><a href="https://html5experts.jp/toshirot/4718/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">NUCで手のひらサイズの格安WebSocketサーバーを立ててみた（OSインストール編）を読む⇒</a></strong><br></p>
]]></content:encoded>
		
		<series:name><![CDATA[NUCでWebSocketサーバを立ててみた]]></series:name>
	</item>
		<item>
		<title>NUCで手のひらサイズの格安WebSocketサーバーを立ててみた(OSインストール編)</title>
		<link>/toshirot/4718/</link>
		<pubDate>Thu, 30 Jan 2014 01:00:23 +0000</pubDate>
		<dc:creator><![CDATA[高橋 登史朗]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[NUC]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[WebSocket]]></category>

		<guid isPermaLink="false">/?p=4718</guid>
		<description><![CDATA[連載： NUCでWebSocketサーバを立ててみた (2)前回は、NUCのハードを組み立ててましたが、次は、ソフトのインストールです。慣れてる方には無用な講義ですが、一応ざっくりとした手順をメモしてみました。 もちろん...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/nuc-try/" class="series-157" title="NUCでWebSocketサーバを立ててみた" data-wpel-link="internal">NUCでWebSocketサーバを立ててみた</a> (2)</div><p><a href="https://html5experts.jp/toshirot/4595/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">前回</a>は、NUCのハードを組み立ててましたが、次は、ソフトのインストールです。慣れてる方には無用な講義ですが、一応ざっくりとした手順をメモしてみました。</p>

<p>もちろん、必ずしもこの手順通りである必要はありません。また、目安の時間を入れてありますが、あくまでも目安であって、休憩時間も含みません(笑)。</p>

<p>焦らずに、もしわからないところがあればネットなどで調べながらでも着実にやっていきましょう。</p>

<h3>【30分】OSダウンロード</h3>

<p>OSは最近人気のLinuxディストリビューションUbuntuサーバーでバージョン13.10を入れました。下記でダウンロードしてDVDに焼いて準備します。</p>

<p>Ubuntu入手先
<a href="http://www.ubuntu.com/download/server" title="Ubuntuサーバー ダウンロード先" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">http://www.ubuntu.com/download/server</a></p>

<h3>【12分】OSインストール</h3>

<p>DVDをUSBへ接続してNUCを起動すると、Ubuntuのインストールが始まります。</p>

<p>※インテルによるOSインストールの<a href="http://www.intel.com/jp/support/motherboards/desktop/sb/cs-033935.htm" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">説明ページ</a>もあります。</p>

<p>今回は、最初はssh以外は特に何もいれず、必要なものは後から必要な時に追加する方針でタイトなインストールをしました。Ubuntuサーバー自体の詳細なインストール方法はここでは省略しますが、miniSSDのせいか、HDDのPCで20～30分かかるこの作業が、なんと10分程で終わりました。</p>

<p>特別な設定がなければ、・ユーザー名とパスワード・ディスクへの書き込み許可・ソフトウェアの選択で「OpenSSH server」をスペースキーで選ぶ以外は、質問にほぼ改行するだけでもインストールできます。</p>

<p>ネットワーク設定はもしDHCPで割り当てのある環境なら最初はお任せでも構いませんし、もし、IPを決めてあるなら設定しておくとあとの手間が省けます。</p>

<p>ここでは、ユーザー名は仮にhogeとしました。
そして、OSインストール後の作業は次の通り。</p>

<h3>【1分】自分のIPアドレスを確認してみる</h3>

<p>OSインストール後、再起動してログインしたら、IPアドレスを確認してみましょう。
</p><pre class="crayon-plain-tag">ifconfig</pre><p>
と打ってみます。するとたとえば次のように表示されます。
<code>
hoge@ubuntu:~$ ifconfig<br />
em1       Link encap:Ethernet  HWaddr ec:a8:6b:fe:a3:eb<br />
          inet addr:192.168.1.180  Bcast:192.168.1.255  Mask:255.255.255.0
　　　：
</code>
この NUC の場合は「inet addr」のあとの 192.168.1.180 が IP アドレスです。自分でIPアドレスを指定する場合は、/etc/network/interfaces を修正します。もし、ルーターやバーチャルドメインなどを設定すれば、普通のサイトのようにサーバーを外部へ公開することも可能ですが、今回はそこは省略します。</p>

<h3>【1分】SSHクライアントからログイン</h3>

<p>Ubuntuインストール時に入力したアカウント(今回はhoge)で他のパソコンから上記IPアドレスへログインします。Macならターミナルを使い、たとえば、パスワード認証なら、次のように打てば対話形式で入れます。
</p><pre class="crayon-plain-tag">ssh hoge@192.168.1.180</pre><p>
Windows なら SSH クライアントがなければ<a href="http://sourceforge.jp/projects/sfnet_poderosa/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"> Poderosa </a>などをインストールし、それでログインします。</p>

<h3>【30秒】apt-get（パッケージ管理システム ）のアップデート</h3>

<p>apt-getは必要なパッケージのダウンロードからインストールを依存関係も含めて自動的に行ってくれる便利な管理システムです。次のコマンドでそのパッケージ・リストをまず更新しておきます。sudoでアカウントのパスワードを要求されるので入力するとアップデートが始まります。
</p><pre class="crayon-plain-tag">sudo apt-get update</pre><p></p>

<h3>【20秒】FTP（ファイル転送サーバー）のインストール</h3>

<p>HTMLやjsファイルをNUCへアップロードするために FTP サーバーも入れておきます。下記apt-getですぐに動作しますが、各種設定をする場合は別途ネットなどでご確認ください。
</p><pre class="crayon-plain-tag">sudo apt-get install proftpd</pre><p>
対話形式で設問に答えますが、起動方法でinetdかスタンドアロンかを聞かれます。ここでは inetd で設定しました。</p>

<h3>【20秒】Apache（Webサーバー）のインストール</h3>

<p>WebSocket単体で使う場合や、Nodeなどでhttpサーバーを立てるならまったく不要ですが、やはりApacheはあると便利なので入れておきます。今回は、HTMLはApache で表示し、WebSocketサーバーからのデータをそのHTML内の JavaScript で受け取るという形にしてみます。
</p><pre class="crayon-plain-tag">sudo apt-get install apache2-mpm-prefork</pre><p>
ちなみにここで他のパソコンから、http://192.168.1.180と打てば下記画像のように「It/ works!」と表示され、もうすでにWebサーバーが動作してることがわかります。このHTMLファイルはデフォルトで /var/www/index.html にありますので、もう、これを書き換えるだけで HTTPサーバーは使えますが、今回のアプリはユーザーディレクトリに作ります。</p>

<p><img src="/wp-content/uploads/2014/01/itworks1.png" alt="Apache Webサーバーの動作確認" width="450" height="350" /></p>

<h3>【30秒】ユーザー用公開ディレクトリを用意する</h3>

<p>今回はユーザー(hoge)のディレクトリ配下にアプリを作ってみます。次のようにして公開ディレクトリを用意します。</p>

<ol><li>1行目・ユーザーディレクトリを作成する</li><li>2行目・ユーザーディレクトリを有効にする</li><li>3行目・Apacheをリスタートする</li></ol>

<p></p><pre class="crayon-plain-tag">mkdir ~/public_html
sudo a2enmod userdir
sudo service apache2 restart</pre><p>
ここで http://192.168.1.180/~hoge/　と打って
下記画像のようなユーザーディレクトリが表示されればOKです。</p>

<p><img src="/wp-content/uploads/2014/01/userhoge.png" alt="ユーザーディレクトリの表示" width="450" height="350" /></p>

<h3>【2分】nvm（Nodeバージョン管理マネージャー）インストール</h3>

<p>nvmは、複数のNode.jsバージョンをインストールし、切り替えて使うことを可能にするマネージャーです。Nodeはバージョンのアップデートが早いのでモジュールの依存対応も含めて簡単に切り替えられるnvmなどのバージョン管理マネージャーを入れておくと便利です。
gitを使ってインストールし、ログイン時の自動起動もセットしておきます。nvmの詳細は別途ご確認ください。</p>

<ol><li>1行目・git-coreをインストールする</li><li>2行目・gitから/home/hoge/nvmへダウンロードする</li><li>3行目・nvmを起動する</li><li>4行目・nvmのヘルプを見てみる</li><li>5行目・ログイン時にnvmが自動起動するように/home/hoge/.bashrcへ起動スクリプトをセットする</li></ol>

<p></p><pre class="crayon-plain-tag">sudo apt-get install git-core
git clone git://github.com/creationix/nvm.git ~/nvm
. ~/nvm/nvm.sh
nvm help
echo ". ~/nvm/nvm.sh" &gt;&gt; ~/.bashrc</pre><p></p>

<p>途中で[yes, no]と聞かれたら、特に意図が無ければ普通は、yesと答えます。</p>

<h3>【2分】Node.js（JavaScript実行環境 ）インストール</h3>

<p>そして、Nodeの最新安定バージョン今回は(v0.10.23)をインストール</p>

<ol><li>1、2行目・ビルド用のパッケージ群などをあらかじめ入れておく</li><li>3行目・nvmでNodeのv0.10.23をインストールする</li><li>4行目・nvmのヘルプを見てみる</li><li>5行目・ログイン時にv0.10.23が使えるようにセットする</li></ol>

<p></p><pre class="crayon-plain-tag">sudo apt-get install build-essential
sudo apt-get install libssl-dev
nvm install v0.10.23
echo "nvm use v0.10.23" &gt;&gt; ~/.bashrc</pre><p></p>

<p>さて、ここまでで今回のWebSocketサーバー用の環境ははぼ完成です。
もうNode.jsは使えるようになっています。</p>

<p><a href="https://html5experts.jp/toshirot/4786/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">次回</a>はいよいよ、WebSocketサーバーを入れてアプリを立ち上げてみます。</p>

<p>＜関連レポート＞<br>
<strong><a href="https://html5experts.jp/toshirot/4595/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">NUCで手のひらサイズの格安WebSocketサーバーを立ててみた(ハード組立編)を読む⇒</a></strong><br>
<a href="https://html5experts.jp/toshirot/4786/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><strong>NUCで手のひらサイズの格安WebSocketサーバーを立ててみた(アプリ起動編)を読む⇒</strong></a></p>
]]></content:encoded>
		
		<series:name><![CDATA[NUCでWebSocketサーバを立ててみた]]></series:name>
	</item>
		<item>
		<title>NUCで手のひらサイズの格安WebSocketサーバーを立ててみた(ハード組立編)</title>
		<link>/toshirot/4595/</link>
		<pubDate>Wed, 29 Jan 2014 01:00:31 +0000</pubDate>
		<dc:creator><![CDATA[高橋 登史朗]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[NUC]]></category>
		<category><![CDATA[WebSocket]]></category>

		<guid isPermaLink="false">/?p=4595</guid>
		<description><![CDATA[連載： NUCでWebSocketサーバを立ててみた (1)2012年11月からインテルが出荷をはじめたNUC (Next Unit of Computing)という製品群が自作系PCながら意外にひそかな(?)注目を集め...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/nuc-try/" class="series-157" title="NUCでWebSocketサーバを立ててみた" data-wpel-link="internal">NUCでWebSocketサーバを立ててみた</a> (1)</div><p>2012年11月からインテルが出荷をはじめたNUC (Next Unit of Computing)という製品群が自作系PCながら意外にひそかな(?)注目を集めています。<a href="http://www.google.co.jp/trends/explore#q=Next%20Unit%20of%20Computing" title="Googleトレンド" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">※1</a></p>

<p>今回はこのNUCを使って、手のひらサイズの格安 WebSocket サーバーを立ち上げ、それで動くアプリを作ってみました。</p>

<h2>NUCって何？</h2>

<p>NUCは、本体にメモリやストレージなどを追加して自分で組み立てる、いわゆる自作パソコンの仲間ですが、その特徴は、10com四方の超小型でありながら、最新性能で格安ということです。</p>

<p>それは、こんな外観です。
<img src="/wp-content/uploads/2014/01/8__.jpg" alt="NUC DCCP847DYE" width="500" /></p>

<p>手のひらサイズで総額3～7万円程度で仕上がるにも関わらず、ストレージはminiSSD、CPUはCore i5も選べて無線 LAN・bluetooth タイプもあり、さらに全機種マルチモニター出力可能というフルスペック以上なマシンです。</p>

<p>性能も十分高いので、小さなパソコンとしてだけではなく、社内のいろいろなサーバー用途や、あるいは、複数ディスプレイをつなげるなどして、これから普及が見込まれるリアルタイムデジタルサイネージ（電子看板）などでも重宝しそうという優れもの。</p>

<p>多くの業務と製品がネットワーク経由のサービスと無縁ではなくなりつつある今、わずか数万円でどこにでも持ち歩ける手のひらサイズのサーバーを作り、たとえば、機動性の高いサービスをより現場に近い場所で、NUCを使って次々と立ち上げるというのも面白いかもしれません。</p>

<p>最近はスマートフォンなどの進撃に押されつつある巨人インテルですが、Next Unit of Computingという名称に込めたインテルの気合いが伝わってきます。</p>

<p>インテルは長友だけではないのです^^;。</p>

<h2>今回作るサンプルをチラ見</h2>

<p>さて、ここから、NUCの組み立てからOSやFTP、Apache、Nodeなどのインストールをこつこつやっていきますが、多少地味な作業なので、モチベーションを維持するべく、今回のお題の最終到達地点をチラ見しておきましょう。</p>

<p>WebSocketによるプッシュデータを受け取って表示するリアルタイムチャートです。
下の赤い「Play」ボタンをクリックすると動きます。(※もし、このサンプルが動かない環境ならポートを80や443などにする必要があるかもしれません)</p>

<p>(invalid jsdo.it code)</p>

<p>この仕組みを使えば、たとえば、社内の毎時変化する様々なデータの推移状況(たとえば、刻々と変わる販売チャートや様々なセンサー監視チャート、サーバーログチャート)などを関係者全員がリアルタイムにリロードなしで閲覧するといったシステムを作ることもできます。しかも、わずか数万円で。</p>

<p>では、材料から集め始めましょう。</p>

<h2>今回使った材料</h2>

<p>今回の素材は昨年2月に発売された格安版「NUC、DCCP847DYE」。</p>

<p>CPUはCeleronですが、miniSSDを付けて駆動すれば充分使えます。そして何より安い！今回は1万7千円弱程度で買えました。（※2014年1月現在。価格は変動します）</p>

<h2>レシピ</h2>

<p>このNUCを普通のデスクトップとしてではなく、今回はLinuxのUbuntuを入れ、Node.js+WebSocket サーバーとして、HTML5 Experts.jpらしく料理してみます。</p>

<ul>
    <li><strong>名前：</strong>手のひらサイズの格安 WebSocket サーバー</li>
    <li><strong>所要時間：</strong>2時間弱(うまくいけば小1時間)</li>
    <li><strong>難易度： </strong>Linuxを少し使える(または、使いたいという意思を持っている)方</li>
    <li><strong>素材 ハード：</strong>(総額約3万円程度 *価格は変動します)<ol>
　　　 <li>NUC DCCP847DYE　16,600円</li><li>ストレージ　 8,889円 PLEXTOR PX-64M5M SATA 6Gb/s対応の高速 mSATA SSD64GB </li><li>メモリ　5,615円  G.Skill F3-1600C9D-8GRSL (1.35 V対応) 4G</li><li>電源ケーブル　490円 BUFFALO 3P型ミッキープラグ BSACC0802BK 0.2m</li><li>(上記の他にキーボード、マウス、HDMI ケーブルとOSインストール用 USBタイプのDVDディスクドライブも用意しますが、サーバーなので Win や Mac から SSH でリモート接続して使うと稼働後はほぼ不要なので他のパソコン用のものを流用しました。)</li></ol></li>
    <li><strong>素材 ソフト：</strong>（0円）<ol>　　　 <li>OS　Ubuntu Server  13.10</li><li>JavaScript実行環境 Node.js v0.10.23</li><li>WebSocketサーバー ws</li><li>その他 パッケージ管理 npm, バージョン管理nvm, Webサーバー Apache</li></ol></li>
</ul>

<p>一応、作業時間もメモしておきますが、これらはあくまで目安ですのでご了承ください。</p>

<h3>【20分】組み立てます</h3>

<p>では組み立ててみます。
(時間を節約するためにできれば、下のOSダウンロードも平行して進めておきましょう)</p>

<h6>パッケージを開けると、おなじみインテルCMのあのジングルが鳴る♪</h6>

<p><img src="/wp-content/uploads/2014/01/3__.jpg" alt="パッケージをあけるとおなじみインテルCMのあのジングルが鳴る♪" width="300" height="200" /></p>

<h6>mSATA SSDはこんなに小さい(別売)</h6>

<p><img src="/wp-content/uploads/2014/01/10.jpg" alt="SATA SSDはこんなに小さい(別売)" width="300" height="200" /></p>

<h6>裏ぶたを外して、メモリとmSATA SSDを取り付け。説明図見ながら付ける。</h6>

<p><img src="/wp-content/uploads/2014/01/5__.jpg" alt="裏ぶたを外して、メモリとmSATA SSDを取り付け" width="300" height="200" /></p>

<h6>ふたを閉める。もう完成！</h6>

<p><img src="/wp-content/uploads/2014/01/6__.jpg" alt="ふたを閉める。もう完成！" width="300" height="200" /></p>

<h6>モニタの裏に取り付けるVESA規格のパーツもついてる</h6>

<p><img src="/wp-content/uploads/2014/01/IMG_4316.jpg" alt="モニタの裏に取り付けるパーツもついてる" width="300" height="200" /></p>

<p>あっという間に完成です。</p>

<p><a href="https://html5experts.jp/toshirot/4718/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">次回</a>は、このNUCにソフトをインストールします。</p>

<p>予習：OSはUbuntuサーバーを使いますが、次回のためにあらかじめ下記のOSをダウンロードして、DVDに焼いておきましょう。Ubuntu入手先(Ubuntuサーバー　バージョン 13.10)
<a href="http://www.ubuntu.com/download/server" title="Ubuntuサーバー ダウンロード先" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">http://www.ubuntu.com/download/server</a>　*もし、DVDの焼き方がわからなくても自力で調べてやってみましょう。</p>

<p>＜関連レポート＞<br>
<strong><a href="https://html5experts.jp/toshirot/4718/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">NUCで手のひらサイズの格安WebSocketサーバーを立ててみた（OSインストール編）を読む⇒</a></strong><br>
<a href="https://html5experts.jp/toshirot/4786/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><strong>NUCで手のひらサイズの格安WebSocketサーバーを立ててみた(アプリ起動編)を読む⇒</strong></a></p>
]]></content:encoded>
		
		<series:name><![CDATA[NUCでWebSocketサーバを立ててみた]]></series:name>
	</item>
		<item>
		<title>リアルタイムにライブ映像をマンガ化「マンガテレビ」アーキテクチャ編</title>
		<link>/komasshu/1649/</link>
		<pubDate>Mon, 26 Aug 2013 04:00:45 +0000</pubDate>
		<dc:creator><![CDATA[小松 健作]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[Chrome]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[WebRTC]]></category>
		<category><![CDATA[WebSocket]]></category>
		<category><![CDATA[Webアプリ]]></category>

		<guid isPermaLink="false">/?p=1649</guid>
		<description><![CDATA[「漫画家の先生をゲストに迎えた対談イベントで、“マンガ”をフィーチャーしたエンターテインメントを最新のWeb技術で作れないだろうか？」そんな思いから生まれた「マンガテレビ」。映像をリアルタイムで“マンガ化”するだけでなく...]]></description>
				<content:encoded><![CDATA[<p>「漫画家の先生をゲストに迎えた対談イベントで、“マンガ”をフィーチャーしたエンターテインメントを最新のWeb技術で作れないだろうか？」そんな思いから生まれた「マンガテレビ」。映像をリアルタイムで“マンガ化”するだけでなく、イベントをもり上げる各種機能が盛りだくさん。今回は、そのアーキテクチャを紹介します。</p>

<p><span id="more-1649"></span></p>

<h1>マンガテレビの紹介</h1>

<p>「マンガテレビ」は最新のWeb技術をふんだんに活用し、イベント映像を“リアルタイムでマンガ化”するエンターテイメントツール。俗にいう「技術の無駄遣い」Webアプリケーションです。我らが、HTML5 Experts.jpの編集長「白石俊平」氏が開催している<a href="http://www.kakkoii.tv/events/20130726/index.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">「（白石俊平と）カッコいいやつら」</a>のイベント用ツールとして開発しました。</p>

<p>「マンガテレビ」を説明するのに言葉は不要。とにかく、<a href="https://app.html5experts.jp/manga/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">サイトにアクセス</a>してみてください。ただし、Chromeでしか動作しません（後述のWeb Speech API利用のため）ので、その点ご注意ください。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/68fdef15b331b326d339ad36e4fc77c4.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/68fdef15b331b326d339ad36e4fc77c4-300x244.png" alt="スクリーンショット 2013-08-23 1.13.22" width="300" height="244" class="alignnone size-medium wp-image-1663" srcset="/wp-content/uploads/2013/08/68fdef15b331b326d339ad36e4fc77c4-300x244.png 300w, /wp-content/uploads/2013/08/68fdef15b331b326d339ad36e4fc77c4-1024x834.png 1024w, /wp-content/uploads/2013/08/68fdef15b331b326d339ad36e4fc77c4-207x168.png 207w, /wp-content/uploads/2013/08/68fdef15b331b326d339ad36e4fc77c4.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p><i>なお、初回アクセス時は、下図のようにカメラとマイクへのアクセスを確認するダイアログが画面上部に表示されますので、「許可」をクリックしてください。</i></p>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/360b376de712a69fa57fc0943f063a43.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/360b376de712a69fa57fc0943f063a43-300x18.png" alt="スクリーンショット 2013-08-23 11.26.03" width="300" height="18" class="alignnone size-medium wp-image-1834" srcset="/wp-content/uploads/2013/08/360b376de712a69fa57fc0943f063a43-300x18.png 300w, /wp-content/uploads/2013/08/360b376de712a69fa57fc0943f063a43-1024x62.png 1024w, /wp-content/uploads/2013/08/360b376de712a69fa57fc0943f063a43-207x12.png 207w, /wp-content/uploads/2013/08/360b376de712a69fa57fc0943f063a43.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/79442564a542884b7ec637d465e771b2.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/79442564a542884b7ec637d465e771b2-300x36.png" alt="スクリーンショット 2013-08-23 11.26.21" width="300" height="36" class="alignnone size-medium wp-image-1833" srcset="/wp-content/uploads/2013/08/79442564a542884b7ec637d465e771b2-300x36.png 300w, /wp-content/uploads/2013/08/79442564a542884b7ec637d465e771b2-1024x125.png 1024w, /wp-content/uploads/2013/08/79442564a542884b7ec637d465e771b2-207x25.png 207w, /wp-content/uploads/2013/08/79442564a542884b7ec637d465e771b2.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p>かの有名アプリ<a href="http://tokyo.supersoftware.co.jp/mangacamera/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">「漫画カメラ」</a>を最大限に「リスペクト」したことは一目瞭然です。なお、「マンガ化」のアルゴリズムは<a href="http://www.slideshare.net/masayukimaekawa/java-script-14727253" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">こちらのslideshare(スライド9)</a>を参考にさせていただきました。</p>

<p>しかし、そのままリスペクトするだけではなく、Webならではの機能をふんだんに盛り込みました。</p>

<p>盛り込んだ機能は大きく二つ。一つ目は「みんなで『かっこいいね！！』」機能です。これにより視聴者参加型の「マンガテレビ」を実現しました。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/6edcbc8f5a028530f0da3a910a11c1b6.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/6edcbc8f5a028530f0da3a910a11c1b6-300x257.png" alt="スクリーンショット 2013-08-23 1.17.29" width="300" height="257" class="alignnone size-medium wp-image-1665" srcset="/wp-content/uploads/2013/08/6edcbc8f5a028530f0da3a910a11c1b6-300x257.png 300w, /wp-content/uploads/2013/08/6edcbc8f5a028530f0da3a910a11c1b6-1024x880.png 1024w, /wp-content/uploads/2013/08/6edcbc8f5a028530f0da3a910a11c1b6-207x177.png 207w, /wp-content/uploads/2013/08/6edcbc8f5a028530f0da3a910a11c1b6.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p>Webの特徴は、みんなが簡単に繋がれること。そこで、白石さん謹製の<a href="http://zapper.kakkoii.tv/?eventId=51ecca6063b71c4b39000012" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">「かっこいいねボタン」</a>と連携し、様々なエフェクトがかかるようにしました。「ボタン」を3回連打すると「ざわ・・」、5回連打すると「ゴ・・・」が画面を横切るという仕掛け。イベント参加者が「ちょっと、かっこいいね！」と連打すれば「ざわざわざわ・・」が、「すごい！！かっこいいね！！」と連打すると「ゴゴゴゴゴゴ・・・」と文字が横切っていきます。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/bfea3c4a6086dc8d10b68cd13375360a.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/bfea3c4a6086dc8d10b68cd13375360a-202x300.png" alt="スクリーンショット 2013-08-23 1.18.43" width="202" height="300" class="alignnone size-medium wp-image-1666" srcset="/wp-content/uploads/2013/08/bfea3c4a6086dc8d10b68cd13375360a-202x300.png 202w, /wp-content/uploads/2013/08/bfea3c4a6086dc8d10b68cd13375360a-689x1024.png 689w, /wp-content/uploads/2013/08/bfea3c4a6086dc8d10b68cd13375360a-139x207.png 139w, /wp-content/uploads/2013/08/bfea3c4a6086dc8d10b68cd13375360a.png 431w" sizes="(max-width: 202px) 100vw, 202px" /></a></p>

<p>もう一つは、「自動吹き出し機能」。最新Webの目玉機能の一つ &#8220;Web Speech API&#8221; を使い自動認識した音声を、吹き出しとして表示する機能です。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/27f0501bca54ebef6f534cdc9efb4747.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/27f0501bca54ebef6f534cdc9efb4747-300x260.png" alt="スクリーンショット 2013-08-23 1.20.24" width="300" height="260" class="alignnone size-medium wp-image-1667" srcset="/wp-content/uploads/2013/08/27f0501bca54ebef6f534cdc9efb4747-300x260.png 300w, /wp-content/uploads/2013/08/27f0501bca54ebef6f534cdc9efb4747-1024x889.png 1024w, /wp-content/uploads/2013/08/27f0501bca54ebef6f534cdc9efb4747-207x179.png 207w, /wp-content/uploads/2013/08/27f0501bca54ebef6f534cdc9efb4747.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p>音声認識には誤認識がお約束ですが、その誤りっぷりが相まって、さらにエンターテイメント性を引き立ててくれています。なお、吹き出しの表示位置を調整するために、顔検出機能を利用しています。技術の無駄遣いっぷりは半端ではありません。</p>

<p><li>なお、自動吹き出し機能を利用する際には、<a href="https://app.html5experts.jp/manga/#face" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">#face</a>をハッシュ指定し、顔検出機能をONにする必要があります。</li></p>

<p>また、&#8221;WebSpeech API&#8221;による音声認識機能を応用し、音声コマンド機能も実装しました。例えば「バーン」と話すと、画面に集中線と「バーン」が表示されます。イベントの盛り上がりに欠かせない機能です。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/a5976323aff9cdc6754bc84635e3d0a8.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/a5976323aff9cdc6754bc84635e3d0a8-300x260.png" alt="スクリーンショット 2013-08-23 1.21.45" width="300" height="260" class="alignnone size-medium wp-image-1668" srcset="/wp-content/uploads/2013/08/a5976323aff9cdc6754bc84635e3d0a8-300x260.png 300w, /wp-content/uploads/2013/08/a5976323aff9cdc6754bc84635e3d0a8-1024x889.png 1024w, /wp-content/uploads/2013/08/a5976323aff9cdc6754bc84635e3d0a8-207x179.png 207w, /wp-content/uploads/2013/08/a5976323aff9cdc6754bc84635e3d0a8.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p>なお、開発メンバーは、筆者を含め HTML5Experts.jp 編集部メンバー4名で開発しました。</p>

<h1>マンガテレビ実装のポイント</h1>

<p>この「マンガテレビ」の実装のポイントを紹介します。本稿では、以下の4点について紹介します。</p>

<ul>
<li>WebRTC と HTML5 canvas による映像のリアルタイムマンガ化</li>
<li>WebSpeech API による音声認識</li>
<li>headtrackr.js による顔検出</li>
<li>WebSocket によるインタラクティブ機能</li>
</ul>

<h2>getUserMediaとHTML5 canvasによる映像のリアルタイムマンガ化</h2>

<p>最初に紹介するのは「マンガテレビ」の基本機能。リアルタイムで映像を「マンガ」にする機能の実装方法の紹介です。</p>

<p>ステップは以下のようになります。</p>

<ol>
<li>WebRTC の getUserMedia() により、カメラから映像を取得する。</li>
<li>HTML5 canvasのgetImageData() により、映像から画像データを取り出す。</li>
<li>画像データにフィルター処理を行い、マンガ化する。</li>
<li>requestAnimationFrame() により、画像変換処理を繰り返す。</li>
</ol>

<h3>WebRTC の getUserMedia() により、カメラから映像を取得する。</h3>

<p>まず、カメラから映像を取得します。これを行うためWebRTCのgetUserMedia()を使っています。(前述のとおり、Chromeを前提としているため、接頭辞 <abbr>webkit</abbr> を記述しています）</p>

<p><code lang="javascript">
$v_ = $("video");</p>

<p>// カメラからのストリーム映像を取得し、videoノードのsrc属性にURL指定する
navigator.webkitGetUserMedia({video: true, audio: false}, function(stream){
  var url = window.webkitURL.createObjectURL(stream);
  $v_[0].src = url;
  $v_[0].play();
});
</code></p>

<p>取得するのは映像のみのため、第一引数のaudioプロパティはfalseとしました。第二引数は取得成功時のコールバック関数で、カメラから取得した映像はインスタンスstreamに返されます。ここで、「マンガ化」の変換処理を行うためには、一旦streamをURLに変換し、videoノードにsrc属性として指定する必要があるため、 createObjectURL()を用いています。また、video ノードは、play()をコールしないと映像再生が開始しないため、このタイミングで呼んでいます。</p>

<h3>HTML5 canvasのgetImageData() により、映像から画像データを取り出す。</h3>

<p>マンガ化する画像変換処理を行うためには、映像から画像のピクセルデータを取得しなければなりません。このため一コマづつcanvasに画像として書きだし、getImageData()により、取得します。</p>

<p><code lang="javascript">
var canvas = $("canvas")[0]
  , ctx_ = canvas.getContext('2d');</p>

<p>// 映像の一コマを一旦canvasに画像として書き出す
ctx_.drawImage($("video")[0], 0, 0)</p>

<p>// canvasから画像のピクセルデータを取得する
var imgData = ctx_.getImageData(0, 0, 640, 480)
</code></p>

<h3>画像データにフィルター処理を行い、マンガ化する。</h3>

<p>取得した画像データに対してフィルター処理を行いマンガ化します。アルゴリズムは以下の通りです。</p>

<ol>
<li>画像を黒・スクリーントーン・白の3階調化する。</li>
</ol>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/e80a00d615240a9c282ed400de5dd322.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/e80a00d615240a9c282ed400de5dd322-300x260.png" alt="スクリーンショット 2013-08-23 1.27.21" width="300" height="260" class="alignnone size-medium wp-image-1670" srcset="/wp-content/uploads/2013/08/e80a00d615240a9c282ed400de5dd322-300x260.png 300w, /wp-content/uploads/2013/08/e80a00d615240a9c282ed400de5dd322-1024x889.png 1024w, /wp-content/uploads/2013/08/e80a00d615240a9c282ed400de5dd322-207x179.png 207w, /wp-content/uploads/2013/08/e80a00d615240a9c282ed400de5dd322.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<ol>
<li>エッジ抽出を行い、階調画像に縁取りを行う。</li>
</ol>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/3f19b7ae945f638e8725b9146ce4dc17.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/3f19b7ae945f638e8725b9146ce4dc17-300x260.png" alt="スクリーンショット 2013-08-23 1.26.57" width="300" height="260" class="alignnone size-medium wp-image-1672" srcset="/wp-content/uploads/2013/08/3f19b7ae945f638e8725b9146ce4dc17-300x260.png 300w, /wp-content/uploads/2013/08/3f19b7ae945f638e8725b9146ce4dc17-1024x889.png 1024w, /wp-content/uploads/2013/08/3f19b7ae945f638e8725b9146ce4dc17-207x179.png 207w, /wp-content/uploads/2013/08/3f19b7ae945f638e8725b9146ce4dc17.png 640w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p><i>（階調処理やエッジ抽出の具体やコードについては次回の記事で紹介します）</i></p>

<p>上記フィルター処理を行ったデータをcanvasに描画し、「マンガ化」された画像が表示されます。</p>

<p><code lang="javascript">
// 「マンガ化」フィルター処理後のデータを、canvasに書き出す。
ctx_.putImageData(toon, 0, 0)
</code></p>

<h3>requestAnimationFrame() により、画像変換処理を繰り返す。</h3>

<p>上記操作を16msec毎にループ処理することで、60fpsで「マンガ化」された映像が表示されます(パラパラ漫画の要領です)。このような処理を行う際、これまでだと</p>

<p><code lang="javascript">
// doToon()は、映像の一コマを画像変換する関数
setInterval(doToon, 16);
</code></p>

<p>と、setInterval()を用いるのが普通でした。しかしながら、このコードでは、doToon()の処理に16msec以上かかった場合、処理がスタックしていく問題が生じます。ここで、requestAnimationFrame() を用いると、doToon の処理時間に応じ、画像フィルター処理が繰り返し実行され、適切なフレームレートでマンガ化された映像が表示されます。</p>

<p><code lang="javascript">
requestAnimationFrame(doToon)
</code></p>

<h2>WebSpeech API による音声認識</h2>

<p>二番目に紹介するのは、音声認識機能です。認識した音声を吹き出しに自動表示したり、音声コマンドを使ったりするためにこの機能を利用しています。</p>

<p>音声認識を実現するために、Chrome で試験実装されている <a href="https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">WebSpeech API</a> を利用しました。ちなみに、このWebSpeech APIはW3Cで正式なドラフトも出ていない実験的なAPIです。音声認識が簡単にできてしまうなんて、ほんとブラウザの進化は留まるところを知りません。今後、本格的な仕様化や実装が進んでいくことでしょう。楽しみですね。</p>

<p>インスタンス生成後、start()メソッドをコールすると、音声認識が始まります。</p>

<p><code lang="javascript">
var rec = new webkitSpeechRecognition();</p>

<p>rec.continuous = true; // 音声認識を継続する
rec.interimResults = true; // 認識処理中の候補も返す
rec.lang = 'ja-JP'; // 認識モードを日本語にする</p>

<p>rec.start(); // 音声認識を開始
</code></p>

<p>音声が認識されると、&#8217;result&#8217; イベントが発生します。認識結果は、SpeechRecognitionEventオブジェクトとして返されます。</p>

<p><code lang="javascript">
rec.onresult = function(ev) {
  for(var i = ev.resultIndex, l = ev.results.length; i &lt; l; i++) {
    if(ev.results[i].isFinal) {
      // 音声認識が完了した
      var res = ev.results[i][0].transcript; // 音声認識結果
    } else {
      // 音声認識途中
      var res = ev.results[i][0].transcript; // 音声認識中の途中結果
    }
  }
}
</code></p>

<p>「マンガテレビ」では、認識結果の値をチェックし「バーン」や「ドーン」などの場合は、音声コマンドとして認識し、映像エフェクトを実施。それ以外の場合は、吹き出しに表示するといった処理を行っています。</p>

<p>なお、イベント中講演者が話し続けているようなケースだと、ずっと音声認識中となり、認識が完了しないケースが頻繁に発生します。これを避けるため</p>

<p><code lang="javascript">
// 認識結果の文字列長が15文字を超えたら、認識を再起動する。
if(res.length > 15) {
  rec.stop();
  rec.start();
}
</code></p>

<p>のように、認識結果が15文字を超えた時点で音声認識を再起動する処理を入れています。</p>

<p>なお、再起動の際に、サイトのプロトコルスキーマが<strong>httpsでない</strong>場合、毎回音声認識の許可を求めるダイアログが表示されてしまいます。注意してください。</p>

<h2>headtrackr.js による顔検出</h2>

<p>次に紹介するのは、顔検出機能です。音声を表示する吹き出しや、音声コマンドで集中線を出す際の位置を決めるために、この機能を利用しています。</p>

<p>JavaScript で顔検出を行うライブラリーとして <a href="https://github.com/wesbos/HTML5-Face-Detection" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">face.js</a> が有名ですが、検出スピードに難があるため、より高速に動作する <a href="https://github.com/auduno/headtrackr" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">headtrackr.js</a> を用いました。</p>

<p>headtrackr.jsは、最初に顔検出を行うまでは低速ですが、一旦検出が完了すると、それ以降は検出した顔の周辺のみを検出対象とするため、face.jsに比べ格段に高速動作します。もちろんハードウェアに依存しますが、筆者の Mac Book Pro では、概ね30fps(frame per second : 1 秒辺りのフレーム数）が得られました。ただし、検出できる顔は一つのみとなりますので、その点は注意してください。</p>

<p>Tracker オブジェクトのインスタンスを生成した後、検出対象のビデオ要素と、検出処理に用いるcanvas要素を指定し、start()メソッドを呼ぶと顔検出処理が始まります。</p>

<p><code lang="javascript">
var tracker = new headtrackr.Tracker({ui: false}); // uiをfalseにしないと、検出状態を示すテキストが表示される。
tracker.init($("video")[0], $("canvas")[0]); // 対象ビデオ要素と、検出処理用canvas要素を指定する。
tracker.start(); // 顔検出を開始する。
</code></p>

<p>顔が検出されると、&#8217;facetrackingEvent&#8217; イベントが発生します。認識結果は、コールバック関数にfacetrackingEventオブジェクトとして返されます。</p>

<p><code lang="javascript">
document.addEventListener("facetrackingEvent", function(ev){
  // ev.x, ev.y -> 顔の中心座標
  // ev.width, ev.height -> 顔の幅と高さ
}, false);
</code></p>

<p>これら顔の検出位置情報から、吹き出しの表示位置を決めています。</p>

<h2>WebSocket によるインタラクティブ機能</h2>

<p>最後に紹介するのは、インタラクティブ機能です。イベント参加者が「かっこいいねボタン」を連打すると、それがWebSocketで「マンガテレビ」に送られ、連打の数に応じて「ざわ・・」とか「ゴ・・・」などの文字が横断するエフェクトが動きます。参加者も一体になって盛り上がれるインタラクティブ機能です。この連打の数を「マンガテレビ」に送るために、HTML5の双方向通信機能WebSocketを使いました。なお、『マンガテレビ」では、socket.ioを用いているため、WebSocket が利用できない環境では自動的にHTTPポーリングにフォールバックします。</p>

<p>「マンガテレビ」では、socket.ioを更にラップし、メッセージ受信をsubscriberモデルとしたZapper オブジェクト(白石さん謹製)を用いています。</p>

<p>Zapperオブジェクトのインスタンスを生成し、connect()メソッドでサーバーへ接続。その後、&#8217;zap&#8217;メッセージに subscribe()すると、「かっこいいねボタン」の連打データを受信することができるようになります。受信データのcountプロパティに連打回数が入っているため、その値に応じて「ゴ・・・」や「ざわ・・」といったエフェクトをかけています。</p>

<p><code lang="javascript">
// 接続先サーバーを指定してインスタンスを生成
var zapper = new Zapper({
  serverUrl: 'http://somewhere/'
});</p>

<p>zapper.connect(function() {
  var event = zapper.event();</p>

<p>event.subscribe('zap', function(zap){
    // zap.count に連打回数が格納されている
  });
});
</code></p>

<h2>次回はパフォーマンスチューニング</h2>

<p>今回の記事では、筆者らが最新Web技術を駆使してイベント用に開発した「マンガテレビ」について、そのアーキテクチャを紹介しました。HTML5 の進化によりブラウザの世界観がさらに広がっていくことを感じて頂ければ幸いです。</p>

<p>さて、「マンガテレビ」では、本稿で紹介した各種機能以外に、もう一つ大切な点が。それは「高速動作する」ことです。映像のマンガ化処理に時間がかかると、「マンガテレビ」はカクカク動いてしまい、イベントが興冷めしてしまいますからね。次回は、「マンガ化処理」を高速に行うためのパフォーマンスチューニング・ポイントについて紹介します。</p>
]]></content:encoded>
			</item>
		<item>
		<title>WebSocket、WebRTC、ホームネットワーク、W3C標準化etc.通信エンジニアの習性は「何でもつなぐ」── 小松健作さん</title>
		<link>/komasshu/1134/</link>
		<pubDate>Mon, 19 Aug 2013 22:00:38 +0000</pubDate>
		<dc:creator><![CDATA[小松 健作]]></dc:creator>
				<category><![CDATA[最新動向]]></category>
		<category><![CDATA[W3C仕様]]></category>
		<category><![CDATA[WebSocket]]></category>
		<category><![CDATA[デバイス]]></category>
		<category><![CDATA[ネットワーク]]></category>
		<category><![CDATA[プロトコル]]></category>
		<category><![CDATA[ホームネットワーク]]></category>
		<category><![CDATA[標準化]]></category>

		<guid isPermaLink="false">/?p=1134</guid>
		<description><![CDATA[連載： エキスパートインタビュー (2)エキスパートインタビュー第二弾は、小松健作さん。NTTコミュニケーションズでHTML5、特にWebSocketやWebRTCなどの通信系の研究開発、W3Cの標準化活動に携わっていま...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/interview/" class="series-152" title="エキスパートインタビュー" data-wpel-link="internal">エキスパートインタビュー</a> (2)</div><p>エキスパートインタビュー第二弾は、小松健作さん。NTTコミュニケーションズでHTML5、特にWebSocketやWebRTCなどの通信系の研究開発、W3Cの標準化活動に携わっています。通信技術のエキスパートである小松さんに、これまでのキャリアと最近の活動を聞きました。</p>

<p><span id="more-1134"></span></p>

<h2>Webのオープンに自由につながる世界観が大好き</h2>

<h4>──最近、WebSocketやWebRTCなどのWeb通信系に加えて、ホームネットワーク系のDLNA（Digital Living Network Alliance）に関する研究開発もされているそうですね。</h4>

<div id="attachment_694" style="width: 209px" class="wp-caption alignright"><a href="https://html5experts.jp/wp-content/uploads/2013/08/IMG_7205.jpg" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/IMG_7205-200x300.jpg" alt="エキスパートNO.2　小松健作さん" width="200" height="300" class="alignright size-medium wp-image-1365" srcset="/wp-content/uploads/2013/08/IMG_7205-200x300.jpg 200w, /wp-content/uploads/2013/08/IMG_7205-682x1024.jpg 682w, /wp-content/uploads/2013/08/IMG_7205-137x207.jpg 137w, /wp-content/uploads/2013/08/IMG_7205.jpg 426w" sizes="(max-width: 200px) 100vw, 200px" /><p class="wp-caption-text">　エキスパートNO.2　小松健作さん</a></p></div>

<p>通信系のエンジニアって、何でもかんでもつなぐのが大好きなんです。通信系のAPIってことで、WebSocketをやってきたけど、それがホームネットワークにも広がってきたってかんじ。これまでのWebは自分の端末をクラウドのWebサーバと繋ぐパターンがメインで、ホームネットワークの端末同士を繋ぐ用途には殆ど使われてなかったんです。このような用途には、主にApple TVやビエラリンクに代表されるような、各ベンダーに特化した連携サービスが使われています。</p>

<p>それが最近のWebではブラウザからテレビを操作するというような、ベンダーにかかわらず端末同士をオープンに繋ぐことができるようになってきました。オープンで自由につながるっていうのがすごく大好き。HTML5の中でも、Canvasなどの描画系技術も基本知識としては押さえているつもりだけど、どっぷりつかるのはやっぱり通信系ですね。</p>

<h4>──なるほど。通信エンジニアの習性というか、好みを聞いたのは初めて。何でもつなぐのが大好きなんですね。僕的には、ホームネットワークとWebSocketって全然違う技術。なんで小松さんそこいったのかなと思ってたけど、「つなぐ」っていう共通点があった。</h4>

<p>なんでHTTPからWebSocketに飛び込んだかっていうと、HTTPって結構制約が多いじゃないですか。あくまでも文章をダウンロードするための用途っていうか。つなぐという意味では自由がない。WebSocketはその制約がなくなって、クラウドと自分のデバイスを自由につなげられる世界が作られる。そうなるとすごく楽しいなと。ホームネットワークについてもベンダーに特化した仕様ではなく、Webとオープンに自由につながる世界観ができれば自然に面白いことができるんじゃないかなって思ってます。</p>

<h2>磁気センサーからデータマイニングまで。幅広いキャリア</h2>

<h4>──小松さんって、キャリア的には通信エンジニアになるんですか？</h4>

<div id="attachment_695" style="width: 209px" class="wp-caption alignleft"><a href="https://html5experts.jp/wp-content/uploads/2013/08/IMG_7219.jpg" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/IMG_7219-200x300.jpg" alt="白石編集長" width="200" height="300" class="alignleft size-medium wp-image-1368" srcset="/wp-content/uploads/2013/08/IMG_7219-200x300.jpg 200w, /wp-content/uploads/2013/08/IMG_7219-682x1024.jpg 682w, /wp-content/uploads/2013/08/IMG_7219-137x207.jpg 137w, /wp-content/uploads/2013/08/IMG_7219.jpg 426w" sizes="(max-width: 200px) 100vw, 200px" /></a><p class="wp-caption-text">白石編集長</p></div>

<p>う～ん…自分の専門職が何なのか語るのは難しいなあ。大学の時は磁気センサーのデバイスの研究をしていました。会社に入ってからは映像配信システムやCDN（コンテンツデリバリネットワーク）など、どちらかというとインフラよりのネットワークエンジニアを10年くらいやってきました。</p>

<p>でも新しいことがやりたくて、データマイニングの研究を始めました。テキスト解析など今で言うビッグデータに走ってましたね。そんな中、HTML5に出会いWebSocketで一番最初に作ったのが<a href="http://wakachi.komasshu.info/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">wakachi</a>のデモ。当時社内の要望で、テキスト解析システムをHTTPインタフェースで作って欲しいと頼まれたんだけど、たった一つの文章でも下手をすると30秒ぐらいかかっちゃったり、これだと一億文書解析するのに100年かかっちゃう。それがWebSocketで処理すると、1時間か2時間くらいで分析できてしまう。WebSocketを使うと、簡単に業務課題を解決できると思って飛び込んだのが3～4年前です。</p>

<h4>──小松さんはいろんなことをされてますよね。初めてお会いしたときは、Webサービス系をやってなかったでしたっけ？</h4>

<p>Webサービス系もやってましたね。NTTの中でいろんな会社を渡り歩いていますが、新サービスを立ち上げる担当を任されることが多かったんです。NTT東にいた頃は、<a href="https://flets.com/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">フレッツ</a>とか、<a href="https://flets.com/hikaridenwa/" taget="_blank" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ひかり電話</a>とか…。Webに関しては、やれって言われたわけじゃなくて、すごいと思って個人的にやってたら仕事になったかんじですが（笑）。今はHTML5に関する事業を社内で立ち上げる役目です。通信系のWebSocketやWebRTC、ホームネットワークとか…ざっと挙げただけでも結構あります。</p>

<!-- -->

<h2>jQueryのように誰でも簡単に使いこなせる仕組み作り</h2>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/IMG_6996.jpg" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/IMG_6996-200x300.jpg" alt="小松健作さん" width="200" height="300" class="alignright size-medium wp-image-1372" srcset="/wp-content/uploads/2013/08/IMG_6996-200x300.jpg 200w, /wp-content/uploads/2013/08/IMG_6996-682x1024.jpg 682w, /wp-content/uploads/2013/08/IMG_6996-137x207.jpg 137w, /wp-content/uploads/2013/08/IMG_6996.jpg 426w" sizes="(max-width: 200px) 100vw, 200px" /></a></p>

<h4>──ホームネットワークでいろんなデバイスが共通のプロコトルでつながる話を聞きたいです。将来的にはあらゆる家電にWebサーバーが搭載されると、それを連携させる仕組みや標準化も必要になりますね。</h4>

<p>WebSocketやWebRTCをうまく活用して、リアルタイムコミュニケーションなどの双方向性サービスがもっと広がる形を作っていきたいと考えています。ただ、WebSocketやWebRTCを使うには新たにサーバーを立てなくてはいけないし、ネットワークの知識も必要だし、面倒なことが多いのも事実。そこを通信事業者としてサポートして、もっと気軽に使える環境を築いていきたい。例えばjQueryのように、誰でも簡単に使いこなせるようなもの。今のWebはjQueryが登場したからこそインタラクティブなものに発展しました。そういうチャレンジングな世界観を創りたいですね。</p>

<h4>──その小松さんのビジョンには、すごく共感します。言葉にしちゃうとSaaSとかPaaS、BaaSになっちゃうのかもしれないけど、それとは違う。アプリを簡単に作れるようにとか、双方向に通信する次のWebを加速させるといったことのために取り組むという姿勢に感銘を受けました。WebRTCやWebsocketが当たり前の世界でないなら、あって当たり前の世界に変えていけば、Webがさらに進化する。時代の到来を早めようとしている気がします。</h4>

<p>僕、性格的にせっかちなんです。そういう世界を先に見たくて（笑）。</p>

<h2>標準化の活動。日本のデベロッパーにも参加してほしい</h2>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/08/IMG_6979.jpg" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/08/IMG_6979-200x300.jpg" alt="白石編集長" width="200" height="300" class="alignleft size-medium wp-image-1375" srcset="/wp-content/uploads/2013/08/IMG_6979-200x300.jpg 200w, /wp-content/uploads/2013/08/IMG_6979-682x1024.jpg 682w, /wp-content/uploads/2013/08/IMG_6979-137x207.jpg 137w, /wp-content/uploads/2013/08/IMG_6979.jpg 426w" sizes="(max-width: 200px) 100vw, 200px" /></a></p>

<h4>──小松さんが主導で進められたW3Cの活動についても聞きたいです。日本人でWebの標準化について語れる人はまだあまりいません。小松さんはどんなマインドで臨まれているのでしょうか。</h4>

<p>世界から見ても日本のデベロッパーはとても高いポテンシャルを持っているのですが、これまでW3Cみたいな国際標準化団体を遠い存在に感じていたと思います。きっかけ、やり方のヒントがわかればそこに踏み出していけるはず。そう考えて、もっと世界に飛び立つための仕組みやベースを作りたかったんです。</p>

<h4>──日本のWeb開発者とW3Cメンバーとの交流イベント「<a href="http://www.atmarkit.co.jp/ait/articles/1306/19/news010.html" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">W3C Developer Meetup</a>」をやってみてどうでしたか？</h4>

<p>日本の開発者は、これまでW3Cと接触する機会が殆どなかったわけで、それだけでも意義が大きかったと思っています。みんな楽しんでくれていたことに一番の達成感を感じました。今後はこのきっかけを継続する仕組みを作らないといけない。コードは世界共通言語なわけですし、英語が得意でなくても標準化に貢献できるような形も作っていきたいですね。</p>

<p>いやらしいことを考えずにつぶらな眼（まなこ）で貢献をしていけば、何か成果が生まれてくるし、日本全体がよくなってくる。今までの自分自身の活動もそうですね。やらないより、やったほうが絶対よかった。「それって儲かるんですか？」ってよく聞かれるんだけど、金銭などのリターンを常に前提においてやっていたら今の自分はなかったと思ってます。</p>

<h2>エキスパートメンバーで有機的なコラボレーションを</h2>

<h4>──最後に、今後の活動や「HTML5 Experts.jp」に対する抱負をぜひ！</h4>

<p>エキスパートの人たちと意見交換や何かを作り上げたい。有機的なコラボレーションができたらいいですね。
執筆陣やレビュワー以上のつながりを作り出し、エキスパート同士のコラボでどんどん新たなWebの世界を引き拓いていきたいです！</p>

<p><style><!--
.exp-comment {   border-top: 1px solid lightgray; } .exp-thumbnail { } .exp-info {   font-size: 1.1em !important;   margin-bottom: 0 !important; } .exp-comment-pubdate {   font-size: .8em !important;   margin-bottom: 4px !important; } .exp-comment-main {   border: 1px solid lightgray;   border-radius: 8px;   padding: 1em;   min-height: 70px;   margin-bottom: 16px; } .exp-thumbnail {   width: 70px;   float: left; } .exp-comment-body {   margin-left: 80px; } .editor-comment {   clear: left; } .exp-question {   font-weight: bold;   margin-bottom: 8px; }
--></style><article class="exp-comment"></p>

<div class="exp-comment-main">
<div class="exp-thumbnail"><a href="https://html5experts.jp/wp-content/uploads/2013/08/IMG_69792.jpg" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img class="alignleft size-thumbnail wp-image-784" alt="Screenshot_4" src="/wp-content/uploads/2013/08/IMG_69792.jpg" width="66" height="66" /></a></div>
<blockquote class="exp-comment-body">[エキスパート No.2 <a href="https://html5experts.jp/author/komasshu/" data-wpel-link="internal">小松 健作</a>]

1972年生まれ。NTTコミュニケーションズにてネットワークとWebとの関わりや、インタラクティブマルチデバイスWebサービスに関する研究開発・標準化活動に従事。Google API Expert（HTML5）、Microsoft Most Valuable Professional（Internet Explorer）</br>
<a href="http://blog.livedoor.jp/kotesaki/" rel="external nofollow noopener noreferrer" title="" class="ext-link" target="_blank" data-wpel-link="external">こてさきAjax &#8211; livedoor Blog（ブログ）</a></blockquote>
</div>

<p></article></p>
]]></content:encoded>
		
		<series:name><![CDATA[エキスパートインタビュー]]></series:name>
	</item>
	</channel>
</rss>
