【Java】TCPサーバのJavaプログラムを作る① | 若手エンジニアのブログ

若手エンジニアのブログ

文系出身の若手女子エンジニアによる技術ブログ。
日々の経験や学びをアウトプットするためにブログを書いています。
バックエンド(Java+SpringFramework)を経てインフラエンジニアになりました。
今は育休中につき、本で勉強したことを中心にアウトプットしています。

お久しぶりです。

プライベートでバタバタしていたのですが、ようやく落ち着いてきたので、

ブログ更新再開したいと思います!

 

さて、お仕事にて、TCPサーバの動きをするプログラムをJavaで作る機会があったのですが、

なかなかに苦戦しておりまして(´・ω・`)

久しぶりの記事は、Javaにしたいと思います◎

 

もくじ

1.今回やること

2.環境

3.プログラムのイメージ

4.サーバプログラム

5.クライアント

6.実践

7.今後の課題

参考サイト様

 

1.今回やること

 ・Javaプログラムで簡易TCPサーバをつくる。

 ・クライアントは複数存在し、それぞれがTCPサーバへ接続する。

 ・サーバプログラムは、クライアントから電文を受信するたびに、応答を返す。

 

 

 

2.環境

以下のバージョンで動かしてます。(アップデートせねば。。)

 >java -version
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

 

 

3.プログラムのイメージ

TCPサーバ側のプログラムでは、java.net.ServerScoketクラスを用いることで、

TCP通信を受け入れる体制を作ります。

ただしServerSocketインスタンスは、それだけでは1回分の通信受け入れしかできません。

が、実際のTCPサーバは複数のクライアントからの要求を並行して処理するのが普通です。

 

 

そこで、java.lang.Threadクラスを用いて、マルチスレッドで複数のServerSocketインスタンスを作って動かし、複数のTCP接続要求がクライアントから来ても対応できるようなつくりにします。

 

 

 

ではさっそく実装していきます。

 

4.サーバプログラム

まずはサーバプログラム。

クライアントからのTCP接続要求を待ち、要求があれば接続毎にスレッドを作って、

クライアントからの電文内容の取得&応答電文の返却処理を行っています。

 

TCPServer.java

import java.net.*;

import java.io.*;

 

/** TCP要求を待ち受けるサーバプログラム */

public class TCPServer {

 

    //接続待ち受けポート。使われていないポートであればなんでもOK。

    public static final int TEST_PORT = 8001;

 

    public static void main(String args[]) {

        ServerSocket serverSocket = null;

 

        try {

            serverSocket = new ServerSocket(TEST_PORT);

            while (true) {

                System.out.println("接続待ち受け中");

                Socket socket = serverSocket.accept();

                new TCPThread(socket).start();

            }

        } catch (IOException e) {

            e.printStackTrace();

        } finally {

            try {

                if (serverSocket != null) {

                    serverSocket.close();

                    System.out.println("接続待ち受け終了");

                }

            } catch (IOException e) {}

        }

    }

}

 

/** java.lang.Threadを継承したクラスを定義 */

class TCPThread extends Thread {

 

    private Socket socket;

    public TCPThread(Socket socket) {

        this.socket = socket;

        //リモートポート等の情報を出力。スレッド毎にリモートポートは異なる

        System.out.println(socket.getRemoteSocketAddress());

    }

 

    //Threadのrunメソッドを継承し、スレッド毎に行う処理を記述する

    public void run() {

        try (FileOutputStream request = new FileOutputStream("s_out.txt");

            FileInputStream response = new FileInputStream("s_response.txt") ) {

    //s_response.txt(応答電文の中身)は、あらかじめ作成して配置しておく。

 

            InputStream input = socket.getInputStream();

            OutputStream output = socket.getOutputStream();

            int num;

 

            //クライアントからの受信内容をファイルに書き出す

            while ((num = input.read()) != -1 ) {

                request.write(num);

            }

 

            //クライアントへ応答

            while ((num = response.read()) != -1 ) {

                output.write(num);

            }

            System.out.println("スレッド終了");

 

        } catch (IOException e) {

            e.printStackTrace();

        } finally {

            try {

                if (socket != null) {

                    socket.close();

                }

                } catch (IOException e) {}

        }

    }

}

 

ちなみにソースの中にある、「s_response.txt」は、応答電文の中身として、

hello

と記述し、javaソースと同じディレクトリに配置しておきました。

TCPサーバはクライアントからTCP接続を受けると、「hello」と返す仕組みになっています。

 

5.クライアントの準備

クライアント用プログラムもJavaで作ろうかと思ったんですが、

本筋とズレるので手抜きしてツールを使います(^-^;

今回はJMeterを使いました。

 

スレッド数3で、各スレッドで3回同じ電文を送信する設定(つまり9回繰り返し)としました。

 

今回はローカルで動かしているTCPサーバプログラムに向けて通信することと、

ポート番号は8001を使っていることから、localhost:8001に通信する設定としました。

ちなみに「Re-use connection」のチェックは外しています。

 

 

6.実践!

TCPサーバのJavaプログラムを実行したうえで、

先ほどのJMeterも実行します。

 

結果…

合計9回接続しましたが、それぞれにローカルポートも割り当てられていることが分かります。

 

JMeterの結果を見ると、それぞれのリクエスト毎に応答が返ってきていることを確認できました。

 

7.今後の課題

これでめでたしめでたし、と言いたいところですが…。

 

ここまでの例では、

「TCP接続→クライアントからサーバに送信→サーバからクライアントに応答→切断」

を9回繰り返したことになります。

TCPサーバプログラムで、スレッドが9個作られ、それぞれで処理をしているため、

電文送信毎に、接続を切断する流れとなっているということです。

 

 

が、、、、

JMeterの設定を再掲すると、クライアントとしてはスレッドは3つ、その中で処理ループが3つのはずでした。

 

これを忠実に再現するならば、9回とも切断するのではなく、

「TCP接続→送信と応答を3回繰り返し→切断」

という流れが3回行われないといけないはず。(切断はスレッド毎に、合計3回のみとしたい)

 

 

じゃあどうやったらいいのか??

…良い案を思いついていないので、もう少し調べてみたいと思います(´・ω・`)

 

 

■参考サイト様

・TECHSCORE様記事

  https://www.techscore.com/tech/Java/JavaSE/Network/3-2/

 

・「TCPサーバ/クライアントを作る」

  http://kmaebashi.com/programmer/webserver/tcpserver.html