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.