我在Java中做套接字编程,我对setSoTimeout()
及其异常感到困惑。
我在服务器端有一个简单的Callable<String>
用于接受连接:
private Callable<String> waitForConnectionCallable = new Callable<String>() {
@Override
public String call() throws IOException {
if (socket == null) {
socket = new ServerSocket(PORT);
}
socket.setSoTimeout(CONNECTION_TIMEOUT);
Socket inSocket = socket.accept();
return "CONNECTED";
}
};
我用这种方式调用和处理它:
try {
Future<String> connectionFuture = executorService.submit(waitForConnectionCallable);
String connectionResult = connectionFuture.get();
} catch (ExecutionException | InterruptedException e) {
Logs.error(TAG, "No connection received");
e.printStackTrace();
}
现在,setSoTimeout()
的java文档提到引发SocketTimeoutException,虽然奇怪的是它不包括在抛出列表中:
使用指定的超时时间启用/禁用SO_TIMEOUT,以毫秒为单位。如果将此选项设置为正超时值,则对此ServerSocket的accept()调用将仅阻塞此时间。如果超时,抛出java.net.SocketTimeoutException,尽管ServerSocket仍然有效。超时为零将被解释为无限超时。该选项必须在进入阻塞操作之前启用才能生效。
参数:Timeout -指定的超时,以毫秒为单位
扔:SocketException——如果底层协议有错误,比如TCP错误IllegalArgumentException -如果timeout为负
当我运行程序并且连接超时时,我在catch块中获得SocketTimeoutException,但是以这种方式:
java.util.concurrent.ExecutionException: java.net.SocketTimeoutException: Accept timed out
我的问题是,如何捕获特定的异常?我不能将它添加到调用者的catch语句中,因为我会得到没有在方法中抛出的编译错误。此外,我不能添加到call()
方法(在Callable中),因为IOException,一个更一般的异常已经存在了。
另外:为什么SocketTimeoutException不是setSoTimeout()
签名的一部分?考虑到它不是SocketException的子类
在你的观察中有几件事需要纠正
现在,setSoTimeout()的java文档提到了引发一个SocketTimeoutException,但奇怪的是它没有包含在把列表
不,是
使用指定的超时时间启用/禁用SO_TIMEOUT,以毫秒为单位。如果将此选项设置为非零超时,则调用accept(这个ServerSocket将只阻塞这段时间。如果timeout过期,抛出java.net.SocketTimeoutException异常ServerSocket仍然有效。该选项必须在…之前启用进入阻塞操作才有效果。超时时间必须为>0. 超时值为0将被解释为无限超时。
这意味着,如果您使用setSoTimeout()
设置超时,如果在接受连接时发生超时,则将抛出SocketTimeoutException
。所以SocketTimeoutException
没有被setSoTimeout()
抛出,因此它不在它的抛出列表中。
您可以在accept()
方法中捕获超时异常。如果在接受连接时出现问题,则抛出IOException
。超时可能是原因之一。
像下图:
private Callable<String> waitForConnectionCallable = new Callable<String>() {
@Override
public String call() throws IOException {
if (socket == null) {
socket = new ServerSocket(PORT);
}
socket.setSoTimeout(CONNECTION_TIMEOUT);
try {
Socket inSocket = socket.accept();
} catch (SocketTimeoutException e) {
// do timeout handling...
return "TIMEOUT";
}
return "CONNECTED";
}
};
您正在混合一些事情:SocketTimeoutException
是由socket.accept()
抛出的。socket.setSoTimeout()
可以抛出IOException
(尽管这种情况很少发生)。
当SocketTimeoutException
在call()
方法中抛出时(由socket.accept()
抛出),它将被包装在ExecutionException
中,这是一个包装异常。然后,您可以通过调用e.getCause()
来访问原始异常,并相应地处理它。
编辑-我会在你的代码中做一些改变,关于异常处理。首先,我将使用Callable<Socket>
而不是Callable<String>
,因此您可以稍后轻松使用套接字,因为您确定它是打开并连接的:
private Callable<Socket> waitForConnectionCallable = new Callable<>() {
@Override
public Socket call() throws IOException {
if (socket == null) {
socket = new ServerSocket(PORT);
socket.setSoTimeout(CONNECTION_TIMEOUT);
}
return socket.accept(); // Can throw IOException or SocketTimeoutException
}
};
然后,执行waitForConnectionCallable.get()
:
try {
Future<Socket> connectionFuture = executorService.submit(waitForConnectionCallable);
Socket connectionResult = connectionFuture.get();
} catch (InterruptedException e) {
Thread.interrupted();
} catch (ExecutionException e) { // IOException or SocketTimeoutException
Throwable t = e.getCause();
t.printStackTrace();
if (t instanceof SocketTimeoutException) {
Logs.error(TAG, "No connection received");
} else { // IOException
Logs.error(TAG, "Error waiting for connection: "+t.getMessage());
}
}
或者,您可以在Callable
中执行等待,但是设置套接字超时可能没有意义,因为您已经使用了另一个线程,因此您可以保持阻塞模式:
private Callable<Socket> waitForConnectionCallable = new Callable<>() {
@Override
public Socket call() throws IOException {
if (socket == null) {
socket = new ServerSocket(PORT);
socket.setSoTimeout(CONNECTION_TIMEOUT);
}
for (;;) { // Until we receive a connection
try {
return socket.accept(); // Can throw IOException or SocketTimeoutException
} catch (SocketTimeoutException e) {
// Do something? return null, log, continue, ...
} // IOException is not caught here and will bubble up in the ExecutionException
}
}
};