Sockets プログラミング

Sockets プログラミングを Java で行う方法について記述します。ここでは、TCP を用いたプログラミング方法を記述します。 Sockets に関するクラスは、java.net パッケージに含まれています。

ソケットをオープンする方法

クライアントプログラムを作成する場合:

Socket MyClient;
MyClient = new Socket("HostName", PortNumber);

"HostName"は、接続を開始しようとしているサーバー名を指定し、PortNumber は、 そのサーバーのどのポート番号を利用するかを指定します。ポート番号を選択する場合、 特権ユーザー(スーパーユーザーまたはルート)のために 0から 1,023のポート番号が予約されています。 これらのポート番号は、電子メール、FTPおよびHTTPのような標準サービスのためのものです。 もし、自分で作成するサーバーのためにポート番号を選ぶなら、1,024以降を選択してください。

本来は、例外処理を扱うような記述にしなくてはならないので以下のようになります。

Socket MyClient;
try {
    MyClient = new Socket("HostName", PortNumber);
}
catch (IOException e) {
    System.out.println(e);
}

サーバープログラムを作成する場合:

ServerSocket MyService;
try {
    MyService = new ServerSocket(PortNumber);
}
catch (IOException e) {
    System.out.println(e);
}

サーバープログラムを作成する場合、クライアントからの接続要求を受理するために受け付ける PortNumber を指定して ServerSocketオブジェクトを作成する必要があります。 オブジェクトを作成したら、クライアントからの要求を受け付けるソケットを以下のように作成します。

Socket serviceSocket = null;
try {
    serviceSocket = MyService.accept();
}
catch (IOException e) {
    System.out.println(e);
}

入力ストリームを作成する方法

クライアント側で、サーバからのデータを受け取る入力ストリームを作成するために DataInputStreamクラスを使用することができます。

DataInputStream input;
try {
    input = new DataInputStream(MyClient.getInputStream());
}
catch (IOException e) {
    System.out.println(e);
}

DataInputStreamクラスは、テキストやプリミティブなデーターをオープンしたソケットから 読むことができます。メソッドとして read, readChar, readByte などがあります。 以前は readLine というメソッドがあったのですが、現在は推奨されていません。BufferedReaderクラスの readLineメソッドを利用するようにします。同じように、サーバ側でクライアントからデータを受け取るためにDataInputStreamクラスを使用する ことができます。

DataInputStream input;
try {
    input = new DataInputStream(serviceSocket.getInputStream());
}
catch (IOException e) {
    System.out.println(e);
}

出力ストリームを作成する方法

クライアント側で、PrintStreamクラスやDataOutputStreamクラスを使用してサーバーへデータを送るために出力ストリームを作成することができます。

PrintStream output;
try {
    output = new PrintStream(MyClient.getOutputStream());
}
catch (IOException e) {
    System.out.println(e);
}

PrintStreamクラスのような、テキストを画面に出力するクラスを用いることができます。 DataOutputStreamクラスを利用すには以下のようにします。

DataOutputStream output;
try {
    output = new DataOutputStream(MyClient.getOutputStream());
}
catch (IOException e) {
    System.out.println(e);
}

DataOutputStreamクラスは、テキストやプリミティブなデーターをオープンしたソケットへ 出力することができます。メソッドとして write, writeByte, writeBytes, writeChar, writeChars,などがあります。 同じように、サーバ側でクライアントへデータを出力するためにPrintStreamクラスやDataOutputStreamクラスを使用する ことができます。

DataOutputStream output;
try {
    output = new DataOutputStream(serviceSocket.getOutputStream());
}
catch (IOException e) {
    System.out.println(e);
}

ソケットをクローズする方法

ソケットをクローズする前に、出力と入力ストリームを閉じる必要があります。

クライアントプログラムの場合:

try {
    output.close();
    input.close();
    MyClient.close();
}
catch (IOException e) {
    System.out.println(e);
}

サーバープログラムの場合:

try {
    output.close();
    input.close();
    serviceSocket.close();
    MyService.close();
}
catch (IOException e) {
    System.out.println(e);
}

サンプルプログラム

サーバー、クライアントの単純なプログラムを作って動きを見て見ましょう。あまり、よいプログラムではありませんが全体の流れを理解することはできると思います。

サーバープログラムは、クライアントからのメッセージをポート9999番で待ち、受け取ったメッセージをそのままクライアントに返すだけのものです。

import java.io.*;
import java.net.*;
 
public class echoServer {
    public static void main(String args[]) {
        // ソケットや入出力用のストリームの宣言
        ServerSocket echoServer = null;
        String line;
        BufferedReader is;
        PrintStream os;
        Socket clientSocket = null;
 
        // ポート9999番を開く
        try {
            echoServer = new ServerSocket(9999);
        }
        catch (IOException e) {
            System.out.println(e);
        }
 
        // クライアントからの要求を受けるソケットを開く 
        try {
            clientSocket = echoServer.accept();
            is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            os = new PrintStream(clientSocket.getOutputStream());
 
            // クライアントからのメッセージを待ち、受け取ったメッセージをそのまま返す
            while (true) {
                line = is.readLine();
                os.println(line); 
            }
        }
        catch (IOException e) {
            System.out.println(e);
        }
    }
}

クライアントプログラムは、ポート9999番に対してメッセージを送りサーバーからのメッセージを受け取って終了します。

import java.io.*;
import java.net.*;
 
public class echoClient {
    public static void main(String[] args) {
        // ソケットや入出力用のストリームの宣言
        Socket echoSocket = null;
        DataOutputStream os = null;
        BufferedReader is = null;
 
        // ポート9999番を開く
        try {
            echoSocket = new Socket("localhost", 9999);
            os = new DataOutputStream(echoSocket.getOutputStream());
            is = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: localhost");
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to: localhost");
        }
 
        // サーバーにメッセージを送る
        if (echoSocket != null && os != null && is != null) {
            try {
                // メッセージを送ります
                os.writeBytes("HELLO\n"); 
 
                // サーバーからのメッセージを受け取り画面に表示します
                String responseLine;
                if ((responseLine = is.readLine()) != null) {
                    System.out.println("Server: " + responseLine);
                }
 
                // 開いたソケットなどをクローズ
                os.close();
                is.close();
                echoSocket.close();
            } catch (UnknownHostException e) {
                System.err.println("Trying to connect to unknown host: " + e);
            } catch (IOException e) {
                System.err.println("IOException: " + e);
            }
        }
    }
}

コンパイルと実行

サーバー側では、ソースをコンパイルしてプログラムを実行します。

# javac echoServer.java
# java echoServer

クライアント側では、ソースをコンパイルして、サーバーが動いているのを確認してからプログラムを実行します。

# javac echoClient.java
# java echoClient
Server: HELLO
#

クライアントから送ったメッセージがサーバから返ってきたことが確認できます。このサンプルでは、クライアントのプログラムで接続するホストを localhost にしているので、サーバー、クライアントプログラムともに同一ホストで実行してください。