Java ソースコード GUIserver.java
ソケット通信GUIサーバーサンプル(一部)。
ソケット通信GUIクライアントサンプルとあわせてお読みください。サーバーの実装の際、accept()によるブロック状態から待受けを停止するにはどうしたらいいかという 問題の解決方法の一つとして、accept()が含まれるループの終了フラグをセットしたのち、自分自身に接続して accept()の次の処理に進ませ、ループを終了させるといいう方法もあります。以下のサンプルではこの方法で accept()のブロック状態から抜け出るようにしています。
Socket socket = new Socket("127.0.0.1", port);
//すぐに閉じる
socket.close();
という感じですね。
TCP/IPソケットプログラミング Java編
↑この本はJavaの通信プログラムの基本理解に手頃です。あまり厚くないのでとっつきやすい。
//このプログラムは、複数クライアントからの接続を受け付け、 //接続したクライアントにメッセージを1行送信します。 //その後、クライアントから送信された文字データを受信すると、同じ内容を //送り返すという動作を繰り返します。 //送られてきた文字列が「!quit」であった場合には通信を終了します。 // //受信を繰り返す場合に、BufferedReaderのreadLine()メソッドを使うと、 //そこで動作がブロックされてしまうため、アプリケーションがスムースに //動くよう対策を施しています。 //このプログラムは、GUIclient.javaをクライアントとして動作します。 //プログラム作成にはNetbeans7.1を使っています。 //GUIデザインに関するソース部分は省略し、ソケット通信等に関する部分 //のみ示しています。GUIデザインはNetBeansで行っています。 //なお、ポート番号はportTextField、 // 接続総数の表示はjLabelConnectCount、 // 動作状況表示はjLabelInfo1、 // サーバーに送信する文字列入力はjTextServerMessage、 // サーバーとの通信内容Log表示はjTextAreaLog //の各GUIコンポーネントを使用しています。 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; import java.awt.event.*; import java.io.InputStream; import java.io.PrintStream; public class GUIserver extends javax.swing.JFrame implements ActionListener{ ServerSocket svSocket = null; static String quitcmd="!quit"; boolean runflag=true; int port; int connectCount=0; ChkThread chkth; public GUIserver() { initComponents(); port=Integer.parseInt(portTextField.getText()); jButtonStop.setEnabled(false); jButtonStart.setEnabled(true); } public void actionPerformed(ActionEvent e){ } private void jButtonStartActionPerformed( java.awt.event.ActionEvent evt) { // 待受け開始 connectCount=0; jLabelConnectCount.setText(String.valueOf(connectCount)); runflag=true; chkth=new ChkThread(); //待受けもスレッドで行う chkth.start(); jButtonStart.setEnabled(false); jButtonStop.setEnabled(true); } private void jButtonStopActionPerformed( java.awt.event.ActionEvent evt) { jButtonStart.setEnabled(true); jButtonStop.setEnabled(false); runflag=false; halt(); chkth=null; jButtonStart.setEnabled(true); } public void halt() { try { //accept()を回すために自分自身に接続 Socket socket = new Socket("127.0.0.1", port); //すぐに閉じる socket.close(); connectCount--; } catch (IOException e) { e.printStackTrace(System.out); } } //クライアントからの接続待ちを行うために、ServerSocketクラスの //accept()を使いますが、このメソッドは現在のスレッドをブロック //する(現在のスレッドの処理を停止状態にする)ため、メインスレッド //内で使うと、GUIのボタン操作などができなくなってしまいます。 //そこでスレッドを新たに作って、その中で //accept()による待受けを行います。こうすることで待受け中でも、ボタン //クリックに対応するなどの処理が行えます。 class ChkThread extends Thread{ //待受け用スレッド public void run(){ try { svSocket = new ServerSocket(port); jTextAreaLog.append("待受中. "+ "\n"); System.out.println("待受中. "); jLabelInfo1.setText("待受中."); while(runflag){ //接続待ち受けループ Socket socket; socket = svSocket.accept(); //accept()は、クライアントからの接続要求があるまで処理を //ブロックする。 //接続があれば次の命令に移る。 //ここでは新たにスレッドを起動し、クライアントと通信する。 connectCount++; jLabelConnectCount.setText(String.valueOf(connectCount)); //接続先との通信はスレッドを起動して行う new SrvThread(socket,connectCount).start(); } //runflagがfalseの場合、待受けループを終了、ソケットを閉じる //runflagをメインスレッドなどでfalseに変化させることで、 //待受けループを終了させる。 //ただし、whileループを回すためには、まず実行中のaccept()の //ブロックを解除する必要がある。accept()は接続が行われたときに //ブロックが解除されるので、接続待ちを終了するためには自分自身 //に接続することでaccept()の次の処理に進ませることができる。 svSocket.close(); } catch (IOException e) { System.out.println("ChkThread:"+e); } jTextAreaLog.append("停止中. "+ "\n"); System.out.println("停止中. "); jLabelInfo1.setText("停止中."); } //End of run() } //End of class ChkThread class SrvThread extends Thread{ //GUIserverの内部クラスとして定義 Socket soc; String response; PrintStream outStream; BufferedReader inStream = null; InputStream inpStream = null; InputStreamReader inprStream = null; int num; public SrvThread(Socket sct, int n){ soc=sct; num=n; jTextAreaLog.append("["+num+"] Connect to " + soc.getInetAddress() + "\n"); System.out.println("["+num+"] Connect to " + soc.getInetAddress()); } public void run(){ try { boolean loopflag=true; outStream = new PrintStream(soc.getOutputStream()); inpStream = soc.getInputStream(); inprStream =new InputStreamReader(inpStream); inStream = new BufferedReader(inprStream); outStream.println(jTextServerMessage.getText()); outStream.flush(); jTextAreaLog.append("->["+num+"] " + jTextServerMessage.getText() + "\n"); // クライアントからのメッセージを待ち、 // 受け取ったメッセージをそのまま返す while (loopflag) { while(inpStream.available()!=0){ response = inStream.readLine(); System.out.println("<-["+num+"] " + response); jTextAreaLog.append("<-["+num+"] " + response + "\n"); outStream.println(response); outStream.flush(); jTextAreaLog.append("->["+num+"] " + response + "\n"); if(response.equals(quitcmd)){ loopflag=false; break; } } Thread.sleep(200); // CPUの負荷を軽減するためのsleep200ミリ秒 } inStream.close(); inprStream.close(); inpStream.close(); outStream.close(); soc.close(); jTextAreaLog.append("["+num+"] 通信スレッド停止. "+ "\n"); System.out.println("["+num+"] 通信スレッド停止. "); connectCount--; jLabelConnectCount.setText(String.valueOf(connectCount)); } catch (IOException | InterruptedException e) { System.out.println(e); } //End of Deffinition of SrvThread (Inner class of GUIserver class.) } } }
動作画面例
下の画像は、クライアントを3つ同一PC上で接続した実行時の画像です。
Kom., 2013