如何终止因 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)
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)