テキストチャット
簡単な Chat プログラム
さっきは、一方向でデータ通信してみましたが、今度は、双方向に通信してみましょう。
作成するのは、簡単なチャットです。
- ChatServer が、指定ポートで待っている
- ChatClient が、ホスト名とポート番号を指定して接続を行う
- 接続完了後は、クライアント、サーバが交互に一行ずつメッセージを交換する。
- どちらかが exit という入力を入れたら終了
動作イメージは、こんな感じ
server 側 client 側
[起動]
Server waitting on serverSocket..
[起動]
Connecting to localhost/....
Connection established Connection established
Client Input> clientMsg0 [入力]
Client msg: clientMsg0 [受信]
Server Input> serverMsg0 [入力]
Server msg: serverMsg0 [受信]
Client Input> client3 [入力]
Client msg: client3 [受信]
Server Input> exit [入力]
normal termination Server msg: exit [受信]
normal termination
まずは、動かしてみて、イメージを掴んでください。
動きを把握したところで、接続確立・交互に通信のあたりを見ていきましょう。
接続
connection を張るまでは、先ほどの例と差はありません。 違うのは、今回は双方向通信なので、Server, Client 両側で、Input/OutputStream を両方開いている点ですね。
サーバ側
ServerSocket serverSocket = new ServerSocket(portNum);
System.out.println("Server waitting on serverSocket " + serverSocket);
Socket socket = serverSocket.accept(); /* 接続待ち */
System.out.println("Connection established"); /* 接続完了 */
try {
receiveAndSend(new Scanner(socket.getInputStream()),
new PrintWriter(socket.getOutputStream()));
} finally {
socket.close();
serverSocket.close();
}
- 接続開始〜確立
- socket から Input/OutputStream を開いて、処理を
receiveAndSend(in,out)
に任せる- 今回は、テキスト形式で送受信するため、in, out の処理は以下のクラスを用いる
- Scanner in: 一行単位読み込み(nextLine())用
- java.io.PrintWriter out: 一行単位書き込み(println())用
- receiveAndSend() の中身は後で紹介
- 今回は、テキスト形式で送受信するため、in, out の処理は以下のクラスを用いる
- 最後に、socket のclose()
クライアント側
/* IP address の取得 */
InetAddress host = InetAddress.getByName(hostName);
/* Socket の生成 */
System.out.println("Connecting to " + host + ":" + portNum);
Socket socket = new Socket(host, portNum);
System.out.println("Connection established");
try {
sendAndReceive(new Scanner(socket.getInputStream()),
new PrintWriter(socket.getOutputStream()));
} finally {
socket.close();
}
- 接続開始〜確立
- socket から Input/OutputStream を開いて、処理を receiveAndSend(in,out)に任せる
- in, out の形式は、サーバ側と同じ
- sendAndReceive() の中身は後で紹介
- 最後に socket を close()
交互に送受信
サーバ(受信・送信を交互に)
while (true) {
String recvMsg = netIn.nextLine(); // ネット受信
System.out.println("Client says: " + recvMsg); //表示
if (recvMsg.equals("exit")) // exit なら終了へ
break;
System.out.print("Server Input> ");
System.out.flush();
String userMsg = userIn.nextLine(); // ユーザ入力
netOut.println(userMsg); // ネット送信
netOut.flush();
if (userMsg.equals("exit")) // exit なら終了へ
break;
}
System.out.println("normal termination");
主なI/O
- netIn: Scanner, クライアントからの受信用
- netOut: PrintWriter, クライアントへの送信用
- userIn: Scanner, ユーザ入力の取り込み用
プログラム見てもらえば、交互に送受信しているだけですね。
注意点
としては、送信時に flush()
している点です。これは、送信したつもりでバッファに残っていたりすると、
- 送信側:送ったつもり(実はバッファに溜まっている)
- 受信側:受け取り待ち
とかなって、処理が進まなくなってしまうからです。(java.io.PrintWriter の自動フラッシュ機能をON にしている場合は、println()
がflush()
もしてくれます。自動フラッシュを ON にするコンストラクタもあるので、そっちを使ってもらっても構いません。)
クライアント(受信・送信を交互に)
受信と送信の順序が逆になっている以外は、処理の流れはまったく同じです。
while (true) {
System.out.print("Client Input> ");
System.out.flush();
String userMsg = userIn.nextLine(); // ユーザ入力
netOut.println(userMsg); // ネット送信
netOut.flush();
if (userMsg.equals("exit")) // exit なら終了へ
break;
String recvMsg = netIn.nextLine(); // ネット受信
System.out.println("Server says: " + recvMsg); //表示
if (recvMsg.equals("exit")) // exit なら終了へ
break;
}