如何在setSoTimeout()中捕获SocketTimeoutException "raised"?



我在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(尽管这种情况很少发生)。

SocketTimeoutExceptioncall()方法中抛出时(由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
}
}
};

相关内容

  • 没有找到相关文章

最新更新