クライアント
サーバーへのアクセス
前回は、要求者に文字列を返して切断するだけの単純なサーバーを作りました
次は、サーバーにアクセスするクライアントを作成しましょう
プログラムがサーバーにアクセスするためには 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);
}
}
}
クライアントは、コマンドライン引数でホスト名とポートを指定してください
上記したサーバーに接続できれば、標準入力から入力を要求します
サーバーもクライアントも十分な例外処理を行っていないため
セキュリティは極めて微弱なので実用には耐えられません
しかし、このプログラムで、サーバーとクライアントの対話方法が理解できるはずです
サーバーはクライアントと対話を行っている間、接続を受理できません
複数の接続を処理することができる本格的なサーバーを作る場合は
接続ごとにスレッドを作るか、子プロセスを生成する必要があります