如何终止因 Socket.accept() 而阻塞太久的线程?

How to terminate a thread that has been blocked for too long due to Socket.accept()?

public class Slave implements Runnable {
   public ServerSocket slaveSocket;

   public Slave(ServerSocket sk) {socket = sk;}

   @Override
   public void run() {
      Socket client = slaveSocket.accept(); // slave will wait to serve a client
      // more code...

      Socket clientPart2 = slaveSocket.accept();
      // more code...
   }
}

public class Server {
   public static void main(String[] args) {
       // for example only, incomplete code
       ServerSocket serverSocket = new ServerSocket(0); // a client connect to 8088
       Slave slave = new Slave(serverSocket);
       new Thread(slave).start(); // slave serve the current client, the server wait for new client

       // send new slave's port to client ... 
   }
}

所以我有一台同时为多个客户端提供服务的服务器。每当有客户端连接时,服务器会创建一个新的Slave,将那个slave的IP/port发送给客户端,然后客户端就会与slave一起工作。

然而,如果客户端收到slave的地址,则什么都不做(或退出)(编辑:这意味着客户端和服务器已连接但客户端什么都不做,因为对于例如用户去吃午饭)slaveSocket.accept() 导致从属线程永远 运行,这是浪费。

我希望从属线程在等待 30 秒后退出 slaveSocket.accept()。由于 slaveSocket.accept() 正在阻塞,我无法从 void run() 内部执行此操作。

解决这个问题的正确、简洁的方法是什么?谢谢。

编辑 1:将 ServerSocket 传递给从服务器,因为客户端可以有多个进程连接到该从服务器。所以它不只是执行一个功能。

非阻塞I/O:

看看 AsynchronousServerSocketChannel's accept method which returns a Future. Then the Future has a getter with timeout 可以满足您的要求。

注意:您可以阅读 related tutorial

然后 getter 将 return 一个 AsynchronousSocketChannel 可以通过相应的 Channels.newInputStream and Channels.newOutputStream 方法 转换 回阻塞与工作线程中的阻塞方法一起使用。

阻止 I/O:

我认为您实际上是指如何实现一个服务器,该服务器按顺序接受客户端并并行地为它们提供服务,并带有阻塞 I/O。如果是这样的话,那么你可以看看下面的例子:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Objects;

public class Main {

    public static class Worker implements Runnable {
        private final Socket sck;
        private OutputStream os;
        private InputStream is;

        public Worker(final Socket sck) {
            this.sck = Objects.requireNonNull(sck);
        }

        @Override
        public void run() {
            try {
                os = sck.getOutputStream();
                is = sck.getInputStream();

                //ALL the work with the client goes here, unless you need more than one connections with him.
            }
            catch (final IOException iox) {
                System.err.println(iox);
            }
            finally {
                try { is.close(); } catch (final IOException | RuntimeException x) {}
                try { os.close(); } catch (final IOException | RuntimeException x) {}
                try { sck.close(); } catch (final IOException | RuntimeException x) {}
            }
        }
    }

    public static void main(final String[] args) {
        ServerSocket srv = null;
        try {
            srv = new ServerSocket(8088);
            while (true)
                new Thread(new Worker(srv.accept())).start();
        }
        catch (final IOException iox) {
            System.err.println(iox);
        }
        finally {
            try { srv.close(); } catch (final IOException | RuntimeException x) {}
        }
    }
}

如果您使用 setSoTimeout 设置超时并且没有客户端连接,ServerSocket.accept 将抛出异常。你可以捕获这个异常。

要设置 30 秒的超时,请使用:

serverSocket.setSoTimeout(30000)