-1

ユーザー定義の終了時刻までマルチキャスト ソケットからデータを読み取るコードがあります。また、スレッドがへの呼び出し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オンラインで例を検索しましたが、上記のコードを変更して a の代わりに aを使用する方法がわかりませんMulticastSocket。助けが必要な問題は次のとおりです。

  1. DatagramChannel で SO_TIMEOUT パラメータを設定するにはどうすればよいですか?
  2. InetAddressNetworkInterfaceオブジェクトに変換するにはどうすればよいですか?

以下は、要件を満たすために実装を から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);
} ...

このアプローチは機能しますか? スローできる例外の1つとしてDatagramChannel.receiveリストされていません。これが機能する場合SocketTimeoutExceptionは、2 番目の実装を最初の実装と同等にする方法を変更する必要があることを教えてください。そうでない場合、ユーザーの操作を介して実行を停止する方法を提供しながら、事前に定義された時間にデータグラムの受信を停止するという要件を満たす方法について、他のアイデアはありますか?ClosedByInterruptExceptionThread.interrupt

4

2 に答える 2

0

最終的な作業コードは次のとおりです。

// 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
} ...

注意事項:

  1. への呼び出しsocket.receive。これを に変更するとmcastChannel.receiveSocketTimeoutException.
  2. への呼び出しはbind、への呼び出しの前に発生しjoinます。join異なるネットワーク インターフェイス上で、必要なだけ何度でも呼び出すことができます。
于 2017-03-16T02:47:29.020 に答える