ServerSocketのaccept()メソッドを中断するにはどうすればよいですか?


143

メインスレッドには、ServerSocketオブジェクトwhile(listening)を呼び出すループがありaccept()、新しいクライアントスレッドを開始し、新しいクライアントが受け入れられたときにコレクションに追加します。

また、「exit」などのコマンドを発行するために使用する管理スレッドがあります。これにより、すべてのクライアントスレッドがシャットダウンされ、それ自体がシャットダウンされ、メインスレッドがシャットダウンされます。

ただし、ループ内のaccept()呼び出しはwhile(listening)ブロックされ、それを中断する方法がないように見えるため、while条件を再度チェックすることはできず、プログラムは終了できません。

これを行うより良い方法はありますか?または、ブロッキング方法を中断する方法はありますか?



x時間待ってから反応したいpepoleには、setSoTimeout()を使用します。
Abdullah Orabi

回答:


151

close()別のスレッドから呼び出すことができ、accept()呼び出しはをスローしSocketExceptionます。


2
ありがたいことに、私には思いもよらないことでした!ループを終了した後、close()を呼び出していました。
lukeo05

4
奇妙なことに、ドキュメントにこの情報がないこと:download.oracle.com/javase/6/docs/api/java/net/…メソッドがSocketExceptionをスローするようにマークされていません。それは、ここでしか言及されているdownload.oracle.com/javase/1.4.2/docs/api/java/net/...
ウラジスラフRastrusny

1
呼び出しても大丈夫ですか?それclose()を行うと例外がスローされるので、リクエストのリスニングを停止する他の方法(例外をスローせず、タイムアウトに基づいていない)はありますか?
Kushal

3
そして、接続がすでに受け入れられていて、スレッドがデータを待っている場合はどうするか:while((s = in.readLine())!= null)?
Alex Fedulov 2012

1
@AlexFedulov入力用にそのソケットをシャットダウンします。readLine()次にnullを返し、スレッドがすでに備えているはずの通常のクロージャ操作が発生します。
Lorneの侯爵2017年

31

タイムアウトをに設定すると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この場合には閉じられていません。



4

ブレークサーバー用の「ボイド」ソケットを作成することができますocket.accept()

サーバ側

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) {
     }
}

4

例外ServerSocket.close()スローされる理由は、 そのソケットにまたはが接続されているためです。最初に入力ストリームと出力ストリームを閉じることにより、この例外を安全に回避できます。次にを閉じてみてください。次に例を示します。outputstreaminputstreamServerSocket

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();
  }
}

このメソッドを呼び出すと、例外を発生させることなく、どこからでもソケットを閉じることができます。


7
入力ストリームと出力ストリームはにアタッチされてServerSocketいませんが、でSocketServerSocketないを閉じることを話しているSocketので、のストリームをServerSocket閉じずにを閉じることができSocketます。
icza 14


1

OK、私はこれを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()を使用してから、スローされた割り込みを使用しますタイムアウトの後、親がスレッドをシャットダウンしようとしているかどうかを確認します。その場合は、ソケットを閉じるように設定し、スレッドが完了したことを示すフラグを設定してから、ヌルを返す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;
    }
};

}

次に、私のControllerクラスで...(関連するコードのみを表示し、必要に応じて独自のコードにマッサージします)

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());
        });
    }

}

これが将来の誰かに役立つことを願っています。


0

あなたが試すことができるもう1つのことは、よりきれいで、受け入れループのフラグをチェックし、管理スレッドが受け入れのスレッドブロッキングを強制終了したいときに、フラグを設定して(スレッドセーフにする)、クライアントソケットを作成することですリスニングソケットへの接続。Acceptはブロックを停止し、新しいソケットを返します。リスニングスレッドがスレッドを完全に終了するように指示する簡単なプロトコルを作成できます。そして、クライアント側のソケットを閉じます。例外はなく、はるかにきれいです。


これは意味がありません...このようなコードがある場合、socket = serverSocket.accept(); その時点でブロッキングが開始し、ループはコードの一部ではないため、ブロッキングコードに設定したフラグを探す方法があります...少なくとも、これを行う方法を見つけることができませんでした。 ..機能するコードがある場合は、共有してください。
Michael Sims

ええ、それはあまり意味をなさないようにする重要な情報を1つ省略したようです。受け入れソケットを非ブロックにし、ループが繰り返される前の一定期間だけブロックする必要があります。その領域で、終了フラグを確認します。
stu

どのようにして正確に、ソケットを非ブロッキングにしますか?
Michael Sims

ロングオン= 1L; if(ioctl(socket、(int)FIONBIO、(char *)&on))
stu

@stuこの質問はJavaのソケットに関するものです。
クロウ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.