ホーム < ゲームつくろー! < IKD備忘録

サーバ
クライアントからサーバに要求を出してみる


 ここまでで、LAMP構成のゲームサーバは立てました。ローカルPCのブラウザから特定のURL(PHPファイルへのアドレス)を打ち込んでWebサーバ(apache)へ送信、apacheが解釈してPHPを動かし、ついでにMySQLから情報を拾って、HTMLを動的に作ってローカルPCに送信するのも出来ました。しかし、現状で出来ているのはクライアントの立場で言えば「新しいページを見せて下さい」という要求だけです。いつものホームページを閲覧するのと変わりません。つまり、ゲームサーバの「ゲーム」の所はまだ未開拓な訳です。

 ゲームサーバと言うからには、ブラウザに開いたゲームクライアント上から非同期で何かサーバに要求を出して、それを受けて何らかのレスポンスを返す機能が必要です。これを実現するには、@クライアント側で開いているページをリロードせずにサーバに何か通信する仕組み、A通信手段、Bクライアントから来る通信をサーバ側で受ける仕組み、C受けた通信から値を抽出し、何かごにょごにょしてクライアントに結果を送信する仕組み、Dサーバから非同期で来る結果を受け取る仕組み、という各段階の実装手段を知る必要がありそうです。…どうすんだべか(^-^;。また長い調査の始まりです。



@ サーバ側はPHP、クライアント側はJavaScriptで

 前章で、ゲームクライアントについてあれこれ検討し、とりあえずHTML5で行こうかなと決めました。で、調べた範囲ですがHTML5とJavaScriptという組み合わせが良さそうなんです。なので、サーバ側はPHPで、クライアント側はJavaScriptで記述してみようかなと思います。



A 相互通信はHTTPで

 では、クライアント側からサーバ側にどうやって情報を受け渡そうかなと考える訳ですが、当然サーバが認識できる形式で送らないといけないわけです。今LAMP構成で作ったサーバはWebサーバなので、HTTPなら認識できます。apache君がHTTP信号がやって来るポートをじ〜っと見ていて、やってきたら「ほいさ!」っとそれを受け取り、そのHTTPの中身を解釈して振る舞う。それが「marupeke.htmlを下さい」という事であればそのHTMLを送信しますし、「test.phpを呼び出しなさい」という事であれば、対象のPHPスクリプトを実行するわけです。つまり、HTTPに情報を乗せれば相互通信が出来そうです。

 となると真っ先に疑問になるのが「クライアント側のJavaScriptからHTTPをどうやって送信するのか?」です。まずはその辺から調べてみましょう。

 Google先生で「JavaScript HTTP 送信」と検索するといくつかサイトが出てきました。それらサイトを眺めていると頻繁に「XMLHttpRequest」というオブジェクトが出てきます。どうやらこの人が通信の鍵を握っているようです。XMLHttpRequestのWikipediaによると、これは「JavaScriptなどのウェブブラウザ搭載のスクリプト言語でサーバとのHTTP通信を行うための、組み込みオブジェクト(API)である。」とあります。また「すでに読み込んだページからさらにHTTPリクエストを発することができ、ページ遷移することなしにデータを送受信できる」とあります!お〜、どストライク(^-^)

 使い方は、クライアント側でXMLHttpRequestオブジェクトを生成し、openメソッドで送信したい項目を登録、sendメソッドで送信と至ってシンプル。え〜と、サンプル作りたいですね。んと〜、じゃぁユーザがブラウザ上で何かキーを押す度に、サーバ側から適当なテキストが返って来るようなサンプルを目指してみます。とりあえずHTML5のCanvasとキー押し下げチェックが入ったベースとなるHTML5を先に用意しときます:

HTTPRequestTest.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>XMLHTTPRequestテスト</title>

        <script>
            var context;
            var timer;

            window.onload = function() {
               var canvas = document.getElementById("view");
               if ( canvas.getContext ) {
                    context = canvas.getContext("2d");
                    var timer = setInterval( mainLoop, 16 );
                    document.onkeydown = onKeyDown;
                }
            }

            // キーチェック
            function onKeyDown( e ) {
                // ここでサーバに送信する!
            }

            function mainLoop() {
                context.clearRect( 0, 0, 320, 240 );
                context.font = "32px Arial";
                context.fillText( "何かKeyを押すのだ!", 20, 60 );
            }
        </script>
    </head>

    <body>
        <canvas id="view" width=320px height=240px></canvas>
    </body>
</html>


 テストはこちら

 この辺詳しくはIKD備忘録「HTML5で最低限のゲーム環境を作ってみる」をご参照ください。

 画面で何かのキーを押し下げると、上のコードのキーチェックの所にイベントが飛んできます。ここでサーバに対してHTTPを送信する事にします。まずはXMLHttpRequestオブジェクトを作成し、openメソッドを呼んでみます。え〜と、XMLHttpRequest.openメソッドに与える引数は…、

XMLHttpRequest.openメソッド
open( method, URI, async );

methodにはHTTPの命令(メソッド)を文字列で与えるようです。HTTPのメソッドには8種類あるようですが(HTTPのWikipedia)、ほとんどはGETとPOSTで賄えるようです。GETはクライアントが指定のURIにあるリソースを取って来るメソッドです。一方POSTはクライアントがサーバにデータを送信するメソッドです。どちらもサーバからは返答できるようなのですが、POSTにするとちょっと面倒があるので、今はGETで行こうかと思います。
URIにはメソッドの引数となる文字列を与えます。GETであればリソースの場所、つまりURLになります。POSTもURL…ですな(^-^;。サーバはこの文字列から振る舞いを判断する事になります。
asyncには送信してからサーバからの返答が返って来るまで待つか待たないかという「同期」の有無をtrueもしくはfalseで指定します。trueにすると非同期、つまり送信後特に待つことなくプログラムが進んでいきます。一方falseにすると送信後サーバが返答してくるまでプログラムがそこで止まってしまいます。ゲームの場合これは大アウトですから、このフラグはtrueで決まりです。

 URIの所にどういう文字列を与えるかですが、URLに情報がくっついたような物を送ります。例えばこんな感じ:

xmlHttpRequest.open( "POST", "php/keyDown.php?keyCode=" + keyCode, true );

 URLの所が中途半端な感じがしますね。普通URLと言うと「http://marupeke296.com/」のようにドメイン名が入っているのですが、そこが抜けています。上のようにすると開いているHTMLのディレクトリをルートパスとした相対パスで、そのHTMLがあるサーバに対してPOSTされます。「じゃぁフルURLで書けば世界中のあらゆるドメインにPOSTできるの?」となりますが、実はこれは原則的に出来ません。これは「クロスドメイン問題」というセキュリティの問題上があり制限されているためです。ま、今はゲームサーバがあるドメインの中である事が確定なので、上のようにドメインの後からの相対パスで指定します。

 ファイル名の後ろに「?」が付いています。これは「ここから先はパラメータ文字列ですよ〜」というマークです。ここから先の文字列は「クエリストリングス」と言って、サーバに引き渡すパラメータを並べる事ができます。書式は決まっていて[パラメータ名]=[値]がひと固まりで、複数ある場合は「&」をセパレタとします。

 openメソッドで送信する情報を設定したら、後はsendメソッドを呼ぶだけです。sendメソッドにはクエリパラメータと同様のパラメータを付記できるのですが、HTTPのヘッダー情報を正しく設定しないとうまく送れません。今はテストなので引数はnullにしてしまいましょう:

xmlHttpRequest.send( null );

 これでopenで指定したリソースに対してクエリストリングスに渡したパラメータがサーバに向けて送信されます。で、今は非同期なので、送信後に受信を待っておかなければなりません。送信後にサーバから返信が来た時に呼ばれるメソッドがあります。それがXMLHttpRequest.onreadystatechangeメソッドです。このメソッドを設定しておけば、返信後に勝手に呼ばれます。ここまでをコードにすると、こんな感じになります:

データ送信!
// キーチェック
function onKeyDown( e ) {
    // ここでサーバに送信する!
    var xmlHttpRequest = new XMLHttpRequest();
    xmlHttpRequest.open( "GET", "php/keyDown.php?keyCode=" + e.keyCode, true );

    xmlHttpRequest.onreadystatechange = function() {
        // 受信!
    };

    xmlHttpRequest.send( null );
}

この段階で実際にクライアント側から送信できます。openメソッドで指定したPHPファイルを適当に作っておいて(空でOK)、Chromeでその様子を見てみると…:

お〜確かに指定のphpファイル(keyDown.php)を取得出来ています。Status Codeが「200」になっていますが、これが「成功だよ〜」の印です。もちろんこの時にリロードは入っていません(ここ重要!)

 さて、先程のコードではsendメソッドに押し下げたキーコードを渡していました。今度はサーバ側でそれを受ける所に注目する事にしましょう。



B サーバでHTTPからの情報を受ける&クライアントに返す

 クライアント側でXMLHttpRequest.sendメソッドの引数に渡したパラメータは、openメソッドで指定したPHPコードに渡されて来ます。PHP側では「$_GET」というグローバルな変数にパラメータが格納されるそうです。

 例えばクエリストリングスとして、

id=IKD&pass=hogehoge&param1=50

と複数のパラメータ(id, pass, param1)を設定た場合、PHP側では、

<?php

    $id = $_GET['id'];
    $pass = $_GET['pass'];
    $param1 = $_GET['param1'];

?>

とハッシュとして受け取る事ができます。

 Aでは押し下げたキーコード(keyCode)をパラメータとして渡していました。テストとしてそれをPHP側で受けて、その値をそのまんまクライアント側に文字列として返してみます。PHPのコードはこんな感じになります:

keyDown.php
<?php
    $keyCode = $_GET['keycode'];
    print( $keyCode );
?>

print関数で文字列を出力する、実はたったこれだけでサーバ側はクライアントに文字列を返した事になります。クライアントが受け取るのはPHPのコードでは無くて「PHPのコードが出力した文字列」だからです。あら、あっさり(^-^;

 文字列だけでやりとりするわけですから、後はサーバ側でパラメータに対してどのような結果でも返せますよね。



C クライアント側での受け取り

 sendメソッドでサーバに送信すると、サーバは何らかの文字列を返してくれます。返信された時に呼び出されるのがXMLHttlRequest.onreadystatechangeメソッドです。クライアント側はこのメソッドを登録して、その中で値を得ます。このメソッドは実は何度も呼ばれる場合があります。理由は返信にもいくつかの状態(State)があるためです。このメソッドは名前にあるように状態が変化するたびに呼ばれるんです。

 状態にも色々あるのですが、とりあえず読み込みや解析が終わってデータ取得完了な状態になると、XMLHttpRequest.readyStateに「4」という値が格納されます。その状態になった時にサーバが渡してくれた文字列を受けてみる事にしましょう。実装はこうなります:

データ受信!
// キーチェック
function onKeyDown( e ) {
    // ここでサーバに送信する!
    var xmlHttpRequest = new XMLHttpRequest();
    xmlHttpRequest.open( "GET", "php/keyDown.php?keyCode=" + e.keyCode, true );

    xmlHttpRequest.onreadystatechange = function() {
        // 受信!
        if ( xmlHttpRequest.readyState == 4 )
            keyCode = xmlHttpRequest.responseText;
    };

    xmlHttpRequest.send( null );
}

サーバが返してくれた文字列はXMLHttpRequest.resonseTextプロパティに格納されていますので、それを変数に受けます。これでサーバへの送信から受信までのプロセスが出来ました!!思いの外簡単でびっくり。


 サンプルとして、クライアント側で押し下げたキー情報をサーバに渡し、それに対応した文字列を返すブラウザアプリを作りました:


テストはこちら

 クライアントとサーバとで送受信ができました。これを使って少しずつブラウザゲームを作っていけそうですが、問題は「データの永続性」そして「セキュリティ」。その辺りをまた調べていきましょう。