如何在阻塞模式 NIO 中实现超时?

Posted

技术标签:

【中文标题】如何在阻塞模式 NIO 中实现超时?【英文标题】:How does one implement a timeout in blocking mode NIO? 【发布时间】:2013-02-13 17:58:10 【问题描述】:

我没有使用任何选择器或类似的东西。我只有一个简单的ServerSocketChannel 监听和一个SocketChannel 以阻塞模式连接到它。我想对连接施加超时,但SocketChannel.socket().setSoTimeout() 不起作用。

我尝试让后台线程休眠 30 秒并检查变量是否仍然是 null(因为它会阻止等待读取该变量)但我无法正确同步变量,因为我无法访问我的匿名类中的局部变量。

还有其他方法可以做到这一点吗?

更新:我的问题措辞有误。我还希望读取操作以及连接本身超时。

【问题讨论】:

Specify connection timeout in java.nio 的可能重复项 不,那是用于非阻塞模式的 nio。我说的是阻塞模式。 【参考方案1】:

setSoTimeout() 设置读取超时,而不是连接超时,并且由于某种原因,它在 SocketChannels 上根本不起作用,即使在阻塞模式下,甚至在包装流的情况下也是如此。

您要查找的方法是 channel.socket().connect(),带有两个参数。

【讨论】:

谢谢,但我也希望读取操作超时。我也更新了我的问题以反映这一点。昨晚累的时候问的,忘记提了。 那我建议你使用java.net.Socket,它确实有读取超时。 关于setSoTimeout() 不能在SocketChannel 上工作的最佳答案是here,有一个解决方法。试过了,对我有用。【参考方案2】:

据我所知,在nio 中使用同步操作是不可能的。通过Socket.setSoTimeout() 设置的套接字超时会影响读写,但不会建立连接。

即使在系统库级别,连接超时也不同于读/写超时 - 请参阅 man 2 connectman 2 setsockoptman 7 socket 了解详细信息。所以如果你想要连接时真正的、应用程序控制的超时,你需要使用异步连接协议和适当的Selector,检查SelectionKey.isConnectable()等等。不幸的是,这会使代码变得更长。

我现在手头没有 Java 库源,但是看看 Socket.connect(SocketAddress endpoint,int timeout) 是如何在内部实现的会很有趣 - 但我相信它也在内部使用 select()

【讨论】:

你错了。您可以在阻塞模式下使用 NIO,并且可以获得读取超时和连接超时。 OP 不必切换到非阻塞模式。 -1 @EJP 我不这么认为。 SocketChannelImpl.connect() 的代码使用 Net.connect(),它没有传递任何超时值。如果你使用SocketChannel.socket(),你会得到一个SocketAdaptor 的实例,它的connect() method 在内部使用异步I/O。所以 SO_TIMEOUT 似乎不会影响 SocketChannel.connect()。 调用 channel.socket.connect(address, timeout) 你会得到一个连接超时,就像它在锡上说的那样。关于 SO_TIMEOUT 我什么都没说。 如果您调用SocketChannel.socket().connect(address,timeout),您实际上使用的是普通的旧Socket API - 所以我不会将此称为使用nio 连接的示例,我理解OP 的需求。 他需要的是一种从 NIO API 开始获取连接超时的方法,这就是方法。他“不需要”任何类型的“NIO API 纯度”,并且当已经有一个已经完全可以做到的单线器时,他也“不需要”用非阻塞模式和选择器实现定时连接在引擎盖下。

以上是关于如何在阻塞模式 NIO 中实现超时?的主要内容,如果未能解决你的问题,请参考以下文章

如何在android中实现请求超时?

如何在 C++ 中实现函数超时

如何在 Grails 中实现超时页面

如何在 Java 中实现同步方法超时?

如何在 JS/TS 中实现伪阻塞异步队列?

如何停止在共享库中实现的阻塞 pthread_join()