メインスレッドには、ServerSocketオブジェクトでwhile(listening)
を呼び出すaccept()
ループがあり、新しいクライアントスレッドを開始して、新しいクライアントが受け入れられたときにコレクションに追加します。
また、「exit」などのコマンドを発行するために使用する管理スレッドもあります。これは、falseをリッスンすることで、すべてのクライアントスレッドをシャットダウンし、自身をシャットダウンし、メインスレッドをシャットダウンします。
ただし、accept()
ループのwhile(listening)
呼び出しはブロックするため、それを中断する方法はないようです。そのため、while条件を再度チェックできず、プログラムを終了できません。
これを行うためのより良い方法はありますか?または、ブロック方法を中断する方法はありますか?
別のスレッドから close()
を呼び出すことができ、 accept()
呼び出しは SocketException
をスローします。
accept()
にタイムアウトを設定すると、呼び出しは指定された時間後にブロッキングをタイムアウトします。
http://docs.Oracle.com/javase/7/docs/api/Java/net/SocketOptions.html#SO_TIMEOUT
Socket
操作のブロックにタイムアウトを設定します。ServerSocket.accept(); SocketInputStream.read(); DatagramSocket.receive();
このオプションは、ブロック操作に入る前に設定する必要があります。タイムアウトが期限切れになり、操作が引き続きブロックされる場合、
Java.io.InterruptedIOException
が発生します。この場合、Socket
は閉じられません。
ServerSocket
でclose()
を呼び出すことはオプションですか?
http://Java.Sun.com/j2se/6/docs/api/Java/net/ServerSocket.html#close%28%29
このソケットを閉じます。現在accept()でブロックされているスレッドは、SocketExceptionをスローします。
ServerSocket.close()
がexceptionをスローする理由は、そのソケットにoutputstream
またはinputstream
が接続されているためです。最初に入力ストリームと出力ストリームを閉じることにより、この例外を安全に回避できます。その後、ServerSocket
を閉じてみてください。以下に例を示します。
void closeServer() throws IOException {
try {
if (outputstream != null)
outputstream.close();
if (inputstream != null)
inputstream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
if (!serversock.isClosed())
serversock.close();
}
}
このメソッドを呼び出すと、例外を発生させることなく、どこからでもソケットを閉じることができます。
Breaksocket.accept()用の「void」ソケットを作成できます。
サーバー側
private static final byte END_WAITING = 66;
private static final byte CONNECT_REQUEST = 1;
while (true) {
Socket clientSock = serverSocket.accept();
int code = clientSock.getInputStream().read();
if (code == END_WAITING
/*&& clientSock.getInetAddress().getHostAddress().equals(myIp)*/) {
// End waiting clients code detected
break;
} else if (code == CONNECT_REQUEST) { // other action
// ...
}
}
サーバーサイクルを中断する方法
void acceptClients() {
try {
Socket s = new Socket(myIp, PORT);
s.getOutputStream().write(END_WAITING);
s.getOutputStream().flush();
s.close();
} catch (IOException e) {
}
}
わかりました、OPの質問にもっと直接対処する方法でこれを機能させました。
私がこれをどのように使用するかのスレッドの例についての短い答えを読んでください
簡潔な答え:
ServerSocket myServer;
Socket clientSocket;
try {
myServer = new ServerSocket(port)
myServer.setSoTimeout(2000);
//YOU MUST DO THIS ANYTIME TO ASSIGN new ServerSocket() to myServer‼!
clientSocket = myServer.accept();
//In this case, after 2 seconds the below interruption will be thrown
}
catch (Java.io.InterruptedIOException e) {
/* This is where you handle the timeout. THIS WILL NOT stop
the running of your code unless you issue a break; so you
can do whatever you need to do here to handle whatever you
want to happen when the timeout occurs.
*/
}
実世界の例:
この例では、スレッド内の接続を待機しているServerSocketがあります。アプリを閉じるとき、アプリを閉じる前にスレッド(より具体的にはソケット)をきれいにシャットダウンしたいので、ServerSocketで.setSoTimeout()を使用し、スローされた割り込みを使用しますタイムアウト後、親がスレッドをシャットダウンしようとしているかどうかを確認します。その場合は、ソケットを閉じて設定し、スレッドが完了したことを示すフラグを設定してから、nullを返すThreadsループから抜け出します。
package MyServer;
import javafx.concurrent.Task;
import Java.io.IOException;
import Java.net.ServerSocket;
import Java.net.Socket;
import Java.net.SocketException;
import javafx.concurrent.Task;
import Java.io.IOException;
import Java.net.ServerSocket;
import Java.net.Socket;
import Java.net.SocketException;
public class Server {
public Server (int port) {this.port = port;}
private boolean threadDone = false;
private boolean threadInterrupted = false;
private boolean threadRunning = false;
private ServerSocket myServer = null;
private Socket clientSocket = null;
private Thread serverThread = null;;
private int port;
private static final int SO_TIMEOUT = 5000; //5 seconds
public void startServer() {
if (!threadRunning) {
serverThread = new Thread(thisServerTask);
serverThread.setDaemon(true);
serverThread.start();
}
}
public void stopServer() {
if (threadRunning) {
threadInterrupted = true;
while (!threadDone) {
//We are just waiting for the timeout to exception happen
}
if (threadDone) {threadRunning = false;}
}
}
public boolean isRunning() {return threadRunning;}
private Task<Void> thisServerTask = new Task <Void>() {
@Override public Void call() throws InterruptedException {
threadRunning = true;
try {
myServer = new ServerSocket(port);
myServer.setSoTimeout(SO_TIMEOUT);
clientSocket = new Socket();
} catch (IOException e) {
e.printStackTrace();
}
while(true) {
try {
clientSocket = myServer.accept();
}
catch (Java.io.InterruptedIOException e) {
if (threadInterrupted) {
try { clientSocket.close(); } //This is the clean exit I'm after.
catch (IOException e1) { e1.printStackTrace(); }
threadDone = true;
break;
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
};
}
次に、コントローラークラスで...(関連するコードのみを表示し、必要に応じて独自のコードにマッサージします)
public class Controller {
Server server = null;
private static final int port = 10000;
private void stopTheServer() {
server.stopServer();
while (server.isRunning() {
//We just wait for the server service to stop.
}
}
@FXML private void initialize() {
Platform.runLater(()-> {
server = new Server(port);
server.startServer();
Stage stage = (Stage) serverStatusLabel.getScene().getWindow();
stage.setOnCloseRequest(event->stopTheServer());
});
}
}
これが将来の誰かの助けになることを願っています。
よりクリーンで試すことができるもう1つのことは、acceptループでフラグを確認し、管理スレッドがacceptでスレッドブロッキングを強制終了する場合、フラグを設定して(スレッドセーフにする)、クライアントソケットを作成することですリスニングソケットへの接続。 acceptはブロックを停止し、新しいソケットを返します。リスニングスレッドにスレッドを完全に終了するように指示する簡単なプロトコルを実行できます。そして、クライアント側のソケットを閉じます。例外はなく、ずっときれいです。