ServerSocket accept () 메소드를 어떻게 중단 할 수 있습니까?


143

내 주요 스레드에는 ServerSocket 객체 while(listening)를 호출 accept()한 다음 새 클라이언트 스레드를 시작하고 새 클라이언트가 수락 될 때 컬렉션에 추가 하는 루프 가 있습니다.

또한 'exit'과 같은 명령을 실행하는 데 사용하려는 Admin 스레드가 있는데, 이는 모든 클라이언트 스레드를 종료하고 자체 종료하며 주 스레드를 false로 설정하여 종료시킵니다.

그러나 루프 블록 의 accept()호출은 while(listening)중단 할 수있는 방법이 없어서 while 조건을 다시 확인할 수없고 프로그램을 종료 할 수 없습니다!

더 좋은 방법이 있습니까? 또는 차단 방법을 방해하는 방법은 무엇입니까?



x 시간을 기다린 다음 반응하려는 pepole의 경우 setSoTimeout ()을 사용하십시오.
압둘라 오라비

답변:


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/…
Vladislav Rastrusny

1
을 호출해도 괜찮습니까? close()예외를 처리 할 때 예외가 발생한다는 것을 의미하므로 요청 수신을 중지하는 다른 방법 (예외가 발생하지 않으며 시간 초과가 아닌)이 있습니까?
쿠샬

3
연결이 이미 승인되었고 스레드가 일부 데이터를 기다리는 경우 수행 할 작업 : while ((s = in.readLine ())! = null)?
Alex Fedulov

1
@AlexFedulov 입력을 위해 해당 소켓을 종료합니다. readLine()그런 다음 null을 반환하고 스레드가 이미 있어야하는 정상 폐쇄 작업이 발생합니다.
Lorne의 후작

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

당신은 break serverocket.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) {
     }
}

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만에 Socket우리는 폐쇄 대해 얘기 ServerSocket하지를 Socket(가), 그래서 ServerSocket닫는없이 닫을 수 Socket의 스트림을.
icza


1

좋아, 나는 OP의 질문을보다 직접적으로 다루는 방식으로 이것을 작동시켰다.

내가 이것을 어떻게 사용하는지에 대한 Thread 예제에 대한 짧은 대답을 계속 읽으십시오.

짧은 답변:

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 ()을 사용하고 throw 된 인터럽트를 사용합니다 시간 초과 후 부모가 스레드를 종료하려고하는지 확인하고 확인하십시오. 그렇다면 소켓을 닫은 다음 스레드가 완료되었음을 나타내는 플래그를 설정 한 다음 스레드 루프를 종료하여 null을 반환합니다.

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

}

나는 이것이 길 아래 누군가를 돕기를 바랍니다.


0

당신이 시도 할 수있는 또 다른 것은 accept 루프에서 플래그를 확인 한 다음 관리자 스레드가 수락에서 스레드 차단을 종료하려고 할 때 플래그를 설정하고 (스레드를 안전하게 만듭니다) 클라이언트 소켓을 만드는 것입니다 청취 소켓에 연결합니다. 수락하면 차단을 중지하고 새 소켓을 반환합니다. 청취 스레드가 스레드를 깨끗하게 종료하도록 지시하는 간단한 프로토콜을 해결할 수 있습니다. 그런 다음 클라이언트 쪽의 소켓을 닫습니다. 예외도없고 훨씬 깨끗합니다.


이 소켓과 같은 코드가있을 때 이것은 말이되지 않습니다 ... serverSocket.accept (); 그 순간에 차단이 시작되고 루프가 코드의 일부가 아니므로 차단 코드가 내가 설정 한 플래그를 찾도록 할 수 있습니다 ... 적어도 이것을 할 수있는 방법을 찾지 못했습니다. .. 작업 코드가있는 경우 공유하십시오?
Michael Sims

예, 중요한 정보가 하나도 없어서 이해가되지 않는 것 같습니다. 루프가 반복되기 전에 일정 시간 동안 accept 소켓을 non-blocking으로 만 차단해야하며,이 영역에서 exit 플래그를 확인해야합니다.
stu

정확히, 비 차단 수락 소켓을 어떻게 만드나요?
Michael Sims

long = 1L; if (ioctl (소켓, (int) FIONBIO, (char *) & on))
stu mar

@stu이 질문은 자바 소켓에 관한 것입니다.
Kröw
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.