# Thread "extern" beenden



## freehawk (3. Jan 2022)

Hallo zusammen,

ich versuche mittels der Library pcap4j die MAC Adresse einer IP Adresse zu lesen. Nun habe ich das Problem, dass vor allem wenn die IP Adresse aus irgendwelchen Gründen nicht erreichbar ist, die Funktion der Klasse zwar beendet wird mit der RunTime Exception aber ein Thread stehen bleibt, den ich nicht beenden kann.

Man sieht ja in der Klasse, die ich geschrieben habe, dass ich dort versuche den Thread zu beenden, das hat aber keine Auswirkung.

Wenn ich anschließend extern alle Threads ausgeben lassen sehe ich folgendes Ergebnis:
Thread: Thread[pool-10-thread-1,5,main] : state: RUNNABLE

Wenn ich versuche den Thread zu interrupten passiert nichts, wenn ich ihn versuche zu stoppen geschieht auch nichts.

Hat mir hierzu jemand einen Tipp wie ich das lösen könnte, damit ich die Threads am besten irgendwie beenden kann oder zumindest anschließend aufräumen kann.

Danke

Gruß
Markus



[CODE lang="java" title="Klasse"]import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.pcap4j.core.BpfProgram.BpfCompileMode;
import org.pcap4j.core.NotOpenException;
import org.pcap4j.core.PacketListener;
import org.pcap4j.core.PcapHandle;
import org.pcap4j.core.PcapNativeException;
import org.pcap4j.core.PcapNetworkInterface;
import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode;
import org.pcap4j.core.Pcaps;
import org.pcap4j.packet.ArpPacket;
import org.pcap4j.packet.EthernetPacket;
import org.pcap4j.packet.Packet;
import org.pcap4j.packet.namednumber.ArpHardwareType;
import org.pcap4j.packet.namednumber.ArpOperation;
import org.pcap4j.packet.namednumber.EtherType;
import org.pcap4j.util.ByteArrays;
import org.pcap4j.util.MacAddress;

public class SendArpRequest
{
    private static final String COUNT_KEY = SendArpRequest.class.getName() + ".count";
    private static int COUNT;
    private static final String READ_TIMEOUT_KEY = SendArpRequest.class.getName() + ".readTimeout";
    private static int READ_TIMEOUT; // [ms]
    private static final String SNAPLEN_KEY = SendArpRequest.class.getName() + ".snaplen";
    private static int SNAPLEN; // [bytes]

    private String sourceIP = "";

    private MacAddress sourceMac = MacAddress.getByName( "fe:00:01:02:03:04" );
    private static MacAddress resolvedAddr = null;
    private String destinationIP = "";

    private Debug debug;
    private ConfigRead config;

    public SendArpRequest( Debug d, ConfigRead c, String sourceIP, String sourceMac )
    {
        this.debug = d;
        this.config = c;
        this.sourceIP = sourceIP;
        this.sourceMac = MacAddress.getByName( sourceMac );

        COUNT = Integer.getInteger( COUNT_KEY, config.getArpCount() );
        READ_TIMEOUT = Integer.getInteger( READ_TIMEOUT_KEY, config.getArpTimeout() ); // [ms]
        SNAPLEN = Integer.getInteger( SNAPLEN_KEY, config.getArpPacketSize() ); // [bytes]

        debug.print( 2, "ARP Objekt initialisiert mit SourceIP: " + sourceIP + " und Source Mac: " + sourceMac );
    }

    public String searchMacAddress( String destinationIP ) throws RuntimeException
    {
        debug.print( 2, "Suche nach Macadresse für: " + destinationIP );

        String erg = "";
        Boolean state = true;

        this.destinationIP = destinationIP;
        resolvedAddr = null;

        /*
        System.out.println(COUNT_KEY + ": " + COUNT);
        System.out.println(READ_TIMEOUT_KEY + ": " + READ_TIMEOUT);
        System.out.println(SNAPLEN_KEY + ": " + SNAPLEN);
        System.out.println("\n");
        */

        PcapNetworkInterface nif;

        try
        {
            nif = Pcaps.getDevByAddress( InetAddress.getByName( this.sourceIP ) );
        }
        catch( Exception e )
        {
            debug.print( 1, "Source Netzwerk Interface nicht gefunden: " + e.getMessage() );
            return erg;
        }

        if( nif == null )
        {
            debug.print( 1, "Source Netzwerk Interface nicht gefunden!" );
            return erg;
        }

        debug.print( 2, "Suche über folgendes Inerface: " + nif.getName() + " (" + nif.getDescription() + ")" );

        PcapHandle handle = null;
        PcapHandle sendHandle = null;
        ExecutorService pool = null;

        try
        {
            handle = nif.openLive( SNAPLEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT );
            sendHandle = nif.openLive( SNAPLEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT );
            pool = Executors.newSingleThreadExecutor();

            handle.setFilter( "arp and src host " + destinationIP + " and dst host " + this.sourceIP + " and ether dst " + Pcaps.toBpfString( this.sourceMac ), BpfCompileMode.OPTIMIZE );

            PacketListener listener = new PacketListener()
            {
                public void gotPacket( Packet packet )
                {
                    if( packet.contains( ArpPacket.class ))
                    {
                        ArpPacket arp = packet.get( ArpPacket.class );
                        if( arp.getHeader().getOperation().equals( ArpOperation.REPLY ))
                        {
                            SendArpRequest.resolvedAddr = arp.getHeader().getSrcHardwareAddr();
                        }
                    }

                    debug.print( 3, "Empfangenes Packet: " + packet );

                }
            };

            Task t = new Task( handle, listener );
            pool.execute( t );

            ArpPacket.Builder arpBuilder = new ArpPacket.Builder();
            try
            {
                arpBuilder
                  .hardwareType( ArpHardwareType.ETHERNET )
                  .protocolType( EtherType.IPV4 )
                  .hardwareAddrLength( (byte)MacAddress.SIZE_IN_BYTES )
                  .protocolAddrLength( (byte)ByteArrays.INET4_ADDRESS_SIZE_IN_BYTES )
                  .operation( ArpOperation.REQUEST )
                  .srcHardwareAddr( this.sourceMac )
                  .srcProtocolAddr( InetAddress.getByName( this.sourceIP ))
                  .dstHardwareAddr( MacAddress.ETHER_BROADCAST_ADDRESS )
                  .dstProtocolAddr( InetAddress.getByName( destinationIP ));
            }
            catch( UnknownHostException e )
            {
                debug.print( 1, "Fehler beim Bau des ARP Pakets: " + e.getMessage() );
                return erg;
            }

            EthernetPacket.Builder etherBuilder = new EthernetPacket.Builder();
            etherBuilder.dstAddr( MacAddress.ETHER_BROADCAST_ADDRESS )
                .srcAddr( this.sourceMac )
                .type( EtherType.ARP )
                .payloadBuilder( arpBuilder )
                .paddingAtBuild( true );

            for( int i = 0; i < COUNT; i++ )
            {
                Packet p = etherBuilder.build();

                debug.print( 3, "Gesendetes Packet: " + p );

                sendHandle.sendPacket(p);

                try
                {
                    Thread.sleep( 1000 );
                }
                catch( InterruptedException e )
                {
                    break;
                }
            }

            debug.print( 2, "Pakete gesendet" );

            int i = 0;

            while( true )
            {
                if( resolvedAddr != null )
                {
                    break;
                }

                i = i + 1;

                if( i >= 10 )
                {
                    pool.shutdown();
                    pool.shutdownNow();
                    throwRuntimeException();
                }

                try
                {
                    Thread.sleep( 100 );
                }
                catch( InterruptedException e )
                {
                    break;
                }
            }
        }
        catch( RuntimeException re )
        {
            debug.print( 1, "Runtime Error bei ARP Abfrage: " + re.getMessage() );
            state = false;
            throw new RuntimeException( re );
        }
        catch( Exception e )
        {
            debug.print( 1, "Fehler bei ARP Abfrage: " + e.getMessage() );
            return erg;
        }
        finally
        {
            if( state == true )
            {   
                if (handle != null && handle.isOpen())
                {
                    handle.close();
                }
                if (sendHandle != null && sendHandle.isOpen())
                {
                    sendHandle.close();
                }
                if (pool != null && !pool.isShutdown())
                {
                    pool.shutdown();
                }
            }

            debug.print( 2, destinationIP + " was resolved to " + resolvedAddr );
        }
        return resolvedAddr.toString();
    }

    private void throwRuntimeException()
    {
        throw new RuntimeException( "RunTime Fehler bei " + destinationIP );
    }

    private static class Task implements Runnable
    {

        private PcapHandle handle;
        private PacketListener listener;

        public Task(PcapHandle handle, PacketListener listener)
        {
            this.handle = handle;
            this.listener = listener;
        }

        @Override
        public void run()
        {
            try
            {
                handle.loop(COUNT, listener);
            }
            catch (PcapNativeException e)
            {
                e.printStackTrace();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            catch (NotOpenException e)
            {
                e.printStackTrace();
            }
        }

    }

}[/CODE]


----------



## Oneixee5 (4. Jan 2022)

Einen Thread kann man beenden indem man `thread.interrupt();` aufruft. Der Thread muss dann auf das interrupt-Signal reagieren.

```
while (!thread.isInterrupted()) {
    System.out.println("running");
}
System.out.println("stopped.");
```
Man kann das Verhalten auch mit einer boolean-Variable erreichen.
Reagiert der Thread nicht auf das interrupt-Signal wird er weiterlaufen bis er irgendwann von selbst endet oder die VM gekillt wird.

`ExecutorService.schutdownNow()` stoppt nicht die einzelnen Threads, es gibt keine Versuche die Verarbeitung aktiv ausgeführter Aufgaben anzuhalten.


----------



## kneitzel (4. Jan 2022)

Die Frage ist doch erst einmal: Welcher Thread ist das denn genau? Und wo wird welche Exception geworfen? Wenn die in diesem Thread geworfen wurde und der Thread diese Exception nicht behandelt hat, dann ist der Thread auch beendet. Daher scheint es das ja nicht zu sein.

Ist es evtl. der Task mit dem handle.loop?

Wenn der PcapHandle noch aktiv ist, dann wäre dieser Task per breakLoop() und close() bestimmt zu beenden.

Generell ist immer die Frage: Was läuft denn in dem Thread, den Du stoppen können willst? Und wie kannst Du das unterbrechen? Und wenn es da noch keine Möglichkeit gibt, dann muss man da was bauen.


----------



## freehawk (4. Jan 2022)

Hallo zusammen,

genau das ist der Task mit dem handle.loop.

Ich habe jetzt die Funktionen breakLoop benutzt bei dem PcapHandle und jetzt ist der Fall der hängen bleibenden Threads erstmal nicht mehr aufgetaucht. Manchmal sieht man einfach den Wald vor lauter Bäumen nicht. Danke für den Tipp.

Gruß
Markus


----------

