クライアント


サーバーへのアクセス

前回は、要求者に文字列を返して切断するだけの単純なサーバーを作りました
次は、サーバーにアクセスするクライアントを作成しましょう
プログラムがサーバーにアクセスするためには Socket クラスを使います
Socket() コンストラクタは次のようなものがあります
public Socket()

protected Socket(SocketImpl impl)
		throws SocketException

public Socket(String host , int port)
	throws UnknownHostException , IOException

public Socket(InetAddress address ,
		int port) throws IOException

public Socket(String host , int port ,
	InetAddress localAddr ,
		int localPort) throws IOException

public Socket(InetAddress address , int port ,
		InetAddress localAddr ,
		int localPort) throws IOException
基本的にパラメータ無しの Socket() と impl を受ける protected コンストラクタは
Socket クラスをサブクラス化し、特殊なソケットを実現するために使います

host は接続するホスト名、port は接続するポート番号を指定します
address は InetAddress が表すアドレスを使ってホストに接続します
localAddr には、ローカルホストのアドレスを、localPort にはポートを指定します

localAddr を指定する場合は、ローカルのどのネットワークインターフェイスを使うか
どのローカルポートを利用するかということを明確に記述することができるため
特定のコンピュータ専用のクライアント・ソフトウェアを開発する場合に使えます
localPort に 0 を指定した場合は、開いているポートを利用します

DNS がホスト名を解決できない場合や、ソケットを開けない場合は
コンストラクタは例外を発生させます

ソケットを作成し、リモートホストとの接続が完了すれば
後は、前回のサーバーで説明したように Socket.getInputStream() などを使い
ソケットの入出力を JAVA IO API を使って実現することができます
import java.net.*;
import java.io.*;

public class Test {
	public static void main(String args[]) {
		try {
			Socket sock = new Socket(
				args[0] , Integer.parseInt(args[1]));
			LineNumberReader in = new LineNumberReader(
				new InputStreamReader(sock.getInputStream()));
			String msg = in.readLine();
			in.close();
			sock.close();

			System.out.println(msg);			
		}
		catch(Exception err) {
			System.out.println(err);
		}
	}
}
コマンドライン引数でリモートホスト名とポートを指定して実行してください
プログラムは指定したホストにアクセスし、ソケットを 1 行分読み込みます
前回作成したサーバーに接続すると、"Kitty on your lap" という文字が出力されます

Socket クラスには、ローカルとリモートの情報を取得するメソッドがあります
ローカルの IP は Socket.getLocalAddress() メソッドを使って
ローカルポートは Socket.getLocalPort() メソッドを使って得られます

public InetAddress getLocalAddress()
public int getLocalPort()

接続先アドレスは Socket.getInetAddress() メソッドを
接続先のポートは Socket.getPort() メソッドを使って取得します

public InetAddress getInetAddress()
public int getPort()

これらを使えば、どこの誰が接続したのかを管理することができます
接続した時間などを付属すれば、サーバーのログを残すことが可能になるでしょう
import java.net.*;
import java.io.*;

public class Server {
	public static void main(String args[]) {
		try {
			ServerSocket server = new ServerSocket(0);

			System.out.println("port : " + server.getLocalPort());
			System.out.println("Addr : " + InetAddress.getLocalHost());

			while(true) {
				Socket connect = server.accept();
				InetAddress addr = connect.getInetAddress();
				System.out.print("アドレス-" + addr.getHostAddress());
				System.out.print(" : ホスト名-" + addr.getHostName());
				System.out.println(" : ポート-" + connect.getPort());

				OutputStreamWriter out =
					new OutputStreamWriter(connect.getOutputStream());
				out.write("Kitty on your lap");
				out.flush();
				connect.close();
			}
		}
		catch(Exception err) {
			System.out.println(err);
		}
	}
}
このプログラムは、前回のサーバープログラムを改良したものです
クライアントからのアクセスがあると、標準出力に接続者の情報を表示します
...>java Server
port : 1420
Addr : RENA/192.168.0.2
アドレス-127.0.0.1 : ホスト名-localhost : ポート-1421
アドレス-127.0.0.1 : ホスト名-localhost : ポート-1422
サーバーを実行し、クライアントプログラムを使ってアクセスした結果です


対話処理

これまでは、一方からの入力や出力を行ってきましたが
サーバーとクライアントが対話することも、もちろん可能です
対話は、一方が入力を待ち、もう一方が出力を行う関係を交互に繰り返します

何らかの規約を定め、それをプロトコルとすれば
サーバーとクライアントが専門の処理を行い、情報を交換することができます

次のプログラムは、簡易一行 Echo サーバーです
echo プロトコルは、クライアントからの入力をそのまま返すというものです
サーバーは null か "" を与えられるとソケットを切断します
import java.net.*;
import java.io.*;

public class Server {
	public static void main(String args[]) {
		try {
			ServerSocket server = new ServerSocket(7);

			System.out.println("port : " + server.getLocalPort());
			System.out.println("Addr : " + InetAddress.getLocalHost());

			while(true) {
				Socket connect = server.accept();
				BufferedReader in = new BufferedReader(
					new InputStreamReader(connect.getInputStream()));
				OutputStreamWriter out =
						new OutputStreamWriter(connect.getOutputStream());
				while(true) {
					String echo = in.readLine();
					if (echo == null || echo.equals("")) break;
					
					out.write(echo + '\n');
					out.flush();
				}
				connect.close();
			}
		}
		catch(Exception err) {
			System.out.println(err);
		}
	}
}
次に、上で作成したサーバー専用の echo クライアントを作ります
クライアントは 1 行単位で文字列をサーバーに送信します
すると、即座にサーバーから反応があるので、これを標準出力に表示します
import java.net.*;
import java.io.*;

public class Test {
	public static void main(String args[]) {
		try {
			Socket sock = new Socket(args[0] , Integer.parseInt(args[1]));
			BufferedReader stdin = new BufferedReader(
				new InputStreamReader(System.in));
			BufferedReader in = new BufferedReader(
				new InputStreamReader(sock.getInputStream()));
			PrintWriter out = new PrintWriter(sock.getOutputStream());

			while(true) {
				System.out.print(">");
				String send = stdin.readLine();
				if (send.equals("")) break;

				out.write(send + '\n');
				out.flush();

				String msg = in.readLine();
				System.out.println(msg);
			}
			sock.close();
		}
		catch(Exception err) {
			System.out.println(err);
		}
	}
}
クライアントは、コマンドライン引数でホスト名とポートを指定してください
上記したサーバーに接続できれば、標準入力から入力を要求します

サーバーもクライアントも十分な例外処理を行っていないため
セキュリティは極めて微弱なので実用には耐えられません
しかし、このプログラムで、サーバーとクライアントの対話方法が理解できるはずです

サーバーはクライアントと対話を行っている間、接続を受理できません
複数の接続を処理することができる本格的なサーバーを作る場合は
接続ごとにスレッドを作るか、子プロセスを生成する必要があります



前のページへ戻る次のページへ