人工知能に関する断創録

人工知能、認知科学、心理学、ロボティクス、生物学などに興味を持っています。このブログでは人工知能のさまざまな分野について調査したことをまとめています。最近は、機械学習、Deep Learning、Kerasに関する記事が多いです。



CGIへスコア情報を送信する

(注)サーバは解約したのでスコア登録はできません。ご自身のサーバに置き換えてください。

サーバ上にあるCGIに接続し、スコア情報を送信するサンプルプログラムを書いてみます。ここではCGIをPerlという言語で書いています。 ServletやJSPを使うことでJavaでもCGIを作れます。しかし、レンタルサーバーで対応していることが少ないため今回はよく使われるPerl を選択しました。

ここでは、必要最小限の構成にするためコンソールアプリケーションを作りました。実行するにはファイルをダウンロードしてrun.batをダブルクリックしてください。

send_score.zip

スコアを送信

手順をまとめると

  1. CGI に接続する
  2. CGIへの書き込みストリームを開く
  3. 書き込みストリームを通してCGIへデータを送信する
  4. CGIからの読み込みストリームを開く
  5. 読み込みストリームを通してCGIの出力を受信する

単純なので全コードを掲載します。

public class SendScore {
    public static void main(String[] args) {
        // もりの得点が1500だったというデータ
        String data = "name=もり&score=1500";
        try {
            // CGIを表すURLオブジェクト
            URL cgiURL = 
                new URL("http://javagame.main.jp/cgi-bin/score/send_score.cgi");
            // 接続
            URLConnection uc = cgiURL.openConnection();
            uc.setDoOutput(true);
            uc.setUseCaches(false);
            // CGIへの書き込み用ストリームを開く
            PrintWriter pw = new PrintWriter(uc.getOutputStream());
            // CGIにデータを送信する
            pw.print(data);
            // ストリームを閉じる
            pw.close();
            
            // CGIからの読み込み用ストリームを開く
            BufferedReader br =
                new BufferedReader(
                    new InputStreamReader(uc.getInputStream()));
            // CGIの出力を読み込んでコンソール画面に表示
            String line = "";
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
            // ストリームを閉じる
            br.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

送信するデータはString型で&で区切られた形にしています。

   name=もり&score=1500

これは、もりさんのスコアが1500点だというデータです。CGIはこのデータを処理してスコアを保存したり、表示したりします。CGIのアドレスはURLクラスで表します。

    URL cgiURL =
        new URL("http://javagame.main.jp/cgi-bin/score/send_score.cgi");

score.cgiは自作のCGIで私が借りているレンタルサーバ(http://javagame.main.jp/)上に配置してあります。旧ドメインなのでリンク切れです。send_score.cgiに先ほどのデータを送信するわけです。CGIの作り方は次のCGIの構成を見てください。CGIへの接続は、URLConnectionを使います。

    URLConnection uc = cgiURL.openConnection();

URLクラスのopenConnection()で接続を開始します。CGIへデータを送信したり、受信したりするためにはストリームを使います。 Javaはファイルへ読み書きするのと同じ方法でネットワークを介したCGIへ読み書きできます。

    // CGIへの書き込み用ストリームを開く
    PrintWriter pw = new PrintWriter(uc.getOutputStream());
    // CGIにデータを送信する
    pw.print(data);

文字列を読み書きしたいのでPrintWriterを使います。URLConnectionのgetOutputStream()で開いたストリームをPrintWriterに渡すだけです。CGIへデータを送信するにはこのストリームにデータをprint()するだけです。一般的にCGIは何らかのデータ(普通はHTML)を返してきます。その返されたデータを受け取ってあげましょう。

    BufferedReader br =
        new BufferedReader(new InputStreamReader(uc.getInputStream()));
    // CGIの出力を読み込んでコンソール画面に表示
    String line = "";
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }

読み込みにはBufferedReaderを使います。URLConnectionのgetInputStream()で開いたストリームを InputStreamReader()を通してBufferedReaderに渡します。ファイルから読み込むときと似てますね。CGIからデータを受信するにはファイルと同じくreadLine()を使って1行ずつ読み込みます。ここでは単純に画面に出力しています。

CGIの構成

このサイトはJavaがメインなのでPerlについては特に解説しません。Perlについて詳しく知りたい方はPerlプログラミング講座や初めてのPerlを読んでください。

初めてのPerl 第6版

初めてのPerl 第6版

CGIもそんなに複雑でないので全コードを掲載します。

#!/usr/bin/perl

# 送信されたデータを受け取る
if ($ENV{'REQUEST_METHOD'} eq "POST") {
    read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
}

# データを分割する
@pairs = split(/&/, $buffer);
# 各データをさらに分割して%paramハッシュに入れる
foreach $pair (@pairs) {
    ($key, $value) = split(/=/, $pair);
    # valueが日本語の場合コード化されているので変換する
    $value =~ s/%([a-fA-F0-9][a-fA-F-0-9])/pack("C", hex($1))/eg;
    $param{$key} = $value;
}

# 出力されるHTML
print <<END;
Content-type: text/html

<html>
<head><title>あなたの得点は登録されました</title></head>
<body>
<h1>あなたの得点は登録されました</h1>
<p>$param{name}さんは$param{score}点です。</p>
</body>
</html>
END

CGIは大きく分けると

  1. データを受信する
  2. 受信したデータを処理する
  3. HTMLを送り返す

となっています。上にあげたサンプルも3つに分割できます。まずデータを受信するところです。

# 送信されたデータを受け取る
if ($ENV{'REQUEST_METHOD'} eq "POST") {
    read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
}

まあこれはこう書くものと思ってもらっていいと思います。CGIへのデータ送信方法はGETとPOSTという2種類があるんですが、ここでは POSTの方を使っています。Javaから送ったname=もり&score=1500という文字列が$bufferに入ることだけわかればいいと思います。次にデータ処理部分です。

@pairs = split(/&/, $buffer);

は、name=もり&score=1500を&で分割して、name=もりとscore=1500を配列@pairsに入れています。そして、

foreach $pair (@pairs) {
    ($key, $value) = split(/=/, $pair);
    # valueが日本語の場合コード化されているので変換する
    $value =~ s/%([a-fA-F0-9][a-fA-F-0-9])/pack("C", hex($1))/eg;
    $param{$key} = $value;
}

の部分で、name=もりをさらに分割してハッシュ%paramを作ってます。

   ハッシュ%param
   name => もり
   score => 1500

というハッシュです。Perlではハッシュをよく使うのでわからない方は上のリンク先で調べてみてください。最後にHTMLを生成してJavaプログラムに返します。

# 出力されるHTML
print <<END;
Content-type: text/html

<html>
<head><title>あなたの得点は登録されました</title></head>
<body>
<h1>あなたの得点は登録されました</h1>
<p>$param{name}さんは$param{score}点です。</p>
</body>
</html>
END

$param{name}が「もり」に、$param{score}が「1500」に置き換わったHTMLになります。実際にプログラムを実行すると画面にこのHTMLが表示されることがわかります。

最後にこのCGIをサーバーにアップします。ご自分でサーバーを持っている場合はアップしてみてください。このとき、CGIのパーミッションを実行可能(755)にしとかないと正しく動きません。結構忘れがちなので注意です。

「あなたの得点は登録されました」とか書いてありますが、登録してません(笑)もしスコアを登録(保存)したい場合はこのCGIを改造する必要があります。HTMLを返す前にファイルを開いてスコアをファイルに書き込む処理をはさむだけです。次回取り上げます。