我有一些代码从组播套接字读取数据,直到用户定义的结束时间。如果通过调用Thread.interrupt
(或者您可以提出的任何其他用户启动的操作)中断线程,我还想停止读取数据。我无法弄清楚如何在线程中断时获取通知。现有代码如下:
// These are the constants I am given
final int mcastPort = ...;
final InetAddress mcastIP = ...;
final InetAddress ifaceIP = ...; // Null indicates all interfaces should be used
final Instant endTime = ...; // Time at which to stop processing
// Initialize a datagram
final byte[] buffer = new byte[1000];
final DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// Process incoming datagram packets
try (final MulticastSocket socket = new MulticastSocket(port)) {
socket.joinGroup(mcastIP);
if (ifaceIP != null)
socket.setInterface(ifaceIP);
do {
final Duration soTimeout = Duration.between(Instant.now(), endTime);
socket.setSoTimeout(soTimeout);
socket.receive(packet);
// Process packet
...
} while (true);
} catch (final SocketTimeoutException e) {
// Normal condition... the recording time has ended
} catch (final ClosedByInterruptException e) {
// Uh-oh... this never happens
} ...
我看到有一个DatagramSocket.getChannel
方法返回DatagramChannel
,所以我自然地认为该类型用于读取/写入底层套接字。该假设不正确,这意味着MulticastSocket
未实现InterruptibleChannel
。因此,MulticastSocket.receive
永远不会抛出ClosedByInterruptException
。
我在网上搜索了一些示例,但无法弄清楚如何修改上述代码以使用DatagramChannel
代替MulticastSocket
。我需要帮助的问题是:
InetAddress
转换为NetworkInterface
对象?以下是关于如何将我的实施从MulticastSocket
转换为DatagramChannel
以满足我的要求的最佳猜测:
// Initialize a buffer
final ByteBuffer buffer = ByteBuffer.allocate(1000);
try (final DatagramChannel mcastChannel = DatagramChannel.open()) {
mcastChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
mcastChannel.connect(new InetSocketAddress(port));
mcastChannel.join(mcastIP);
if (ifaceIP != null)
// HELP: this option requires an InterfaceAddress object,
// but I only have access to an InetAddress object
mcastChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, ifaceIP);
do {
final Duration soTimeout = Duration.between(Instant.now(), endTime);
// HELP: SO_TIMEOUT is not a member of StandardSocketOptions
mcastChannel.setOption(SO_TIMEOUT, ???);
mcastChannel.receive(buffer);
// Process packet
...
} while (true);
} ...
这种方法是否有效? DatagramChannel.receive
未列出SocketTimeoutException
作为其能够抛出的异常之一。如果这样可行,那么请告诉我如何将第二个实现更改为与第一个相同,但是当客户端调用ClosedByInterruptException
时能够抛出Thread.interrupt
。如果没有,那么有没有人对如何满足在预定义时间停止数据报接收的要求有任何其他想法,同时还提供了一种通过用户交互来停止执行的方法?
答案 0 :(得分:1)
如何在DatagramChannel上设置SO_TIMEOUT参数?
致电channel.socket().setSoTimeout()
。
如何将
InetAddress
转换为NetworkInterface
对象?
您可以枚举网络接口,直到找到具有所需地址的网络接口。
DatagramChannel.receive()
没有列出SocketTimeoutException
它没有必要。它列出了IOException
,SocketTimeoutException
扩展了IOException
。
你的第二段代码应该调用bind()
,而不是connect()
,它应该在调用join()
之前设置界面,而不是之后。除此之外,一旦修复网络接口问题,它应该按预期工作,如果被中断,它将抛出ClosedByInterruptException
。
答案 1 :(得分:0)
最终的工作代码是:
int **twod = new int *[rows];
注意事项:
// Create a datagram packet used to read multicast data from the socket
final byte[] buffer = new byte[1000];
final DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// Process incoming datagram packets
try (final DatagramChannel mcastChannel =
DatagramChannel.open(StandardProtocolFamily.INET)) {
// Set the appropriate parameters on the socket
mcastChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
mcastChannel.bind(new InetSocketAddress(port));
if (ifaceIP == null) {
// Call join on each NetworkInterface that supports IPv4 multicast
} else {
mcastChannel.join(mcastIP, NetworkInterface.getByInetAddress(ifaceIP));
}
final DatagramSocket socket = mcastChannel.socket();
do {
final Duration timeToEnd = Duration.between(Instant.now(), endTime);
if (timeToEnd.compareTo(Duration.ZERO) < 0) break;
socket.setSoTimeout((int)timeToEnd.toMillis());
socket.receive(packet);
// Process packet
...
} while (true);
} catch (final SocketTimeoutException e) {
// The end time has passed
} catch (final ClosedByInterruptException e) {
// The user initiated the closure
} ...
。如果将其更改为socket.receive
,则永远不会获得mcastChannel.receive
。SocketTimeoutException
的呼叫发生在致bind
的呼叫之前。您可以根据需要在多个不同的网络接口上多次呼叫join
。