Java HTML解析 〜Wikipedia 武器 ページの解析〜

タイトルにあるページの解析を行います。解析する内容は、表示されている武器の一覧を作るための情報を抜き取るような形です。

ページの解析

HTMLはタグで、ツリー状に出来上がっているので、そこを基準にして解析します。

そして、忘れてはいけないのが「目的」です。今回の目的は、武器の一覧を作成することですので、以下のような項目を取得することを目的にしようと思います。

  1. 武器の種類(刀、鉄砲、戦車etc...)
  2. 武器を使うためのエネルギー源
  3. 現実か、架空のものか( Yes or No)
  4. その武器に対する説明、逸話など

上のような解析をしたいと思います。
駄菓子菓子!自然言語処理ができるわけでもなし、完全に、独自理論での実装になります。

ステップアッププログラミング!

何はともあれ、いきなりすごいことはできないので一歩ずつ進みます。
千里の道も一歩から、プログラミングの良いところは「理解すれば、すぐできる」というところです。ギターとか、ドラムとかのように毎日の練習は必要ありません。その代わり勉強が必要かもしれないけど(笑)

タグを取得

タグを取得します。実装したコードはこちらになります。Githubですので、ダウンロードも可能です。

public void execute() {
    System.out.println("*** execute ***");
    String url = "https://ja.wikipedia.org/wiki/%E6%AD%A6%E5%99%A8";
    Document doc = null;
    try {
        doc = Jsoup.connect(url).get();
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("*** Get Content ***");
    Element body = doc.body();
    Elements eles = body.getElementsByTag("h2");
    System.out.println("size: " + eles.size());
    for (int i = 0; i < eles.size(); i++) {
        Element item = eles.get(i);
        System.out.print("wholeText(): " + item.wholeText());
        System.out.println(" / text(): " + item.text());
    }
}

前回作成したコードよりもスッキリしています。シンプルに「h2タグ」を取得してそれをコンソールに出力しているだけです。

HTML解析 Lv1: ポイントでデータ取得

上のコードは、h2タグを取得しているだけですが、これを、「〜タグを取って、次は〜」というように、順番に必要なデータを取得していくのも1つの手段だと思います。小難しい「人工知能」など使わなくてもいけます(笑)多分ね。。。

そして、ちょいと考えてみたのが、下のような手順です。

  1. Wikiをターゲットにして、Wikiページから調べたい情報を取得するために、Wikiページの構成を調べる
  2. 構造を理解し、必要な情報を欲しい形で取得できるようにプログラム設計を行う
  3. 実装、テスト、成果物(実行結果)を使用してテキストRPGを作る

こんな感じの実装でいけると思います。
ダメだったら、また別な方法を考えます(笑)

そんなもんでしょう(笑)

関連ページ

Java Basic

  1. Java Basic Level 1 Hello Java
  2. Java Basic Level2 Arithmetic Calculate
  3. Java Basic Level3 About String class
  4. Java Basic Level 4Boolean
  5. Java Basic Level 5If Statement
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 Traning of If statement
  8. Java Basic Level8 How to use for statement
  9. Java Basic Level 8.5 Array
  10. Java Basic Level 9Training of for statement
  11. Java Basic Level 10 While statement 
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1
  17. Java Basic JUnit 〜テストスイートの作り方〜


Java Network 〜HTMLからデータ取得2〜

今回は、HTMLからのデータ取得を行う処理を実装しようと思います。その2回目です。

HTMLからデータ取得

HTMLからデータを取得する、つまりインターネットにアクセスして特定のウェブページを見る(閲覧)などして自分らが必要な情報を取得する、ということをプログラムにやらせようという分けです。

前提として、プログラム(PC)は人間ほど柔軟な思考ができるわけでもなく、我々人間が指示(プログラム)した通りにしか動いてくれません。なので一工夫が必要になります。

最近では、人工知能なんてものもありますので、この分野は広く開拓できそうです。
初めから複雑なことはできないので、1つずつ段階的に実装していきます。

データ取得Lv2

前回は、データの取得、HTMLを取得するという部分を実装しました。ソースはGithubにアップしてあります。
下のようなコードを実装しました。

// ⑴ URLを指定する
String targetUrl = "https://ja.wikipedia.org/wiki/%E6%AD%A6%E5%99%A8";
// ⑵取得したHTMLをオブジェクトにするためのクラス
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
    builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}
// ⑶ 指定したURLにアクセス、HTMLの取得
try {
    URL urlCls = new URL(targetUrl);
    urlCls.openConnection();
    InputStream inp = (InputStream) urlCls.getContent();
    Document html = builder.parse(inp);
} catch(MalformedURLException e) {
    e.printStackTrace();
} catch(IOException ie) {
    ie.printStackTrace();
} catch (SAXException e) {
    e.printStackTrace();
}

ここで使用しているクラスはJDKに入っているもので「〜.jar」をダウンロードとかしなくても使用できるコードになります。

そして、あくまでもHTMLを取得するまでの処理なのでここからがHTMLの読み込み、必要なデータの取得になります。

HTMLの読み込み

早速コードを見ていきます。

    try {
        URL urlCls = new URL(targetUrl);
        // 指定のURLにアクセス(コネクションの取得)
        urlCls.openConnection();
        // HTML文書の入力ストリーム(ファイルと同じ扱いです)
        InputStream inp = (InputStream) urlCls.getContent();
        Document html = builder.parse(inp);
        // 取得したHTMLの「ulタグ」を全て取得する
        NodeList nodes = html.getElementsByTagName("ul");
        for (int i = 0; i < nodes.getLength(); i++) {
           // 子ノードを取得し再起処理を行う loopPrint(nodes.item(i).getChildNodes());
        }

    } catch(MalformedURLException e) {
        e.printStackTrace();
    } catch(IOException ie) {
        ie.printStackTrace();
    } catch (SAXException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return "";
}
/** 再起処理を行うメソッド */
private void loopPrint(NodeList list) {
    String emp = System.getProperty("separator") + "\t\t";
    for (int i = 0; i < list.getLength(); i++) {
        Node node = list.item(i);
        if (node.hasChildNodes()) {
            loopPrint(node.getChildNodes());
        } else {
            if ("#text".equals(node.getNodeName())) {
                System.out.println("NodeName: " + node.getNodeName() + " -> Value: "+ node.getNodeValue());
            }
        }
    }
}

上の実装では「再起処理」がポイントになります。
取得したHTMLはタグで階層状になっているので階層をどんどん下がって、次のタグへ。。。というような処理が必要になります。このような時にしようするのが「再起処理」です。

つまるところは、以下のような処理です。

  1. ulタグのはじめの部分を取得
  2. タグの子ノードの有無を確認
  3. 子持ちなら、同じメソッドを呼び出す、そうでないなら中身をコンソールに表示
  4. これをルートノードの子供の数だけ繰り返す

こんな実装をしています。しかし、ulタグを取得しているだけなので、ちょっと実用的ではありません。

まとめ

駄菓子菓子、読み込み中身を取得しても、必要なデータのみを読み込むのに、色々と処理を書かないといけないので、ライブラリを使用して実装し直すことにします。使用するライブラリはJsoupです。
でわでわ。。。



Java Network 〜HTMLからデータ取得1〜

今回は、HTMLからのデータ取得を行う処理を実装しようと思います。

HTMLからデータ取得

HTMLからデータを取得する、つまりインターネットにアクセスして特定のウェブページを見る(閲覧)などして自分らが必要な情報を取得する、ということをプログラムにやらせようという分けです。

前提として、プログラム(PC)は人間ほど柔軟な思考ができるわけでもなく、我々人間が指示(プログラム)した通りにしか動いてくれません。なので一工夫が必要になります。

最近では、人工知能なんてものもありますので、この分野は広く開拓できそうです。俗に言う「自然言語解析」と言うやつです。
初めから複雑なことはできないので、1つずつ段階的に実装していきます。

データ取得Lv1

データの取得、HTMLを取得するという部分を実装します。
結論からいうと下のようなコードになります。

// ⑴ URLを指定する
String targetUrl = "https://ja.wikipedia.org/wiki/%E6%AD%A6%E5%99%A8";
// ⑵取得したHTMLをオブジェクトにするためのクラス
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
    builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}
// ⑶ 指定したURLにアクセス、HTMLの取得
try {
    URL urlCls = new URL(targetUrl);
    urlCls.openConnection();
    InputStream inp = (InputStream) urlCls.getContent();
    Document html = builder.parse(inp);
} catch(MalformedURLException e) {
    e.printStackTrace();
} catch(IOException ie) {
    ie.printStackTrace();
} catch (SAXException e) {
    e.printStackTrace();
}


ここで使用しているクラスはJDKに入っているもので「〜.jar」をダウンロードとかしなくても使用できるコードになります。
処理の内容としては、以下の通りになります。

⑴ URLを指定する

これは、説明するまでもないような気もしますが、ポイントになるので。。。
文字列の変数「targetUrl」に、アクセスするURLを設定します。
現状のコードでは、固定値になりますが、今後この部分を引数に変更して動的なURLにアクセスできるように変更します。

⑵取得したHTMLをオブジェクトにするためのクラス

Documentクラスを使用して取得したHTMLから必要な情報を取得します。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

実際にHTMLの文字列を取得することもできますが、取得したいデータは、記載されている内容なので、タグの部分は無視したいためこのような方法を使いました。
ちなみに、これ(Documentクラス)も余計な情報をふんだんに持っているので、今後はJsoupを使用して実装する予定です。これを使うにはリンク先のJARファイルをダウンロード、ビルドパスに設定という手順を踏む必要があります。→この部分に関してはこちらの記事を参照ください。この記事ではJSONライブラリ(json-path-2.1.0.jar)を追加しています。下のような記事です。

⑶ 指定したURLにアクセス、HTMLの取得

ここまできたら、あとはメソッドを呼び出すだけです。
ちょっと注意が必要なのはURL#getContent()の返り値がObujectなので「何でも返す」というところです。当然本当はStringを返しているのにFileで受け取ろうとすればExceptionがでます。このメソッドの用途を明確にして取得するもの(HTMLやイメージファイルなど)をはっきりさせて実装する必要があります。

まとめ

今回は、HTMLを取得するのに以下のクラスを使用してやりました。

  1. URLクラス: インターネット上のコンテンツ(HTMLなど)にアクセスする
  2. Documentクラス: 取得したコンテンツをドキュメント(HTMLやXML)として扱う ※同じ名前で違う機能のものがあるので注意が必要です。
  3. [InputStream](): 読み込み用、入力ストリームです。この「ストリーム」に関してはこちらの記事を参照ください。クラスの実装例でInputStreamを使用しています。
  4. DocumentBuilderFactory: DocumentBuilderクラスを取得するためのクラスです。
  5. DocumentBuilder: Documentクラスを取得するためのクラスです。

これらのクラスを使用して、インターネット上のHTMLファイルを読み込み、コンテンツ(HTML文書)を取得、までの実装になります。こちらのGitにあるソースは、文書の読み込みも実装しています。

目的の確認

HTMLをダウンロードしてその中身を解析することが目的なのでこの方法では、HTMLを取得するのに結構な手間がかかります。なのでフレームワークの利用を検討します。

次回は読み込み部分の処理について記載したいと思います。

でわでわ。。。



Java IoT 予備知識〜ラズパイにJava(Web)サーバーを立てる〜

ラズパイにIoT開発環境を構築するという自前のミッションがあるのですが、急遽ラズパイにJava製のWebサーバーを起動しようということになりました。※そのように考えた次第です。。。

具体的に、Iotの実装を行うためにはそれなりの開発環境を構築する必要があります。※JavaME環境

しかし、自分が持っているラズパイは古いので、遅い。。。開発環境を構築して作業を行うのはちょいと面倒なわけです。

ラズパイでの開発環境構築が必要な理由は、デバイス(USB、GPIOなど)へのアクセスが必要になるため実機での開発環境が必要になります。

なので、今回は古いラズパイ(RPi2 model B)での実装を行うので、Windows上で開発したJavaサーバーをラズパイに移植して起動しようという考えです。

具体的な手順

  1. Widnows上で開発、テストを行う
  2. JARファイル(実行ファイル)を出力して起動できる確認をする
  3. 作成したコードをGithubにコミットする
  4. ラズパイでGithubからソースをチェックアウトしてビルド、JARファイルを作成
  5. ラズパイの起動時に実行するシェル・スクリプトで手順4の処理を行う

上記のような手順で作業を行うと結果としては、下のように動くであろうというところです。

  1. ラズパイに電源を入れる
  2. 起動スクリプトが動く
  3. 作成したJavaサーバーが起動する

問題は、作成したJavaサーバーに何を行わせるか?というところです。

今回作成する、JavaサーバーはHTTPプロトコル以外でも受信し処理を行うことができるものを作成します。
「なぜそれが可能か?」というところですが、これは低レベルな実装なためHTTPだろうが、バイナリだろうが関係なく受信、レスポンスの送信を行うことが可能になります。

その代わり、以下のような部分を実装する必要があります。

  1. 受信したリクエストがHTTPなのか、それ以外なのか?を判別
  2. HTTPリクエストならばHTTPでレスポンスを返し、それ以外はそれぞれのレスポンスを返す
  3. それぞれのプロトコル(HTTP, FTPなど)で受信したときにどのような処理を行うのか?

通常というか身近にある「ウェブサーバー」と呼ばれているものはHTTPリクエストを受け、それに対応するHTMLを読みこんで、そのテキストデータ(HTML)を返却、ブラウザで表示というような処理を行っていますが、

今回作成するJavaサーバーは低レベルなため、大体のことは大体できますが、以下の部分を実装する必要があります。

HTTPリクエストを受け、それに対応するHTMLを読みこんで、そのテキストデータ(HTML)を返却

この部分を実装できるようになると、自作のフレームワークを作成したり一人で多くの作業を行おうとするときに「前もって作成しておく道具」として使用することができます。このような「道具」を作成しておくとやらなければいけいない作業が減り、いろいろなことができるのではないでしょうか?

注意点

上記のJavaサーバーは、ServerSocketを使用して作成しますが、これの実装をする前に理解しておく必要がいくつかあります。

  • サーバーとクライアントの関係に関する理解
  • Webサーバー、アプリケーションサーバー(APサーバー)、DBサーバーの関係性に対する理解
  • MVCモデルの概要に対する理解

まとめると次のようになります。

  1. リクエストの送信と受信を行うときに「サーバー」と「クライアント」の関係がある
  2. HTTP以外にもプロトコルがあり、ブラウザはHTTP、FTPなどのプロトコルを使用する
  3. HTTP以外のプロトコルで、Socket通信のようにバイトデータをそのまま送信することもある
  4. HTTP、それ以外、それぞれの通信方法で、それぞれに対応した処理を実装する必要がある

次はHttpServeltの実装について

ちょっと難しくなってしまったので、まずはJavaでのウェブサーバーを見ていきます。

JavaのWebサーバー

とりあえずはここのページを参考に作成しようと思います。

結論から言うと。。。

HttpServletクラスを拡張(extends)してリクエストを受け付ける常駐アプリを作成しようと言うことです。

とても、シンプルなJavaウェブサーバーです。Java出なくてもクライアントとサーバーの関連を学習したい人は一読作成して見ると、一発で理解できます。(理解できないと作成できません。)

このような、わりかし低レベルな実装は経験する機会があまりないので、Java学習者やウェブデザイナーなどウェブ・サーバーをよく使う人には一度やって見ると理解が早いです。

ちなみに、「通常実装するとき」というのは、SpringBoot(DispatcherServlet)、JavaEE(FacesServlet)のようなフレームワークを使用した実装のことを指しています。

今回作成するシンプルなJavaウェブサーバーというのは、一段レベルを下げて上記のようなサーブレットクラスの親クラス・インターフェース部分の実装を行うというところです。

ちなみに、FacesServletクラスはServletインターフェースを実装しています。なので、この部分に係る実装を行うということです。

具体的には、HttpServletクラスの子クラスを作成してこのクラスを拡張してやります。

大まかな仕組み

下のような図になります。

ふざけているのではありませんので。。。

  • クライアント(スマホやPC)からURLを指定してサーバーにアクセスします。
  • クライアントは、PCやスマホのことなので自分が持っている機械(デバイス)です。

そして、サーバーはどこかの会社とか、国の機関とかに置いてあるウェブサーバー(アプリ)を起動している機械(コンピュータ)にアクセスしてそのアプリが返却するHTMLを見ている状況です。一緒にJSなどもダウンロードされて画面(ブラウザ)に表示されます。

大まかに上のような処理を行う「ウェブサーバー」をJavaを使用して作成しようと言うことです。

サーバーはアプリのこと

サーバーはアプリケーションです。常駐して動き、何かしらのアクセスを待機しているアプリケーションのことを「サーバー」と呼びます。別な言い方をすると「常駐待機しているアプリケーション」ということになります。
具体的には、PCを動かすために起動する(画面を表示する)Xサーバーなんてものもありますし、電源を入れたらスマホの画面が開くのも常駐アプリなので「サーバー」の仲間に入ります。
全部がサーバーだと呼ぶのに不便なのでスマホアプリと言ったり、サーバーと言ったりして区別します。

ちなみに、サーバー(アプリケーション)を稼働させるためのコンピュータのことを「サーバー機」と呼びます、そして、基本情報などの説明書きには「サーバー」などと書かれていますが、「サーバー機」と「サーバー(アプリケーション)」とでは別物なので、注意してください。

Java版Webサーバー

通常は、TomcatとかApacheとかをダウンロードしてきてウェブサーバーとして稼働することが多いのですが、今回はこれをJavaで作成しようと思います、まずは調査から行きますが、TomcatはJavaでできているようです。

そして、よくある書籍などでは、下のようなコードで実装しています。

public class JspLlesson1 extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        res.setContentType("txtext/html; charset=UTF-8");
        PrintWriter write = res.getWriter();
        write.println("Hello World");
    }
}

HttpServletのポイント

このクラスはJavaDocにあるように、抽象クラスです。しかし、抽象メソッドが定義されていないので「必ずオーバーライド」をしなくてもよい形になっています。

意図としては、継承して使用してくださいということなのだと思います。※予想になります。。。

そして、「doGet()」を「doPost()」メソッドをオーバーライドすることでHTTPリクエストに対する処理を実装する事ができます。

上記の落書きのようなイメージにある、リクエストとレスポンスをハンドルする処理がこのクラス・メソッドに実装することになるというわけです。

具体的には、リクエストの種類が2つあり、それぞれ「GETリクエスト」「POSTリクエスト」になります。

GET(ゲット)リクエスト

ブラウザで、URLを叩いたときに「XXXX.html」のようなファイルを開くときに使用するリクエストです。
その名の通り、対象のファイル(HTMLファイル)をダウンロード、ブラウザで表示します。
もし、ブラウザではなくプログラムで対象のURLにアクセスしたときはどのようになるか?

下の画像は、次のコマンドをコマンドプロンプトでたたいたときの結果です。

curl https://zenryokuservice.com

POST(ポスト)リクエスト

これは、画面のキャプチャがとりずらく、イメージが取れませんでしたが、よくある「ログイン画面」を思い出してもらうとよいです。
これらの画面には、必ず「ログイン」などのような文言のある「ボタン」があると思います。

この「ボタン」はPOSTリクエストを送信するボタンになっていて、HTMLのみで書くとしたのようなHTMLコードになります。

HTMLソース
<form action="cgi-bin/abc.cgi" method="post">
<p>
名前:<input type="text" name="namae">
</p>
<p>
<input type="submit" value="送信する">
<input type="reset" value="入力内容をリセットする">
</p>
</form>

実際のブラウザ表示 ※このボタンを押下すると画面がリロードされるだけです。


HTMLソース

名前:



ちょっと補足を加えます。上記のHTMLは「formタグ」で囲っている内容をPOSTリクエストで送信しますという意味ですが、「formタグ」の属性(attribute)部分の「action」を見てみると「空」になっています。

このアクションが「空」になっているときは「このページにリクエストを送信しますよ」という意味になります。

同様に「post」の部分を見てみると「post」と書いてあります。これは「POSTリクエストを送信しますよ」という意味です。

そして、「inputタグ」にある「name」の部分はこの名前で、それぞれの値を送信するという意味になります。
つまりは、submitのボタンを押下したら、送信先へPOSTリクエストが送られるのですが、POSTリクエストはデータが一緒に送信されます、GETリクエストでも、データの送信ができますが、この場合はURLの後ろに「?変数名1=値,変数名2=値 ... 」のようにURLへ値を書く必要があります。

これでは、ログイン情報などの秘密にしたい情報が公開された状態でリクエストを送信することになるのでとても危険なのです。そのために「POSTリクエスト」を使用します。

JavaでのWebサーバー

このサイトを参考にしました。

結論から言うと、ServerSocketで受付(ポートを指定してリスン状態で待機)してHTTPメッセージを返却する。と言うものがWebサーバーの正体のようです。

シンプルなSocketサーバーは以前作成しました。

これを一度作ったことがあるならば(ServerSocketアプリのことです)わりかし簡単に作成することができると思います。

しかし、現時点では、HttpServletクラスを拡張した実装を行う方向で話を進めているのであくまでも下のようなコードをベースに考えてもらいたく思います。

public class JspLlesson1 extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        res.setContentType("txtext/html; charset=UTF-8");
        PrintWriter write = res.getWriter();
        write.println("Hello World");
    }
}

このコードは、Tomcatを使用したときのコードです。Eclipseで実行したのですが、作成した上のクラスファイルを右クリックして「で起動」をクリックすると実行する事ができます。

これで、「サーバー上で利用できるリソースがありませんでした。」のようなメッセージが表示されたら下の手順で、実行することができるようになります。

Tomcatの準備

Tomcatランタイムの追加

Eclipseの上部にあるメニューからウィンドウ -> 設定を選択、下のイメージのように 検索部分に「サーバー」と入力 -> 赤枠を選択します。

そして、Java8を使用しているので、Tomcatの8.5を選択します。

ここで、Tomcatがインストール(ダウンロード)されていない場合は、Apacheのサイトからダウンロードしてきます。

ダウンロードした、Tomcatを展開して、Eclipseの設定でそのフォルダーを指定します。

最後に完了を押下します。

サーバープロジェクトの追加

1.パッケージエクスプローラーを右クリック、そのほかのプロジェクトをクリック
2.下のイメージのように、その他から次へをクリック

3.同様に、完了をクリックする※設定を確認する

4.プロジェクトファセットの設定を行います。

5.プロジェクトを選択して「Altボタンを押しながらEnterを押下」すると下のような画面が見れます。

6.プロジェクトファセットを選択、下のイメージのようにJava1.8を設定、動的ウェブモジュールを設定します。

7.設定を適用します。

8.web.xmlを生成します。

web.xmlの設定を行う

書き方は以下のようになります。

<servlet>
  <servlet-name>サーブレット名</servlet-name>
  <servlet-class>クラスファイル名</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>サーブレット名</servlet-name>
  <url-pattern>呼び出す時につけるURLパス名</url-pattern>
</servlet-mapping>

classesフォルダを作成する

最終的に下のように、フォルダ構成を作ります。

JavaServletを実行する

この状態で、下のように、プログラムを実行します。

以下のURLにアクセス

localhost:8080/プロジェクト名/

今回作成したプロジェクト名が「StudyJavaBasic」なので下のようなURLにアクセスすると作成したサーブレットが起動できます。

http://localhost:8080/StudyJava/

Httpメッセージ

よく、「Httpヘッダー」とか「Httpボディ」とか言ったりしますがHttpメッセージの「ヘッダ」「ボディ」と言うのが正確な言い方です。

参考の処理内容

  1. ServerSocketをXXXポートで待機
  2. リクエストを受け付けたら、受け取ったメッセージ(Httpメッセージ)から相手のIPを取得
  3. IPを使用してHttpレスポンスを作成
  4. OuputStreamでリクエスト元に返却する(レスポンスを返却する)

と言うような流れで処理を作成しているようです。

レシピ

詳細に関しては、Java APIを参照してください。

  1. ServerSocket
  2. BufferedReader(Reader)
  3. DataOutputSream(OutputSream)

こんなくらいです。

webサーバー

  1. リクエストを受けて(SocketServer#accept())
  2. リクエストの値からファイルを読み込む
  3. HTTPメッセージボディにHTMLを書く
  4. データ(DataOutputSream)を返却

シンプルにこんな手順で処理を行えば良いと思います。

低レベルなAPIは、全て作成する必要がありますが、逆に言えば全てが想い通りに作れると言うところが魅力的(笑)

こんな感じで考えております。このアプリにどんな機能を追加するかは作成者の思うままですね。。。

何やろうかな?

でわでわ。。。



Java Socket 〜極小サーバーで通信の基本を学ぶ〜

java.net.ServerSocket

このクラスを使用してサーバーを作成します。
サーバー側のコードは以下の通りになります。
ちなみに通信がまともに繋がるまで、テストした時の残骸が残っております。

サーバー

/**
 * サーバーソケットクラス。
 * コンストラクタで指定されたポート番号でサーバーを起動します。<br/>
 * ローカルホスト(アプリを起動するPC)上の指定ポート番号で受付を開始します。
 * @author takunoji
 * 2019/07/20
 */
public class SocketServerBasic {
    private ServerSocket server;
    /**
     * コンストラクタ。
     * 
     * @param portNo アクセスを受け付けるポート番号
     */
    public SocketServerBasic(int portNo) {
        try {
            server = new ServerSocket(portNo);
            if (server.isBound() == false) {
                server.bind(new InetSocketAddress("localhost", portNo));
                server.setSoTimeout(3000);
            }
        } catch (IOException e) {
            this.finalize();
            e.printStackTrace();
        }
    }

    /**
     * Objectクラスの終了処理(ガベージコレクションに呼ばれる)
     */
    @Override
    public void finalize() {
        // メモリの解放
        server = null;
    }

    /**
     * コンストラクタで作成されたサーバーソケットで
     * 受付処理を開始する。
     */
    public void startServer() {
        try {
            System.out.println("*** SocketServer start " + server.isBound() + "***");
            Socket recieve = server.accept();
            System.out.println("*** Server Get Request start***");
            // 受信データの読み込み
            StringBuilder responseTxt = new StringBuilder("<Response>");
            // 受信状態
            System.out.println("クライアント: " + recieve.getRemoteSocketAddress());
            System.out.println("接続: " + recieve.isConnected());
            System.out.println("入力ストリームが閉じている: " + recieve.isInputShutdown());

            // 受信したリクエスト
            InputStream in = recieve.getInputStream();
            // 返却するレスポンス
            OutputStream writer = recieve.getOutputStream();
            System.out.print("Server recieve is ...");
            int c = 0;
            // CRとCRLFの場合で入力が終了している時がある
            while((c = in.read()) != -1) {
                char ch = (char)c;
                responseTxt.append(ch);
                // 空の場合
                if (c == 10 || c == 13) {
                    break;
                }
            }
            System.out.println(responseTxt.toString());
            System.out.println("*** Server Send Response start***");
            // レスポンス送信
            writer.write((responseTxt.toString() + System.getProperty("file.separator")).getBytes());
            writer.flush();
            in.close();
            writer.close();
            System.out.println("*** SocketServer end ***");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 開いたストリームを閉じる
            try {
                closeServer();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * ソケットサーバーを終了します。
     */
    public void closeServer() throws IOException {
        server.close();
        server = null;
    }

    /**
     * メインメソッド
     * @param args プログラム引数
     */
    public static void main(String[] args) {
        SocketServerBasic serverSocket = new SocketServerBasic(8080);
        serverSocket.startServer();
    }
}

クライアント

public class ClientSocketBasic {
    private static final String HOST = "localhost";
    private static final int PORT = 8080;
    /**
     * コンストラクタ
     * @param portNo ポート番号
     */
    public static void main(String[] args) {
        System.out.println("*** SocketClient start ***");
        try (Socket sock = new Socket(HOST, PORT)) {
            // 受信したレスポンス
            InputStream reader = sock.getInputStream();
            // 送信するリクエスト
            OutputStream writer = sock.getOutputStream();
            System.out.println("接続済み: " + sock.isConnected());
            System.out.println("接続サーバー: " + sock.getRemoteSocketAddress());
            // リクエスト送信
            writer.write("ping...\n".getBytes());
            writer.flush();

            System.out.println("*** Testing start " + sock.getInetAddress() + " ***");
            // レスポンス受信
            int c = 0;
            while((c = reader.read()) != -1) {
                System.out.print((char)c);
            }
            System.out.println("*** Testing end***");

            // 終了処理
            reader.close();
            writer.close();
            sock.close();
            System.out.println("*** SocketClient end ***");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ハマったところ

ここで自分が詰まったのはクライアントからのデータを受信するときに「BufferedReader#readLine()]」を使用していたところです。

原因は。。。

データを送信するときに改行がないと受信時にデータの終わりが確認できないので「readLine()」のところで処理が止まってしまうところです。
とりあえずはこちらを参照して実装しました。
実装してみたところ、サーバーとクライアントの送受信で互いに「読み取り」処理の時の区切りが悪かった。。。なので下のように、送信データの終わりが明確にわかるように実装しました。

int c = -;
while((c = reader.read()) != -1) {
   char ch = (char) c;
   stBuilder.append(ch);
}

こんな感じで実装していたのですが、読み取るデータがいつになっても「-1」にならない事件が起きてしまい。。。
結局は下のようにコードを直しました。

int c = -;
while((c = reader.read()) != -1) {
   char ch = (char) c;
   stBuilder.append(ch);
   if(c == 10 || c == 13) {
       break;
   }
}

c == 10, 13の部分はchar型のint値です。10はLF(改行文字)で13はCR('改行文字)のようです。参考サイト
ソースコードはGitにアップしています。SocketServerBasic.java, ClientSocketBasic.javaをアップしてあります。

ネットワーク確認コマンド

コマンドプロンプトで下のようなコマンドを叩くと、使用しているポートがわかります。以下コマンドの書き方

netstat -nao | find "[ポート番号]"
netstat -a(使用している全ポートを一覧)

勘違いによるエラー

1.接続ポートを間違える

あたりまえだけど、サーバーがlocalhost: 2000で動いているならば、クライアントはlocalhost: 2000に接続する必要があります。「java.net.ConnectException」が出力されます。

2.テストしたときのコネクションが残っている

実装時に処理の起動確認を行いながらやりますが、処理の実行時にコネクションが消えていない、となると「Address already in use」とかエラーが出ます。

3.クライアントからの送信するデータの終わり

クライアントからサーバーへデータを送信するデータの終わりを示す文字として「改行コード」を入れてやる必要がある。でないといつまでもサーバーが受信待機する。

とりあえず完了

いやー、read()処理の部分でハマりました(笑)
昔はreadLine()とかで問題なく動いたのですが、ちょっと変わっているようです。今回の実装はJDK1.8での実装になります。
でわでわ。。。

やったこと一覧

  1. Eclipse+Gluonでスマホアプリを作る
  2. Micro:bitで遊ぶ
  3. Eclipseのセットアップ: 再生リスト
  4. LWJGLGitBook Chapter1-7
  5. IntelliJ IDEA〜セットアップなど〜
  6. Java Coord〜Java + Discord〜
  7. Java Basic〜Javaの基本〜: 再生リスト
  8. JavaDocJavaDocの解釈〜
  9. ラズパイ(RPi)関連〜RPiで遊んでみる〜
  10. JavaミニGame〜Java Console Game〜
  11. Eclipseアプリ作成〜家計簿アプリ作成〜
  12. OpenCv〜環境構築〜
  13. OoenCV + JavaFX
  14. JavaFXでハローワールド〜OpenCVチュートリアル迄〜
  15. Git操作〜Git関連〜
  16. JUnitJavaで遊ぶならこれ!〜
  17. TensorFlow〜Kerasで機械学習〜

ワンポイントレッスン的な

  1. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  2. JavaFX LineChart 〜グラフを描く、ワンポイントレッスン的な〜
  3. ND4J ベクトル生成〜ワンポイントレッスン的な〜

Micro:bitで遊ぶ

  1. Micro:bit(Chibi:bit) での開発環境セットアップ
  2. Micro:bit スマホでプログラミング〜ブラウザにアクセス〜
  3. Micro:bit スマホでプログラミング〜Bluetoothの設定〜
  4. PHP JS 〜WebSocketもどきの作成〜
  5. PHP ServerSocket 〜レンタルサーバーでSocket受信〜
  6. Python websocket client〜WebSocket送信処理を作る〜
  7. Micro:bit Python 〜マイクロビット→PCへシリアル通信〜
  8. ターミナル(コマンド)を使う 〜FTPSを使ってファイルアップロード〜
  9. Microbit リファレンス 〜サイトを眺めてみる〜
  10. Microbitで遊んでみる
  11. Microbitで遊ぶ〜ボタンを押す〜
  12. Microbitで遊ぶ〜シリアル通信をする〜
  13. Java Microbit Serial data connect 〜シリアル通信〜※中途半端です
  14. Microbit Python 〜シリアル通信データを受け取る〜
  15. Microbit Python HTTP リクエストを飛ばす
  16. Microbit花火〜MicrobitからWebServerまでの旅〜

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Eclipse+Gluon(MobileApp)

  1. Eclipse Android〜Gluonでアプリを作る〜
  2. Eclipse Android〜Gluonでアプリを作る2〜
  3. Eclipse Android〜Gluonでアプリを作る3: 3Dモデル〜
  4. Eclipse Android〜Gluonでアプリを作る4: 普通に作る〜
  5. Eclipse Android〜Gluonでアプリを作る5: ゲーム部分を作る〜

LWJGL

  1.  Chapter1[外枠の表示のみ]
  2. Chapter2-1〜クラスの構成〜
  3. Chapter2-2〜インターフェースの使い方と詳細〜
  4. Chapter2-3〜GameEngineクラス(サンプルクラス)〜/li>
  5. Chapter2-4〜Windowクラス(サンプルクラス)〜
  6. Chapter3〜描画処理を読む〜
  7. Chapter4〜シェーダについて〜
  8. Chapter5-1〜レンダリングについて〜
  9. Chapter5-2〜レンダリング詳細〜
  10. Chapter6Projection(投影)
  11. Chapter7-1〜Cubeを作る〜
  12. Chapter7-2〜Texture〜
  13. Java 3DGame LWJGL Retry Lv1 〜動かして理解する〜
  14. Java 3DGame LWJGL Retry Lv2 〜動かして理解する2
  15. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3「負け越し」
  16. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4「黒星」
  17. Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5「引分け」
  18. Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6「白星」
  19. ava 3DGame LWJGL Retry Lv7 遊んでみる3〜全部テクスチャにする〜

IntelliJ IDEA

  1. IntelliJ IDEA 環境構築 〜インストールと起動〜
  2. IntelliJ IDEA GitGitリポジトリからクローン〜
  3. IntelliJ IDEA 使い方〜Git接続 Java起動 etc
  4. IntelliJ IDEA Jarファイルを作る
  5. IntelliJ IDEA 使い方〜Maven Projectの作成〜
  6. IntelliJ IDEA 使い方〜Mavenでライブラリを追加する〜
  7. IntelliJ IDEA 使い方〜Javaのコンパイルレベル設定〜
  8. IntelliJ IDEA Gradleセットアップ〜コマンド入力部の表示〜
  9. IntelliJ IDEA Discord Botを作る〜Gradle環境のセットアップ〜

Java Discord

  1. IntelliJ IDEA Discord Botを作る〜Gradle環境のセットアップ〜
  2. Java Discord セットアップ〜Hello Discord
  3.  Java Discord ピンポン〜Discordプログラム〜
  4. Java Discord Listener実装〜コマンドを好きなだけ追加しよう〜

Java Basic

  1. Java Basic Level 1 Hello Java
  2. Java Basic Level2 Arithmetic Calculate
  3. Java Basic Level3 About String class
  4. Java Basic Level 4Boolean
  5. Java Basic Level 5If Statement
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 Traning of If statement
  8. Java Basic Level8 How to use for statement
  9. Java Basic Level 8.5 Array
  10. Java Basic Level 9Training of for statement
  11. Java Basic Level 10 While statement
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1
  17. Java Basic JUnit 〜テストスイートの作り方〜

JavaDoc

  1. Java Doc 読解 System
  2. Java Doc 読解〜System.out
  3. Java Doc読解 System.In
  4. JavaDoc 読解 Filesクラス
  5. Java Doc読解 BufferedReader
  6. Java Doc 読解〜BufferedWriter
  7. Java Doc 読解 List JavaDocList その1〜
  8. Java Doc 読解 Map
  9. Java Doc Classloader

ラズパイ(RPi)関連

  1. ラズパイ SSH接続メモ
  2.  ラズパイ Under-voltage detected! 〜エラー対処〜
  3.  ラズパイ(CUI)セットアップ
  4.  RPi Settingup Wifi in CUI ~ラズパイ CUI Wifi接続~
  5. Memos about Settingup RPi ~使用したコマンドメモ~
  6. RPi and JavaFX sample of deployment 〜ラズパイにサンプルデプロイ〜
  7.  RPi JavaFX execution ~ラズパイ JavaFX自動起動~
  8. RPi Install Git 〜ラズパイにGitのインストール〜
  9. RPi Java Swing〜ラズパイにJava Swingアプリを起動する〜※失敗しています。。。」
  10. RPi Maven Install 〜ラズパイでMeven
  11. Install XFCE4 on RPi 〜ラズパイに高速軽量デスクトップインストール〜

<Java Step1〜ミニゲーム作成>

  1. Java Hello World はじめのプログラム Step1-1
  2. Java 四則演算 演算子 Step1-2
  3. Java データ型 変数の扱い方〜Step1-2-1
  4. Java Basic Booleanなどの意味 Step1_2_2
  5. Java Basic for Step1_3_1
  6. Java Basic While Step1_3_2
  7. Java Basic try catch Step1_3_3
  8. Java Basic クラスとは〜Step2_1
  9. Java Basic API Listの使い方 Step2_2
  10. Java Basic インターフェース Step2_3
  11. Java Basic ミニゲーム作成 Step3_1
  12. Java Basic ミニゲーム作成 Step3_2

 家計簿アプリ作成

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv33Dグラフ用Cube作り〜
  4. Eclipse アプリ作成 Lv43Dグラフ用Cubeに高さを与える〜
  5. Eclipse アプリ作成 Lv5〜惨敗:CubeTextureを貼る〜
  6. Eclipse アプリ作成 Lv7Cubeを日付順に並べる〜

Git

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

OpenCv

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java OpenCv Lv1 〜入門: 写真の表示〜
  3. Java OpenCV Lv2 〜画像を表示する〜
  4. Java OpenCV Lv3 〜画像の平滑化(smooth())
  5. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())
  6. Java OpenCV Lv3 〜画像に中央値フィルタ(medianBlur())
  7. Java OpenCV Lv4 〜画像の中身をみてみる〜
  8. Java OpenCV Lv5 Matクラスで描画処理〜
  9. Java OpenCV Lv6 Matクラスで背景から作成してみる〜
  10. Java OpenCV Lv7 MatクラスでEllipseしてみる〜
  11. Java OpenCV Lv9 〜画像編集「足し算」(cvAdd)
  12. Java OpenCV Lv9 〜画像編集「引き算」(cvSubtract)
  13. Java OpenCV Lv9 〜画像の掛け算〜
  14. Java OpenCV Lv10 〜行列演算Mat#submat()
  15. Java OpenCv Lv10〜画像の平均値をだす〜

OpenCv + JavaFX

  1. EclipseにSceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. Java OpenCv Lv1 〜入門: 写真の表示〜
  4. Java OpenCV Lv2 〜JavaFXでの画像表示〜
  5. Java OpenCv ビデオキャプチャ〜カメラからの入力を表示〜
  6. OpenCV tutorial〜ヒストグラム〜
  7. OpenCV tutorial 解析 〜ヒストグラム〜
  8. OpenCV tutorial 解析2 〜ヒストグラム計算〜
  9. OpenCV tutorial 〜フーリエ変換など〜

JavaFXでハローワールド〜OpenCVまで

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine〜

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜
  4. ND4J ベクトル生成〜ワンポイントレッスン的な〜
  5. JavaFX + ND4J 〜数学への挑戦:5グラフを描く〜
  6. JavaFX + ND4J 〜グラフ作成の準備〜
  7. JavaFX + ND4J 〜グラフ作成:とりあえず表示〜
  8. JavaFX グラフ描画〜AreaChartを使う〜
  9. JavaFX + ND4J〜数学への挑戦6:グラフの土台を作る〜

JUnit関連

  1. Java Basic JUnit 〜テストスイートの作り方〜

TensorFLowを学ぶ

  1. Tensorflow Keras 〜初めてのKeras
  2. Tensorflow Keras Errors”python is not installed as a framework.”
  3. Python Tensorflow 〜初めての人工知能(TensorFlowインストール)
  4. Tensorflow Keras〜初めのトレーニング_1
  5. Tensorflow Keras〜初めのトレーニング_2:前処理〜
  6. TensorFlow Keras〜テキストの分類〜
    1. TensorFlow Keras 実行結果〜テキストの分類〜
  7. Python TensorFlow tutorial〜チュートリアルを進めるコツ、ワンポイント〜
  8. TensorFlow Keras〜回帰、準備から予測まで〜
  9. TensorFlow Java 環境構築〜JavaでもTensorFlow〜

[rakuten ids="soukai:10640969"]