2017-03-16 4 views
-1

Ich habe einige Code, der Daten von einem Multicast-Socket bis zu einer benutzerdefinierten Endzeit liest. Ich würde auch gerne aufhören, Daten zu lesen, wenn der Thread durch einen Anruf an Thread.interrupt (oder durch eine andere vom Benutzer initiierte Aktion, die Sie auftauchen können) unterbrochen wird. Ich kann nicht herausfinden, wie man eine Benachrichtigung erhält, wenn der Thread unterbrochen wird. Der bestehende Code lautet wie folgt:Java Get MulticastSocket.Receive zu Throw ClosedByInterruptException

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

ich sah, dass es eine DatagramSocket.getChannel Methode war, die ein DatagramChannel zurückkehrt, so dass ich natürlich angenommen, dass Typ verwendet wurde/Schreiben in den zugrunde liegenden Socket zu lesen. Diese Annahme war falsch, was bedeutet, dass MulticastSocketInterruptibleChannel nicht implementiert. Aus diesem Grund wirft MulticastSocket.receive nie eine ClosedByInterruptException.

Ich habe online nach Beispielen gesucht, aber kann nicht herausfinden, wie Sie den obigen Code ändern, um eine DatagramChannel anstelle von MulticastSocket zu verwenden. Die Probleme, die ich brauche, sind:

  1. Wie setze ich den Parameter SO_TIMEOUT auf einem DatagramChannel?
  2. Wie konvertiere ich ein InetAddress in ein NetworkInterface Objekt?

Hier finden Sie meine beste Vermutung auf, wie meine Implementierung MulticastSocket-DatagramChannel konvertieren meine Anforderungen zu erfüllen:

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

Wird dieser Ansatz auch arbeiten? DatagramChannel.receive listet SocketTimeoutException nicht als eine der Ausnahmen auf, die es auslösen kann. Wenn dies funktioniert, lassen Sie mich bitte wissen, wie ich die zweite Implementierung ändern muss, um der ersten zu entsprechen, aber mit der Möglichkeit, eine ClosedByInterruptException zu werfen, wenn ein Client Thread.interrupt aufruft. Wenn nicht, hat dann jemand andere Ideen, wie ich die Anforderung erfüllen kann, den Datagramm-Empfang zu einem vordefinierten Zeitpunkt zu stoppen, während er auch eine Möglichkeit bietet, die Ausführung durch Benutzerinteraktion zu stoppen?

+0

Verwenden Sie keine Angebotsformatierung für Text, der nicht zitiert wird. – EJP

Antwort

1

Wie setze ich den Parameter SO_TIMEOUT auf einem DatagramChannel?

Durch den Aufruf channel.socket().setSoTimeout().

Wie konvertiere ich ein InetAddress in ein NetworkInterface Objekt?

Sie listen die Netzwerkschnittstellen auf, bis Sie eine mit der erforderlichen Adresse finden.

DatagramChannel.receive() nicht aufgeführt SocketTimeoutException

Es zu nicht haben. Es listet IOException, und SocketTimeoutException erweitert IOException.

Ihr zweites Stück Code sollte bind(), nicht connect() anrufen, und es sollte die Schnittstelle vor dem Aufruf join(), nicht nach.Abgesehen davon sollte es wie erwartet funktionieren, sobald Sie das Problem der Netzwerkschnittstelle behoben haben, und es wird ausgelöst, wenn es unterbrochen wird.

0

Der letzte, Arbeitscode lautet:

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

Dinge zu beachten:

  1. Der Aufruf von socket.receive. Wenn dies in mcastChannel.receive geändert wird, erhalten Sie nie eine SocketTimeoutException.
  2. Der Anruf an bind erfolgt vor dem Anruf (s) zu join. Sie können join so oft wie Sie möchten an so vielen verschiedenen Netzwerkschnittstellen aufrufen.