サーバー
サーバーソケット
前回までは、URL クラスがネットワークの存在を隠蔽してくれましたが
この場合は http などの限られたプロトコルしか使用することができません
独自のプロトコルを作り、専用のアプリケーションを作りたい場合
やはり、これよりは幾分低水準なネットワーク API を利用する必要があります
ネットワーク上で対話的に情報を交換するには、パケット通信を行います
パケットとは、データグラムと呼ばれるサイズ制限のある情報で
パケットには、データ本体のほかに宛先やポート番号など、必要な情報が含まれます
送信する情報はデータグラムによって分割され、パケットとして送信されます
受信者はこれを受け取り、データを解析し、組み立てる必要があるのです
ゼロからネットワークをプログラムする場合、避けられない作業です
しかし、UNIX で普及したソケットと呼ばれる技術が現在は普及し
開発者はネイティブなビットを解析する作業から解放されました
情報の入出力は、ハードウェアを隠蔽して統一したインターフェイスを持つべきです
ソケットは、これを実現するために誕生し、Berkeley API として普及しました
Java のネットワーク API も、ソケットによる通信を行います
ネットワークソフトウェアを建築するとき、Java 開発者は
サーバーやクライアントの接続を新しく作成する必要がありますが
その後のデータのやり取りは、Java IO API を使って行うことができます
まず、この場では最初にサーバーの建築方法を説明します
サーバーは、クライアントからの接続を待ち、アクセスがあれば返答を行います
この講座では、サーバーを最初に作って、その後にクライアントをプログラムします
サーバーの動作テストは、多くのシステムでサポートされている Telnet を使いましょう
サーバーソケットは java.net.ServerSocket クラスを使います
サーバソケットは、ネットワークを介して要求が送られてくるのを待ち、
その要求に基づいて処理を行い、場合によっては要求元に結果を返す処理を行えます
public class ServerSocket extends Object
このクラスのコンストラクタは次のようなものがあります
public ServerSocket() throws IOException
public ServerSocket(int port) throws IOException
public ServerSocket(
int port , int backlog) throws IOException
public ServerSocket(
int port , int backlog ,
InetAddress bindAddr) throws IOException
port にはサーバーが新たに開くポート番号を指定します
port に 0 を指定した場合は開いている適当なポートを使います
指定したポートが開かれている場合は例外が発生します
backlog には接続要求可能な登録数を指定することができます
これはキューになっていて、サーバーは要求をキューから引き抜いて順に解決します
接続要求が backlog 以上になると、サーバーは接続拒否します
ただし、OS が許可している最大キューサイズ以上を指定しても意味はありません
bindAddr は、複数の IP を持つコンピュータでサービスを提供する場合
ローカル IP アドレスを指定し、そのアドレスでのみ接続要求を受け付けるようにします
さて、これでサーバーソケットの準備ができましたが、受付がいない状態です
そこで 次に ServerSocket.accept() メソッドを使います
public Socket accept() throws IOException
このメソッドを呼び出すと、サーバーはクライアントからの要求を待機します
accept() は処理をブロックするため、要求があるまで制御を返しません
クライアントから要求があれば、java.net.Socket を返します
これは、サーバーとクライアントの接続を管理するソケットクラスです
public class Socket extends Object
このクラスのインスタンスを使って、サーバーとクライアントが通信を行います
Socket コンストラクタは、クライアントがサーバーに接続する時に使います
これは、クライアントを解説する時に改めて紹介します
データを受信するには Socket.getInputStream() メソッドを
データを送信するには Socket.getOutputStream() を使います
public InputStream getInputStream() throws IOException
public OutputStream getOutputStream() throws IOException
getInputStream はソケット入力ストリーム InputStream を
getOutputStream は出力ストリーム OutputStream を返します
これで、後はファイル入出力と同じようにデータをやり取りすることができます
サーバーは、クライアントの要求に答え、処理が終了したのであれば
クライアントとのソケットを切断し、新しく accept() する必要があります
ソケットの切断は Socket.close() メソッドを使って閉じます
public void close() throws IOException
同様に ServerSocket クラスにも close() メソッドが存在します
サーバーを閉じると、accept() でブロックされているスレッドは例外を発生します
ServerSocket の close() はそれほど重要ではありませんが
Socker は、必ず処理が終了した時点で close() を呼び出すようにしましょう
import java.net.*;
import java.io.*;
public class Server {
public static void main(String args[]) {
try {
ServerSocket server = new ServerSocket(2000);
while(true) {
Socket connect = server.accept();
OutputStreamWriter out =
new OutputStreamWriter(connect.getOutputStream());
out.write("Kitty on your lap");
out.flush();
connect.close();
}
}
catch(Exception err) {
System.out.println(err);
}
}
}
このプログラムは、ポート番号 2000 を開き、接続を待ちます
接続要求があった場合、"Kitty on your lap" という文字列をクライアントに送ります
その後、プログラムはクライアントとの接続を遮断します
Microsoft Telnet を使ってアクセスしたところ、次のようになりました
これは、サーバーと同一のコンピュータで実験したものです
Microoft Telnet> open localhost 2000
接続中: localhost...
Kitty on your lap
ホストとの接続が切断されました。
続行するには何かキーを押してください...
ServerSocket() コンストラクタに 0 を渡した場合は
どのポートが使われているか分からないので、アクセスすべきポートが分かりませんし
サーバーがどのアドレスを使っているのかも、確実に分かる方法があると便利です
ServerSocket.getLocalPort() を使えばローカルホストのポートが
ServerSocket.getInetAddress() を使えばアドレスが得られます
public int getLocalPort()
public InetAddress getInetAddress()
これで、サーバー管理者に必要な情報を伝えることができます
ただし、getInetAddress() よりは InetAddress.getLocalHost() を使うべきです
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();
OutputStreamWriter out =
new OutputStreamWriter(connect.getOutputStream());
out.write("Kitty on your lap");
out.flush();
connect.close();
}
}
catch(Exception err) {
System.out.println(err);
}
}
}
このプログラムは、サーバー起動時にポートとアドレスを表示します
これで、IP アドレスを使ってアクセスしたり、動的にポートを割り当てることができます
因みに、受信待ち状態でも他の処理や監視を行っていたい場合は
accept() メソッドの呼び出しを他のスレッドで行います