[JavaScript/ECMAScript2017]async/awaitとPromiseでjavascript直列/並列処理

potproject.net Advent Calendar 201610日目の記事です。

祝!10日連続更新!


ES8にあたるECMAScript2017では、await/asyncが実装されると聞いて。黙っちゃいられねえ。

まだES2017は策定中ですが、なんかEdgeでも動くようになったらしいし(設定は必要)、多分ほぼ確定。内定状態でしょう。

https://tc39.github.io/ecma262/

しかし、やはり標準では動かないので、このコードはbabelでトランスパイルしてnode.jsで動かしてます。ご了承ください。

await/async修飾子は、元々C#で使われていた非同期処理を同期的に記述できる仕組みです。

一時期C#を使っていてユニバーサル Windows プラットフォーム (UWP) アプリを作っていた時があり、

制約がありすぎて普通にWindows formsで作りてえと思いつつその時に使用しておりました。

基本的に使い方はC#もjavascriptも同じです。しかし、当然javascriptにはTask型なんてありませんので、Promiseがそれの代用になります。

つまりは、await/asyncを使うにはPromiseから習得する必要があります。

なので、ここではPromiseに関しても軽く記載しておきます。

Promiseに関しては、非同期の処理を上手く同期的に見せたり、並列処理が出来るメソッドです。

何が違うかというと、書き方が違います。await/asyncは非同期処理を同期的に行うため、結局直列の処理になりますが、Promiseは並列処理が可能です。

今回書くコードは、Promise.allでの並列処理とawait/asyncでの直列処理の比較です。

プログラム的には、1から指定された数まで全て足して出力するだけの処理です。

arr.reduceを使った非同期処理方法とfor文を使った同期処理方法で、それぞれ処理をして表示します。

一応速度も出力します。

//繰り返し回数
const execute_count=10000000;
(async ()=>{
  //await/asyncでの直列処理
  var startTime = new Date();
  var sumtest = await reduce_sum(execute_count);
  var normalsumtest=normal_sum(execute_count);
  var endTime = new Date();
  if(sumtest===normalsumtest){
    console.log("(await/async):" + (endTime - startTime) + "ms ans:"+sumtest);
  }

  //Promise.allでの並列処理
  var startTime2 = new Date();
  var sumexecute=await sum(execute_count);
  var endTime2 = new Date();
  console.log("(Promise.all):"+(endTime2 - startTime2)+ "ms ans:"+sumexecute);
})();

async function sum(num){
  return new Promise((resolve,reject)=>{
    Promise.all([normal_sum(num), reduce_sum(num)])
    .then(function(sum_result) { 
      if(sum_result[0]===sum_result[1]){
        resolve(sum_result[0]);
      }else{
        reject();
      }
    });

  });

}

function normal_sum(num){
  var arr=[];
  for(var i=1;i<=num;i++){
      arr.push(i);
  }
  var sum = 0;
  for(var x=0;x<arr.length;x++){
    sum+=arr[x];
  }
  return sum;

}

async function reduce_sum(sum){
    return new Promise((resolve,reject)=>{
      var arr=[];
      for(var i=1;i<=sum;i++){
        arr.push(i);
      }
      return resolve(arr.reduce(function(prev, current) {
        return prev+current;
      }));
    });
}

結果はこんな感じ。

(await/async):690ms ans:50000005000000

(Promise.all):786ms ans:50000005000000

並列の方が遅い。ほぼ変わらずです。なんで?って感じですが、多分これが正しいと思います。

javascriptはご存知の通りシングルスレッドなので、並列処理だからってスレッドを増やしたりできません。

シングルスレッドということは同時に別の処理をやることはできないので、だから結局のところ疑似並列処理なだけです。

むしろ並列にしたほうが割り込みが入るので、遅くなると思います。

実際トランスパイラしてるというのもあるし、await/async/Promiseの処理時間とかもあるので上の時間は多分あてになりません。

一番の問題は、コードの見やすさです。

await/asyncを使えばメインのコードにコールバックを一切入れることが無くなるため、見やすいんじゃないかなと思います。

自分は積極的にawait/asyncは使っていきたいです。もうコールバック地獄には入りたくない・・・。

[node.js]LINE BOT API Trialで自作のchatbotを動かす

4月ごろから、LINE BOT API Trial版が一般向けに公開されました。
元々先着1万名だったのですがそれは撤廃されて、GWが始まったくらいに再開され誰でも使えるように。
友達50人に制限されていますが、お試し版ということで普段使っているLINEでbotを作れるのは面白いということで注目度は高いですね。

「LINE BOT API Trial Account」の募集再開–ID検索にも対応 http://japan.cnet.com/news/service/35081826/

ここで、思い出してみますと、
某所で作ったchatterbot「ゆうき」ちゃんを流用することで、LINEのりんねみたいにLINEで話せるじゃあないですか。
GWだけどぼっちでやることなかったので、ちょうどいいです。
これで何時間でも飽きるまでLINEできます!やったリア充だ!

まずはアカウント取得。当然ながらLINEのアカウントが必要です。
(どうでもいいけど、LINEとNaverのアカウントって別なんだよね
ここまではすんなり行けると思います。誰でも。後は、botアカウントを作って、QRコードから友達になれるかまず確認。
これで数少ないLINEの友達が増えました。しかし、当然ながらこのままだと話しても何も返ってこないただのカカシ状態。

yuuki

ここで、LINE BOT APIの構造をかなり簡単に説明。

  • BOTに話しかけると、BOT管理者が設定したcallback URLに話しかけた情報がPOSTされます。
  • <li>その情報をもとに、自分のサーバで処理を行い、LINEのAPIサーバにPOSTを送ることで、BOTが返答します。</li>

    こんな感じ、簡素ですが、その分かなり簡単です。
    なお、署名キーも送られてくるため検証も可能です。
    問題点として、httpsオンリーという制約がちょっとだけ重たいですが、let’s encryptやStart SSLも正式に対応したのでお金もかからず簡単に作れると思います。

    いつものように、処理サーバはnode.js(javascript)で作ってます。ざっと見た感じ、node.jsで動かしてる記事は無かったのでちょうどいいですね。
    node.js(Expressフレームワーク)環境です。chatbotのソースは省きます。検証も省いてます。
    外部モジュールとしてfetch APIのようなものであるsuperagent使ってます。

    var request = require('superagent');
    var express = require('express');
    var app = express();
    //expressの設定等は省きます このままだと多分動きません
    app.post('/line/callback', function (req, res) {
        if (req != undefined && req.body != undefined) {
            var linecontent = req.body.result[0].content;
            if (linecontent) {
                //テキストメッセージのみ対応してます
                if (linecontent.contentType == 1) {
                    var linetext = linecontent.text; //テキストデータ
                    var linefrom = linecontent.from; // 送信ユーザのMID
                    var token = "token";
                    //chatbotの処理
                    api.gettext(linetext, token, function (restext) {
                        //callbackのrestextがchatbotの返答
                        //LINE BOT APIサーバにPOST
                        var sendmessage = {};
                        sendmessage["to"] = [linefrom];
                        sendmessage["toChannel"] = 1383378250; //固定値
                        sendmessage["eventType"] = "138311608800106203"; //固定値
                        sendmessage["content"] = {};
                        sendmessage["content"]["contentType"] = 1;
                        sendmessage["content"]["toType"] = 1;
                        sendmessage["content"]["text"] = restext;
                        //console.dir(sendmessage);
                        request.post("https://trialbot-api.line.me/v1/events")
                            .set('Content-Type', 'application/json; charset=UTF-8')
                            .set('X-Line-ChannelID', [APIのChannelID])
                            .set('X-Line-ChannelSecret', [APIのChannel Secret])
                            .set('X-Line-Trusted-User-With-ACL', [APIのMID])
                            .send(sendmessage)
                            .end(function (res) {
                            });
                    });
                } else {
                    //非対応!
                }
            }
        }
        res.send("res");
    
    });
    
    

    動かしてみるとこんな感じ。いやーやっぱりこういうのはなんか作ってると愛着湧いてきますね。

    chatbotに関して、自作と書いてますが、別に全部作ったものではなく、歴史あるchatterbotであるreudy1.9©Gimite,Glass_sagaを改変したものとなってます。
    まだ機械学習という言葉無い時代のものですが、よくできてると思います。

    その他ハマった点。
    多分レアケースなはまり方。というかプロキシ立ち上げてる人くらいしか躓かないと思うが、自分の環境でのミス。
    1.header
    headerはHOST:https://domain.com:443/callbackといった形になります。自分はheaderをHOST:https://domain.com/callbackに振り分けていたため、動作せず数分悩みました
    headerで振り分けなんかをしている人は要注意です。

    2.Let’s encryptを使う場合、中間CA証明書が必要
    CA Listに記載されているルートCA証明書を含んだ公開鍵証明書が使えるため、Let’s encryptの中間CA証明書が必要になります。
    なのでプロキシ環境では公開鍵としてfullchain.pemを使う必要があります。
    Let’s encryptじゃなくても中間CA証明書が無いと多分動作しません。アクセスログにも表示されないので注意。

    元々Let’s encryptは使えなかったようですが、今回は更新したてのほやほやでしたが使えました(ハマった点を除いて)。Let’s encrypt Autority X3なら問題ないかと。

    [Javascript]CryptoJSで文字や画像をAES暗号化したりする

    ネタが無いので、ありきたりだけどちょっと前にやってたことをブログに書こう。

    CryptoJSはクライアントサイド(つまりはブラウザ上)でAESなんかを使った暗号化が可能なJavascriptライブラリです。
    SHA-1などのハッシュ関数もあり、クライアント上でお手軽暗号化が実装できます。
    使うのか知らないですがRIPEMDとかSHA-3まで対応してます。

    文字列のAES暗号化・複合

    SHA-2でパスフレーズをハッシュ化し、文字列をbase64エンコードしAES暗号化します。
    ここまでやればまずパスフレーズがわからないなら暗号が破られることはないでしょう。

    こんな感じのhtmlを用意。html部分は省略します。
    cryptjs
    上がパスフレーズ、下が暗号化する文字列です。
    めんどいのでjquery使ってます。

    window.onload = function () {
        $("#set").on("click", function () {
            var text = $("#input").val();
            var str = CryptoJS.SHA256(text);
            var hashbase64 = str.toString(CryptoJS.enc.Base64);
            //AES
            var Message = $("#text").val();
            encrypt = CryptoJS.AES.encrypt(Message, hashbase64);
            console.log("aes encrypt:");
            console.log(encrypt.toString());
            decrypt = CryptoJS.AES.decrypt(encrypt, hashbase64);
            console.log("aes decrypt:");
            console.log(decrypt.toString(CryptoJS.enc.Utf8));
        });
    
    };
    

    cryptjs2
    そのままbase64で出力できるのが便利です。
    ※base64で出力するには、enc-base64-min.jsが必要です。

    画像のAES暗号化・複合

    画像も同じように、DataURIスキーマに変換すれば使えます。
    アップロードした画像なんかを暗号化させたいので、
    HTML5 File APIでまず画像を読み込んで処理します。

    window.onload = function () {
        $("#imgset").on("change", function () {
            var filesrc = $(this).prop('files')[0];
    
            if (!filesrc) {
                console.error("No File Error!");
                return;
            } // ファイル未選択
    
            if (filesrc.size >= 307200) {
                console.error("File Size Over Error!");
                return;
            } // サイズ制限300KBまで
    
            console.log(filesrc);
    
            var fr = new FileReader();
            fr.onload = function () {
                src = fr.result; // 読み込んだ画像データをsrcにセット
                $('#preview_field').attr('src', src);
            }
            fr.readAsDataURL(filesrc); // 画像読み込み
        });
    
        $("#imgupload").on("click", function () {
            var text = $("#input").val();
            var str = CryptoJS.SHA256(text);
            var hashbase64 = str.toString(CryptoJS.enc.Base64);
            //AES
            var Message = $("#preview_field").attr('src');
            encrypt = CryptoJS.AES.encrypt(Message, hashbase64);
            console.log("aes encrypt:");
            console.log(encrypt.toString());
            decrypt = CryptoJS.AES.decrypt(encrypt, hashbase64);
            console.log("aes decrypt:");
            console.log(decrypt.toString(CryptoJS.enc.Utf8));
        });
    
    };
    
    

    cryptjs3
    複合したDataURIスキーマをクリックするとちゃんと画像が表示され、問題なく複合できていることがわかります。
    しかし画像はそもそも文字と比べると容量がケタ違いですので、300KB程度では一瞬ですが、
    数MBの画像を処理したりすると時間掛かるかもしれないです。
    これをそのまま受け渡したりサーバに投げて処理できれば、暗号化した通信がhttps無しで実装できますね!まあhttps使えばいいじゃんって話ですが。

    Monaca+node.js+socket.ioでWebsocketアプリを作るてすと

    最近はjavaばっかり使ってる気がしますが、
    この手の記事は、ググってもあまり出ていないので書き留めておくことにしました。

    つまりはPhonegap(cordova)なハイブリットアプリとnode.js鯖、socket.ioでWebsocketなアプリを簡単に作ろうということです。
    Phonegap、大体はスタンドアローン(単体)で動くアプリの作り方や、ajaxを利用した通信なんかがほとんどなので。

    そして、今回は構築環境が簡単で簡単にビルドもできるクラウド環境で動く
    cordova開発プラットフォームMonacaを使います。
    サーバに関してはこれもクラウド環境で。開発環境構築までやってくれるサーバサイド開発プラットフォームCloud9使います。
    サーバサイドプログラミングもクライアントサイドプログラミングも全部クラウド任せ。
    今の時代開発は何から何まで全部クラウドで出来るんだよ。ローカルなんていらないんだよ?っていう目的の記事でもあります。

    とりあえず、そもそもCordovaフレームワークでsocket.ioって使えるのかなと思っていたのですが、公式にすごくわかりやすい例がありました。
    Socket.IO with Apache Cordova

    これを見ると、クライアント側のsocket.io.jsは公式が用意されているCDNを使えばcordovaでも問題なく動くらしいですね。
    <script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>;

    これで、ちゃんと動いてくれるのか試してみよう。
    サーバ側のコード。socket.ioさえ使えればいいのでhttpサーバを立てる必要はありません。
    process.env.PORTは、Cloud9で自動的にポートを振り分けてくれます。

    var io = require('socket.io')(process.env.PORT);
    
    io.sockets.on('connection', function (socket) {
        console.log('socket connected');
    
        socket.on('disconnect', function () {
            console.log('socket disconnected');
        });
    
        socket.emit('text', 'socket.io connect!');
    });

    クライアント側(monaca)に書くコード。省略してjsファイルだけです。

    (function () { //即時関数
        //接続先の指定
        var url = "test-potproject.c9.io"; //websocketサーバのURL。
    
        //接続
        var socket = io.connect(url);
        socket.on('connect', function() {
            socket.on('text',function(text){
                alert(text);
            });
         });
    }());

    接続できればアラートが表示されるだけの簡単なアプリです。PCのChromeではちゃんと動作しました。
    しかし、本元はアプリなんで、AndroidやiOSで動くのか。

    で、何故だが動かない。
    WebViewだから動くと思ったのに。試した端末としてはAndroid4.4です。対応してないってことはなさそうなんだけど・・・

    で、試行錯誤したところ。WebsocketURLをws://test-potproject.c9.ioと記述しないと動きませんでした。ブラウザの仕様の問題か・・・
    Web技術はやりやすくてとてもいいのだが、ブラウザが違うと動かなくなったりするのでこのあたりにぶち当たると痛い。

    Android4.4 Nexus7で試したところですが、すぐに動きました。ほかのブログには接続に5秒とか待たされるとか聞きましたが、問題なく。
    この部分もブラウザと端末で差がかなりありそうです。というか多分古いAndroidなんかでは動かないでしょうし・・・。
    ネイティブJavaで記述されたsocket.ioのcordova pluginもありますが、古いのでこれはこれで最新版のsocket.ioで動くのか気になるところです。


    ここから余談。

    最近知りましたが、
    プロトコルを省略して//blog.potproject.net/という風に書くこともできるらしいですね。
    これも相対URLの一つだとか。ちゃんとしたものでクソ古いブラウザ以外は動作するらしいです。

    つまり、画像を表示するには
    <img src="//blog.potproject.net/wp-content/uploads/2015/02/aapicture.png" />
    と書けばよろしいと。
    表示させてみる。

    後にhttps対応させるとかならこういう方法もあるのね。と。

    node.jsなんかでオンラインなゲームを作る(5) ルーム機能(socket.io)

    Websocketを利用して、特定の処理や通知などをそのルーム、またはその人にだけ行う。

    WebSocket – Wikipedia
    Websocketは双方向通信のプロトコルです。html5と対応ブラウザなら動作します。
    双方向通信なので、つまりは、サーバ側からクライアントにプッシュ通知、クライアントからサーバ側にデータ送信、といったことが可能になります。
    connectionが成立した後は後ろでプロトコルが動いてくれますので、動的なWebサイトとして、しかもサーバとのやり取りをずっと行えます。
    この技術を応用すれば、オンラインゲームに必須なチャット機能的なこと、ダメージ処理とかそういうの、が全部ブラウザ上で動くってことですね。
    Ajax(XMLHttpRequest)なんかだとクライアントから問い合わせは出来てもサーバから受け取ることは難しいので、Ajaxの上位互換みたいな感じらしいです。
    socket.ioは、Websocketを使うためのnode.jsモジュールです。

    まずは、初期設定的な何か。
    サーバ(app.js)
    socketio = require('socket.io')
    var server = http.createServer(app);
    var io = socketio.listen(server);
    これで、websocketプロトコルをlistenします。一応独自プロトコルなのですがTCP 80/443で動作しますので、そのままhttp(TCP 80)に乗っける形になるのでこう書きます。

    クライアント(main.js)
    var socket = io.connect();
    クライアントは、事前にhtmlから/socket.io/socket.io.jsをロードして使用します。
    (socket.ioサーバーを立てた段階で、自動的にサイトからロードできるようになります)
    前回作成したejsに書いておきます。
    そうすれば、この一行を書くだけで使えるようになります。
    socket.ioは、socket.emit(送信する値)で送信、socket.onで受け取りの処理(callback)大体この二つを使います。

    socket.ioでのROOM処理は、この辺を参照。
    既にjoinメゾットというのが準備されていて、同じ部屋にjoinしたクライアントだけ、emit(送る)ことが出来ます。
    参考:Socket.IO — Rooms and Namespaces
    Node.js – Socket.IO覚え書き(Room、joinまわり) – Qiita

    順を追って書いていくと、
    クライアントが接続するので、その接続をサーバ側が受け取り、サーバ側はRoomIDにしたがってjoinメゾットで部屋を振り分けます。
    その後、to(room).emitメゾットでその部屋のみ送ります。

    クライアント(main.js)

    socket.on('connected', function() {
                //部屋番号送信・初期表示
                socket.emit('init', {'room': RoomID});
            });
            //文章受け取り
            socket.on("logmessage", function (data) {
                logpush(data); //dataを文章を表示する関数
            });
    

    s.on(‘connected’,[callback])はWebsocket接続完了時に自動的に呼び出されます。それに、RoomIDをinitとしてサーバに送信。

    サーバ(app.js)

    var chat = io.sockets.on('connection', function(socket) {
        socket.emit('connected');
        console.log(socket.id+" connected");
        //room別に管理
        socket.on("init",function(req){
            //join(room)で部屋を作る
            socket.setRoominfo=req.room;
            socket.join(req.room); //roomIDごとに登録
            socket.join(socket.id); //個別に識別するためにクライアント識別子であるsocketidを登録
    
            console.log(req.room+" join");
    
        });
        socket.on('msg post', function(msg) { //Room内にメッセージ送信
            chat.to(socket.setRoominfo).emit("logmessage", msg);
        });
    });

    簡単なチャットとしての機能が出来ました。
    socket.setRoominfoに、一時的なデータを格納するためRoomIDを入れています。
    これで、各クライアントが’msg post’と文字列をemitすると、同じルームに入っているクライアント全てに表示されます。
    その後、Disconnection(ブラウザから離れた時)の処理、gameをstartした時の処理、などでログを表示するようにします。
    game
    まあ、ゲームの道のりは遠い。次はmongoDBです。


    githubにソースがあります。製作中。ソースは自由に。

    前:node.jsなんかでオンラインなゲームを作る(4) ルーム機能

    [node.js]HTTPS通信に対応させてみる(SSL-リバースプロキシ環境を構築する)

    タイトルの通り。httpsのこともこれから勉強していかなければならないのかなーと思い。

    きっかけはこの記事です。

    『証明書を無料で発行、HTTPSの導入を支援する「Let’s Encrypt」』は何に使え、何に使えないのか – いろいろやってみるにっき

    HTTPS普及を推進するプロジェクト「Let’s Encrypt」、Linux Foundation傘下に

    これを見ながら10年後くらいはWeb全てがhttpsになってんじゃねーかなと思えてきて。

    無料なら個人でも気兼ねなく使えますもんね。商用には辞めたほうがいい感じですけれど・・・

     

    当然、証明書は所謂オレオレ証明書です。

    ・・・オレオレ証明書って言い方はどうなんだろうとちょっと思います。別に偽サイトじゃないのにニュアンスからそう思わせてしまうじゃないかと。

    ソースコード。

    var https = require('https');
    var fs = require('fs');
    
    var ssloptions={key: fs.readFileSync('server.key', 'utf8'),
        cert: fs.readFileSync('server.crt', 'utf8')};
    
    var serverhttps = https.createServer(ssloptions,function ( req, res ) {
            res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('SSL Connection');
    });
    
    serverhttps.listen(443);

    URL:https://potproject.net/

    ※オレオレ証明書なので当然https通信に関する警告が各ブラウザで出ます。

    node.jsには標準モジュールとして既にhttpsが存在します。これでencrypt関連のことは全く知らなくても使えます。

    なので、最小限のコードであれば通常のhttp通信と違うところは証明書の登録をしなくてはならないという点だけです。

    証明書(秘密鍵・公開鍵)に関してはopensslで生成。ここに公的機関が発行した証明書を入れ替えればすぐに使えます。

    これで通信は暗号化されたわけですが、最近はSSL絡みの脆弱性がいっぱい出てきてますし、規格に関してはまだまだ変わっていくのでしょうかねー・・・

    このブログもhttpsに対応させよう

    せっかくなのでブログも対応させよう。

    httpsに対応することで、ログイン時に盗聴されて乗っ取られることはなくなると思います。そもそもまずそんなことは起きない・・・というツッコミはさておき。
    鯖自体、リバースプロキシサーバでサブドメインごとに振り分けているため、設定は結構複雑になってきます。

    こんな感じで鯖の構造も変更しました。
    http-node-proxy(リバースプロキシサーバ | port 80/443[外部ポート番号])
    —potproject.net(Node.js | port 8080(http/https)
    —blog.potproject.net(Apache | port 8081(http)/8443(https))
    別途、ApacheにSSLの設定をしなくてはならないです。

    と思ったんですが、リバースプロキシからの通信は暗号化する必要ないので(同じ鯖で内部ルーティングしているため)、
    ネットワークからリバースプロキシまでをSSLで暗号化、
    リバースプロキシからapacheの受付内部ポートまでは暗号化せずhttpで渡すようにしました。
    なので、バーチャルホスト設定で8443ポートを内部ルーティングできるようにして、ApacheのSSL設定は停止しました。
    Apache ssl.conf設定。ssl.confだけどSSL切ってます

    Listen 8443 https
    
    <VirtualHost _default_:8443>
    
    # General setup for the virtual host, inherited from global configuration
    DocumentRoot "/var/www/wordpress"
    ServerName blog.potproject.net
    SetEnv HTTPS on
    
    #   SSL Engine Switch:
    #   Enable/Disable SSL for this virtual host.
    SSLEngine off
    
    ---中略---
    
    </VirtualHost>

    node.jsプロキシサーバの設定。

    var https = require('https');
    var fs = require('fs');
    
    var httpsoptions={target:'http://127.0.0.1:8443',secure:false};
    
    var ssloptions={key: fs.readFileSync('server.key', 'utf8'),
        cert: fs.readFileSync('server.crt', 'utf8')};
    
    var serverhttps = https.createServer(ssloptions,function ( req, res ) {
            if (req.headers.host == 'potproject.net') {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('SSL Connection');
        }else if (req.headers.host == 'blog.potproject.net') {
            Proxy.web( req, res ,httpsoptions);
    
        }
    
    });
    serverhttps.listen(443);

    こんな形に変更しました。

    options[secure]はhttps -> httpsでプロキシをする時にtrueにします。今回はhttps -> httpなのでfalseです。

    そしてhttpsでつながった・・・のはいいのですが、デザインが崩れて明らかにCSSやJSが読み込めていない。
    調べたところ、こういう問題があるようです。
    WordPress をSSL運用すると管理画面でアップロードした画像が見れない
    こんな問題があるらしい。ってことはまだ治っていないのだろうか?それともテーマの問題か?
    ということでSetEnv HTTPS onをssl.confに追加し、コードを組むことででちゃんと表示されるようになりました。
    その後、WordPress HTTPS(SSL)プラグインを導入し、管理者ログインと画面をhttps通信化。
    ちゃんと管理者画面だけhttpsにリダイレクトしてくれますのでいいですね。

    https://blog.potproject.net/
    ※オレオレ証明書なので当然https通信に関する警告が(以下略
    これでこのブログもhttpsの仲間入りですね。オレオレ証明書だしまあ多分ほとんど意味ない気がしますが・・・
    Let’s Encryptの無料証明書がきたらぜひ使ってみようと思います。まあその時までこのままで。
    無料のSSLならStartSSLなんか使えるという声もありますが登録めんどそうなので・・・

    node.jsなんかでオンラインなゲームを作る(4) ルーム機能

    オンラインゲームには不可欠な存在だと思える、ルーム機能を実装していきます。
    内容は、そのままルーム機能です。チャットで個別の部屋みたいなのを実装するためのもの。
    同じ部屋同士で対戦できます。逆に同じ部屋に入っていないと対戦できません。これがちゃんと機能しないとえらいこっちゃです。

    この機能には、express,ejs,socketを利用します。
    ということで、requireで指定します。おまじないという奴です。
    var express = require('express')
    , http = require('http')//標準モジュール http
    , path = require('path') //標準モジュール path
    , fs = require('fs') //標準モジュール filesystem
    , socketio = require('socket.io')
    , mongoose = require('mongoose')
    , ejs=require('ejs');

    ルーム機能の実装には、socket.ioが不可欠です。ほぼこいつに頼りきり。
    流れとしては、

    1. form.htmlからPOSTでルーム番号を送る
    2. ルーム番号を受け取り、ejsで表示、その部屋に入る(index.ejsへ遷移)
    3. emitで通知する場合、そのルーム内だけ通知するようになる(socket.io)

    これが一連の流れとなります。
    3はsocket.ioを含む処理のため、次の記事で。1,2を実装していきましょう。

    ejsについて
    ejsはテンプレートエンジンというもので、htmlに変数を流しいれるために使うファイルみたいなものです。VIEWに当たるもの。
    書き方としては、PHPやJSPと似てます。htmlにejsタグを付けて、変数を入れていきます。
    ついでに、デフォルトでエスケープ処理をしてくれたりもします。
    [index.ejs]

    <%=room_id %>に送った変数[room_id]が挿入されます。JSPと書き方ほぼ一緒だし見分けがつかない
    これで、POSTで送った値を取得したら、ejsで流し込み、タイトルに表示・javascriptの値として登録します。

    <html>
      <head>
        <meta charset="utf-8">
        <title>RoomID:<%=room_id %></title>
        <script type="text/javascript">
        var RoomID = "<%=room_id %>";
        </script>
      </head>
      <body>
      </body>
    </html>

    [form.html]
    送信する方のPOSTはこんな感じです。特に何も考えずシンプルに実装。こっちはhtmlです。

    <!DOCTYPE html>
      <head>
        <meta charset="utf-8">
        <title>title</title>
      </head>
      <body>
      <form action="./" method="post">
        <p>部屋ID:<input type="text" name="room" size="10"></p>
          <p>
        <input type="submit" value="入室"><input type="reset" value="リセット">
          </p>
    </form>
      </body>
    </html>
    

    [app.js]
    app.jsはサーバーサイドのjavascriptとして。サーバーの処理を記述します。
    httpサーバのlisten、expressの初期設定などは省略します。
    POSTで送った値はreq.bodyに格納されているということが割と重要です。覚えていないと変なところで躓くかも。

    //入室
    app.get('/', function(req,res){ //アクセスされたら
          fs.readFile('form.html',function(err, data){ //form.html読み込んで送る
            if (err) {
                console.error("console:"+err);
                res.send(404); // エラーで404を返す
                return;
            }
            res.set('Content-Type', 'text/html');
            res.send(data);
          });
    });
    //index.htmlのGET
    app.post('/', function(req,res){ //postで送られて来たら
            if(req.body.room==null){
                res.send(500); // 値なしならエラーで500を返す
                return;
            }
            res.render('index.ejs',{room_id:req.body.room}); //ejsをレンダリングして送る
            });
    
    });

    次はsocket.ioについて理解していかないと・・・。


    githubにソースがあります。製作中。ソースは自由に。

    前:node.jsなんかでオンラインなゲームを作る(3) express設定
    次:node.jsなんかでオンラインなゲームを作る(5) ルーム機能(socket.io)