DrawServerTcp.java
package jp.ac.utsunomiya_u.is; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Scanner; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; public class DrawServerTcp { // DrawTaskのリスト private final ArrayList<DrawTask> drawTasks = new ArrayList<>(); public static void main(String[] args) { DrawServerTcp drawServer = new DrawServerTcp(); } public DrawServerTcp() { // Scannerクラスのインスタンス(標準入力System.inからの入力) try (Scanner scanner = new Scanner(System.in)) { System.out.print("DrawServerTcp (" + getMyIpAddress() + ") > Input server port > "); // ポート番号入力 int port = scanner.nextInt(); // スレッドプールの生成 ExecutorService executorService = Executors.newCachedThreadPool(); // ServerSocketクラスのインスタンスをポート番号を指定して生成 try (ServerSocket serverSocket = new ServerSocket(port)) { System.out.println("DrawServerTcp (" + getMyIpAddress() + ") > Started and Listening for connections on port " + serverSocket.getLocalPort()); while (true) { // ServerSocketに対する要求を待機し,それを受け取ったらSocketクラスのインスタンスからChatTaskを生成 DrawTask drawTask = new DrawTask(serverSocket.accept()); // ChatTaskのインスタンスをリストに保管 drawTasks.add(drawTask); // タスクの実行 executorService.submit(drawTask); } } catch (IOException ex) { Logger.getLogger(DrawServerTcp.class.getName()).log(Level.SEVERE, null, ex); } finally { // スレッドプールの停止 executorService.shutdown(); } } } /** * 自ホストのIPアドレス取得 * * @return 自ホストのIPアドレス */ private static String getMyIpAddress() { try { // 自ホストのIPアドレス取得 return InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException ex) { Logger.getLogger(DrawServerTcp.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * メッセージの同報通知(サーバ->クライアント) * * @param message メッセージ */ private synchronized void broadcast(String message) { // ChatTashのArrayListの各要素に対して drawTasks.forEach((drawTask) -> { // PrintWriter経由でメッセージ送信 drawTask.getPrintWriter().println(message); }); } private final class DrawTask implements Callable<Void> { // ソケット private Socket socket; // データ送信用Writer(サーバ->クライアント用) private PrintWriter writer = null; // データ受信用Reader(クライアント->サーバ用) private BufferedReader reader = null; /** * コンストラクタ * * @param socket Socketクラスのインスタンス */ DrawTask(Socket socket) { this.socket = socket; try { // Socket経由での書込用PrintWriter生成(サーバ->クライアント用) writer = new PrintWriter(socket.getOutputStream(), true); // Soket経由での読込用BufferedReader生成(クライアント->サーバ用) reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println("DrawServerTcp (" + getMyIpAddress() + ") > Accepted connection from " + socket.getRemoteSocketAddress()); } catch (IOException ex) { Logger.getLogger(DrawServerTcp.class.getName()).log(Level.SEVERE, null, ex); } } /** * PrintWriterのゲッタ * * @return PrintWriter */ public PrintWriter getPrintWriter() { return writer; } @Override public Void call() { try { String inputLine; // readerから一行読み込み while ((inputLine = reader.readLine()) != null) { // 受信文をそのまま同報通知 broadcast(inputLine); //System.out.println("DrawClientTcp (" + socket.getRemoteSocketAddress() + ") > " + inputLine); } } catch (IOException ex) { Logger.getLogger(DrawServerTcp.class.getName()).log(Level.SEVERE, null, ex); } finally { System.out.println("DrawServerTcp (" + getMyIpAddress() + ") > Terminated connection from " + socket.getRemoteSocketAddress()); try { // socket, reader, writerのclose if (socket != null) { socket.close(); } if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } } catch (IOException ex) { Logger.getLogger(DrawServerTcp.class.getName()).log(Level.SEVERE, null, ex); } } // ChatTaskのリストから自身を削除 drawTasks.remove(this); return null; } } }
DrawClientTcp.java
package jp.ac.utsunomiya_u.is; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javafx.application.Application; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.Stage; public class DrawClientTcp extends Application { // ルートノード private final Group root = new Group(); // ソケット private Socket socket = null; // データ送信用Writer private PrintWriter writer = null; // データ受信用Reader private BufferedReader reader = null; // 受信タスク private ReceiverTask receiverTask = null; @Override public void start(Stage primaryStage) throws Exception { // Stageのタイトル primaryStage.setTitle(getClass().getName()); // Stageのサイズ primaryStage.setWidth(800); primaryStage.setHeight(800); // Stageの終了ボタンが押下された時の対応 primaryStage.setOnCloseRequest((event) -> { exit(); }); // Sceneインスタンス生成 Scene scene = new Scene(root); // Scene上でマウスがドラッグされた時の動作 scene.setOnMouseDragged((event) -> { if (socket != null && socket.isConnected()) { // マウスの座標から文字列生成 String str = event.getX() + "," + event.getY(); for (int i = 0; i < 1000; ++i) { str += "," + event.getX() + "," + event.getY(); } // サーバに情報送信 writer.println(str); // マウス座標を中心とする赤い円を描画 root.getChildren().add(new Circle(event.getX(), event.getY(), 2, Color.RED)); } }); // StageにSceneを貼付け primaryStage.setScene(scene); // IPアドレスとポート番号設定用ペインを作成し,ルートに貼付け root.getChildren().add(new NetworkConfigurePane()); // Stageの表示 primaryStage.show(); } /** * ”退室”ボタンが押下された時の処理 */ private void exit() { // タスクをキャンセル receiverTask.cancel(); try { // SocketとReaderとWriterをclose if (socket != null) { socket.close(); } if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } } catch (IOException ex) { new Alert(Alert.AlertType.ERROR, "ソケットを閉じることが出来ません", ButtonType.OK).show(); } } private class NetworkConfigurePane extends GridPane { // 操作可能コンポーネント private final TextField textFieldServerIpAddress = new TextField("localhost"); private final TextField textFieldServerPortNumber = new TextField("10000"); private final Button buttonEnter = new Button("入室"); private final Button buttonExit = new Button("退室"); public NetworkConfigurePane() { // 各コンポーネントの生成 Label labelIpAddress = new Label("サーバIPアドレス:"); Label labelPortNumber = new Label("サーバポート番号:"); // 各コンポーネントの配置 GridPane.setConstraints(labelIpAddress, 0, 0); GridPane.setConstraints(textFieldServerIpAddress, 1, 0); GridPane.setConstraints(labelPortNumber, 0, 1); GridPane.setConstraints(textFieldServerPortNumber, 1, 1); GridPane.setConstraints(buttonEnter, 2, 1); GridPane.setConstraints(buttonExit, 3, 1); // GridPaneに各コンポーネント追加 getChildren().addAll(labelIpAddress, textFieldServerIpAddress, labelPortNumber, textFieldServerPortNumber, buttonEnter, buttonExit); // コンポーネント表示設定切替 setConnection(true); // buttonEnter.setOnAction((event) -> { try { // textFiledServerIpAddressテキストフィールドに入力された文字列からサーバIPアドレスを指定 InetAddress serverInetAddress = InetAddress.getByName(textFieldServerIpAddress.getText()); // textFieldServerPortNumberテキストフィールドに入力された文字列からサーバポート番号を指定 int serverPortNumber = Integer.valueOf(textFieldServerPortNumber.getText()); // Socket生成 socket = new Socket(serverInetAddress, serverPortNumber); // Socket経由での書込用PrintWriter生成(クライアント->サーバ用) writer = new PrintWriter(socket.getOutputStream(), true); // Soket経由での読込用BufferedReader生成(サーバ->クライアント用) reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); // スレッドプールをSingleThreadで生成 ExecutorService executorService = Executors.newSingleThreadExecutor(); // TaskReceiver生成 receiverTask = new ReceiverTask(); // タスク実行 executorService.submit(receiverTask); // スレッドプールを停止 executorService.shutdown(); } catch (UnknownHostException ex) { new Alert(Alert.AlertType.ERROR, "IPアドレスが不正です", ButtonType.OK).show(); } catch (NumberFormatException ex) { new Alert(Alert.AlertType.ERROR, "ポート番号が不正です", ButtonType.OK).show(); } catch (IOException ex) { new Alert(Alert.AlertType.ERROR, "サーバに接続出来ません", ButtonType.OK).show(); } if (socket != null && socket.isConnected()) { setConnection(false); } }); // ”退室”ボタンが押下された時の処理 buttonExit.setOnAction((event) -> { exit(); if (socket == null || socket.isClosed()) { setConnection(true); } }); } /** * 各コンポーネントの変更可否 * * @param connected 接続時を意味するフラグ */ private void setConnection(boolean connected) { textFieldServerIpAddress.setDisable(!connected); textFieldServerPortNumber.setDisable(!connected); buttonEnter.setDisable(!connected); buttonExit.setDisable(connected); } } /** * サーバからのメッセージを受信するためのタスク */ private class ReceiverTask extends Task<Void> { @Override protected Void call() throws Exception { String inputLine; while ((inputLine = reader.readLine()) != null) { // 受信データを","で分解 String[] position = inputLine.split(","); Platform.runLater(() -> { // 1番目の要素を中心のX座標,2番目の要素を中心のY座標とする青い円を描画 root.getChildren().add(new Circle(Double.valueOf(position[0]), Double.valueOf(position[1]), 2, Color.BLUE)); }); } return null; } } public static void main(String[] args) { launch(args); } }