【问题标题】:How can I interrupt a ServerSocket accept() method?如何中断 ServerSocket accept() 方法?
【发布时间】:2011-02-28 08:32:46
【问题描述】:

在我的主线程中,我有一个while(listening) 循环,它在我的ServerSocket 对象上调用accept(),然后启动一个新的客户端线程并在接受新客户端时将其添加到集合中。

我还有一个管理线程,我想用它来发出命令,比如'exit',这将导致所有客户端线程关闭,关闭自己,关闭主线程,通过监听假的。

但是,while(listening)循环中的accept()调用阻塞了,而且似乎没有办法中断它,所以无法再次检查while条件,程序无法退出!

有没有更好的方法来做到这一点?或者有什么方法可以中断阻塞方法?

【问题讨论】:

标签: java network-programming


【解决方案1】:

您可以在调用接受函数时简单地将超时限制(毫秒)作为参数传递。

例如 serverSocket.accept(1000); 1秒后自动关闭请求

【讨论】:

【解决方案2】:

ServerSocket.close() 引发异常的原因 是因为你有一个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();
  }
}

您可以调用此方法从任何地方关闭任何套接字而不会出现异常。

【讨论】:

  • 输入和输出流不附加到ServerSocket,而是附加到Socket,我们正在讨论关闭ServerSocket而不是Socket,所以ServerSocket可以是关闭而不关闭Socket 的流。
【解决方案3】:

您可以从另一个线程调用close(),而accept() 调用将抛出SocketException

【讨论】:

  • 谢谢,这么明显,我什至没有想到!退出循环后,我正在调用 close()。
  • 奇怪,文档中没有此信息:download.oracle.com/javase/6/docs/api/java/net/… 方法未标记为抛出 SocketException。这里只提到download.oracle.com/javase/1.4.2/docs/api/java/net/…
  • 是否可以调用close(),我的意思是这样做会抛出异常,那么有没有其他方法(不抛出异常,也不是基于超时)停止监听请求?
  • 如果连接已经被接受并且线程正在等待一些数据怎么办:while((s=in.readLine()) != null) ?
  • @AlexFedulov 关闭该套接字以进行输入。然后readLine() 将返回 null 并且线程应该已经存在的正常关闭操作将发生。
【解决方案4】:

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 没有关闭。

【讨论】:

    【解决方案5】:

    ServerSocket 上调用close() 是一个选项吗?

    http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html#close%28%29

    关闭此套接字。当前在 accept() 中阻塞的任何线程都会抛出 SocketException。

    【讨论】:

      【解决方案6】:

      好的,我的工作方式更直接地解决了 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() 然后我使用抛出的中断超时后检查父级是否试图关闭线程。如果是这样,那么我设置关闭套接字,然后设置一个指示线程完成的标志,然后我跳出返回 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;
          }
      };
      
      }
      

      然后,在我的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());
              });
          }
      
      }
      

      我希望这对以后的人有所帮助。

      【讨论】:

        【解决方案7】:

        【讨论】:

        • 这行不通......如果你让它工作,你能展示一个工作代码的例子吗?
        【解决方案8】:

        你可以只为 break serversocket.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) {
             }
        }
        

        【讨论】:

          【解决方案9】:

          您可以尝试的另一件更清洁的事情是检查接受循环中的标志,然后当您的管理线程想要终止接受上的线程阻塞时,设置标志(使其线程安全)然后与侦听套接字的客户端套接字连接。 接受将停止阻塞并返回新的套接字。 您可以制定一些简单的协议来告诉侦听线程干净地退出线程。 然后关闭客户端的套接字。 没有例外,更干净。

          【讨论】:

          • 这没有意义......当你有这样的代码时 socket = serverSocket.accept();在那一刻,阻塞开始并且循环不是我们代码的一部分,所以有办法让阻塞代码查找我设置的标志......至少我还没有找到方法来做到这一点。 ..如果你有工作代码,请分享?
          • 是的,我似乎遗漏了一个重要的信息,这使得它没有多大意义。您需要使接受套接字非阻塞,并且仅在循环重复之前阻塞一段时间,然后您将在该区域检查退出标志。
          • 如何,确切地说,您如何使接受套接字成为非阻塞的?
          • 长 = 1L; if (ioctl(socket, (int)FIONBIO, (char *)&on))
          • @stu 这个问题是针对 Java 的 Sockets 的。
          猜你喜欢
          • 1970-01-01
          • 2016-02-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多