Toon posts:

[java] TCP socket server problemen...

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Voor een hobby projectje ben ik bezig met een logger op basis van een java ServerSocket. Ik heb hier een Siemens PLC die TCP berichtjes stuurt en wacht op antwoord. In het voorbeeld hieronder heb ik de code uitgekleed en stuur ik het bericht zoals hij ontvangen wordt weer terug. Op zich heb ik het redelijk draaiend maar ik liep tegen het probleem aan dat alles in een "while(true)" loop wel erg veel CPU belasting geeft. Een zoektocht leverde een "Runnable Thread" op. Dit geeft aanzienlijk minder CPU belasting maar de helft van de berichten worden laat of niet afgehandeld. Dit is in de "while (true)" situatie niet het geval. Ik heb zelf het vermoeden dat door de code uit te voeren als een Thread the priortijd relatief laag is en op die manier hij berichten "mist".

Mijn vraag:
Mijn vraag is dus eigenlijk hoe kan ik mijn java code zo inrichten dat het zowel niet te veel resources gebruikt en tegelijkertijd goed bereikbaar is?

Relevante software en hardware die ik gebruik
Voor het programmeren/debuggen gebruik ik IntellJ community 2019.2. Als java versie draait hier OpenJDK 11.0.4 in een Ubuntu-Mate 19.04 desktop. De server moet op een Pi3+ gaan draaien. Om de TCP berichten te "simuleren" gebruik ik Packet Sender om het TCP verkeer te debuggen gebruik ik Wireshark

Wat ik al gevonden of geprobeerd heb
Ik ben ook aan het hobbyen geweest met synchronized Threads (Thread.wait() en Thread.notify()) maar dit leide tot nog veel meer fouten in de TCP afhandeling en zelfs disconnect.
Hieronder vind je een omschrijving van hoe de PLC handelt en de 2 stukjes "werkende" code die ik nu heb. Ook een de verschillen laat ik zien tussen de 2 zowel het data verkeer als de CPU belasting op de Pi (op de desktop is het net zo gek alleen kan ik daar het data verkeer niet mooi in beeld krijgen) Voor beide java classen is de zelfde PLC met de zelfde code gebruikt. Ik verwacht dat er maximaal 5 berichten per seconde verstuurt moeten kunnen worden. De PLC kan dit op de slofjes dus Java moet dit ook kunnen ;) maar hoe?

De PLC handelt de berichten als volgt af:
  • Met een "trigger" wordt er een bericht in een FIFO datablock geplaatst.
  • Als er 1 of meerdere berichten in de FIFO staan verstuurt hij het eerste bericht met TCP
  • als dit bericht succesvol verzonden is (ik geloof dat de trigger hiervoor een ACK berichtje i, maar ik ben hier niet 100% zeker van)
  • De PLC gaat nu wachten op antwoord "confirm" van de TCP server (in dit geval is dit het zelfde berichtje dat terug gestuurd wordt)
  • Als deze is ontvangen is kijk de PLC of het bericht de zelfde Header en ID heeft
  • Als deze goed zijn kan het het eventuele volgende bericht verstuurd worden
Met de "while (true)" loop:
Afbeeldingslocatie: https://gallery.diks.me/_data/i/upload/2019/11/04/20191104104406-c6c49049-me.png
Afbeeldingslocatie: https://gallery.diks.me/upload/2019/11/04/20191104102941-b68cf0e7.png
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerOLD {
    private ServerSocket ss;

    public static void main(String[] args) {
        new TcpServerOLD();       // call the constructor
    }

    public TcpServerOLD() {
        try {
            this.ss = new ServerSocket(4444);
            (new Thread(() -> {
                try {
                    this.startAccept();
                } catch (IOException ignored) { }
            })).start();
            System.out.println(InetAddress.getLocalHost().getHostAddress());    // print the IP-Address
            System.out.println("4444");     // print the Port
        } catch (IOException ignored) { }
    }

    public void startAccept() throws IOException {
        while(true) {
            try {
                Socket so = this.ss.accept();
                DataInputStream readBuffer = new DataInputStream(so.getInputStream());
                DataOutputStream writeBuffer = new DataOutputStream(so.getOutputStream());
                (new Thread(() -> {
                    while(true) {
                        try {
                            if (readBuffer.available() > 0) {
                                byte[] message = new byte[64];
                                int len = readBuffer.read(message, 0, 64);
                                if (len > 0) {
                                    byte[] received = new byte[len];

                                    for(int i = 0; i < len; ++i) {
                                        received[i] = message[i];
                                    }
                                    
                                    byte[] returnMessage = received;
                                    writeBuffer.write(returnMessage);
                                    writeBuffer.flush();
                                }
                            }
                        } catch (IOException ignored) {}
                    }
                })).start();
            } catch (IOException ignored) { }
        }
    }
}


Met de "Runnable Thread":
Afbeeldingslocatie: https://gallery.diks.me/i.php?/upload/2019/11/04/20191104104406-f659227e-me.png
Afbeeldingslocatie: https://gallery.diks.me/upload/2019/11/04/20191104102941-5e190fd1.png
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import java.io.*;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.net.Socket;
/**
 * This class is for setting up a TCP server and wait for incoming messages from their clients
 */
public class TcpServerNEW {
    private ServerSocket ss;
    private DataInputStream readBuffer;
    private DataOutputStream writeBuffer;

    /**
     * main, the initializer
     */
    public static void main(String[] args) {
        new TcpServerNEW();       // call the constructor
    }

    /**
     * TcpServerNEW Constructor
     */
    public TcpServerNEW() {
        try {
            ss = new ServerSocket(4444);    // create the ServerSocket
            new Thread(new Runnable() {     // create new Thread for listening to new clients
                public void run() {
                    startAccept();          // run the accept-clients method
                }
            }).start();     // don't forget to start the thread
            System.out.println(InetAddress.getLocalHost().getHostAddress());    // print the IP-Address
            System.out.println("4444");     // print the Port
        } catch (Exception ignored) { }
    }

    /**
     * startAccept wil start the TCP socket server and does the reading and writing of the buffers.
     */
    public void startAccept() {
        while (true) {      // listen to new clients
            try {
                Socket   so = ss.accept();    // accept the clients and create reader/writer
                readBuffer  = new DataInputStream(so.getInputStream());
                writeBuffer = new DataOutputStream(so.getOutputStream());
                new Thread(new Runnable() {     // start new thread for reading from the client
                    public void run() {
                        try {
                            if (readBuffer.available() > 0) {
                                byte[] message = new byte[64];
                                int len = readBuffer.read(message, 0, 64);            // read receive buffer
                                if (len > 0) {
                                    byte[] received = new byte[len];

                                    for (int i = 0; i < len; i++) {                 // temporary the rebuild reply (just send the same message back)
                                        received[i] = message[i];
                                        System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format
                                    }
                                    System.out.println(" end");

                                    byte[] confirm = received; //handleMessage(received,len);

                                    writeBuffer.write(confirm); // write the message to send
                                    writeBuffer.flush();
                                }

                            }
                        } catch (IOException ignored) { }
                    }
                }).start();     // don't forget to start the thread
            } catch (Exception ignored) { }
        }
    }
}

Beste antwoord (via Verwijderd op 13-11-2019 09:11)


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16:32

Janoz

Moderator Devschuur®

!litemod

Verwijderd schreef op maandag 4 november 2019 @ 13:58:
De PLC houd de verbinding in stand. Als ik alle berichten uit schakel zie je ook "keep alive" berichtjes langs komen.
1. Waarom zou dat niet gaan? heeft dit met de opbouw van de threads te maken? of heeft het te maken met de plaats van de accept()
OM het allemaal even niet te druk te maken focus ik even hier op. Als eerste kijken we even wat de verschillende methoden doen:

ServerSocket.accept() is ene blocking call. Deze wacht tot er een verbinding binnenkomt. In dit geval is dat dus een PLC die een verbinding maakt met je applicatie. Vervolgens kan de binnegekomen data uit de inputstream gelezen worden en de uitgestuurde data naar de outputstream geschreven worden. Dit kan doorgaan totdat de verbinding afgesloten is (doordat de java de verbinding sluit, of doordat de plc de verbinding sluit).

Kijken we nu even naar je code (ik gebruik de code uit Verwijderd in "[java] TCP socket server problemen..." )


Op regel 12 wacht de code tot er een verbinding gemaakt wordt. Wanneer de verbinding gemaakt wordt wordt er een input en outputstream opgevraagd en start je een nieuwe thread. Deze thread wordt gelijk gestart. De originele lus gaat parallel verder en wacht vervolgens weer bij regel 12 tot een volgende verbinding binnen komt.

Binnen de thread wordt gekeken of er data beschikbaar is. Laten we voor nu even aannemen dat dit zo is. op regel 20 wordt vervolgens 1x een poging gedaan wat te lezen tot een maximum van 64 bytes. Dit bericht kopieer je en stuur je vervolgens terug op regel 32 en daarna is deze thread afgelopen en wordt er niks meer gelezen. Ook wordt de socket niet geclosed dus de PLC kan nog rustig dingen blijven sturen, maar dat zal nooit gelezen worden.

En dit is alleen nog wanneer de data gelijk beschikbaar is. We praten hier over IO. Het moment waarop alles aankomt is niet zomaar vastgelegd. Misschien is de socket al wel open maar moet de eerste byte nog binnen komen. Regel 18 zal dan false opleveren en op dat moment wordt er uberhaupt niks gelezen, maar zeker ook niks opgeruimd.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'

Alle reacties


Acties:
  • +1 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16:32

Janoz

Moderator Devschuur®

!litemod

InputStream.read en ServerSocket.accept() zijn blocking calls. Het lijkt me niet logisch dat daar je cpu load vandaan komt. Ook hoe je threads gebruikt voegt volgens mij niet heel veel toe. Wat probeer je te bereiken? Threads gebruik je om meerdere taken parallel af te handelen.

Binnen een dergelijke server applicatie is eigenlijk de enige plek waar ik iets met threads verwacht wanneer er meerdere clients tegelijk verbinden. Op zich gebeurt dit soort van binnen de thread die je aanmaakt in de startAccept(), maar vervolgens gebruik je in elke thread dezelfde read en write buffer. Effectief gaat een tweede connectie er voor zorgen dat de eerste connectie niet meer werkt en zijn resultaat naar de tweede connectie sturen.

Hoe verbind die Siemens PLC? Maakt hij een verbinding, stuurt hij een bericht, wacht hij op een antwoord en sluit hij dan de verbinding? Of houdt hij de verbinding open en blijft hij berichten sturen? Dat tweede kan nu niet met je huidige implementatie.


In het kort. Haal sowieso die threadcode in TCPServer weg, die voegt niks toe. Zorg er daarna voor dat je duidelijk hebt waar je load werkelijk vandaan komt ipv maar lukraak wat threadcode toe te voegen. InputStream.read en ServerSocket.accept() zijn allebei blocking. Dus zolang er geen data is zou de load zo goed als niks moeten zijn.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bedankt voor je reactie,
Janoz schreef op maandag 4 november 2019 @ 12:57:
InputStream.read en ServerSocket.accept() zijn blocking calls. Het lijkt me niet logisch dat daar je cpu load vandaan komt. Ook hoe je threads gebruikt voegt volgens mij niet heel veel toe. 1 Wat probeer je te bereiken? Threads gebruik je om meerdere taken parallel af te handelen.

Binnen een dergelijke server applicatie is eigenlijk de enige plek waar ik iets met threads verwacht wanneer er meerdere clients tegelijk verbinden. Op zich gebeurt dit soort van binnen de thread die je aanmaakt in de startAccept(), 2 maar vervolgens gebruik je in elke thread dezelfde read en write buffer. Effectief gaat een tweede connectie er voor zorgen dat de eerste connectie niet meer werkt en zijn resultaat naar de tweede connectie sturen.
1. Als het een succes wordt dan is het wel het idee om er in de toekomst meerdere PLC's mee te laten verbinden. Maar voorlopig is het een hobby projectje en zal het beperkt blijven tot 1 PLC
2. Hier heb je een punt in de "oude" code haal ik de read en write buffer classes in de Thread aan. In de "nieuwe" code defineer ik ze gelijk als private met de hele class. Bij meerdere verbindingen geeft dit problemen natuurlijk Ik denk dat dit "in de war" geraakt is door mijn zoektocht.
Hoe verbind die Siemens PLC? Maakt hij een verbinding, stuurt hij een bericht, wacht hij op een antwoord en sluit hij dan de verbinding? Of houdt hij de verbinding open en blijft hij berichten sturen? 1 Dat tweede kan nu niet met je huidige implementatie.
De PLC houd de verbinding in stand. Als ik alle berichten uit schakel zie je ook "keep alive" berichtjes langs komen.
1. Waarom zou dat niet gaan? heeft dit met de opbouw van de threads te maken? of heeft het te maken met de plaats van de accept()
In het kort. Haal sowieso die threadcode in TCPServer weg, die voegt niks toe. Zorg er daarna voor dat je duidelijk hebt waar je load werkelijk vandaan komt ipv maar lukraak wat threadcode toe te voegen. InputStream.read en ServerSocket.accept() zijn allebei blocking. Dus zolang er geen data is zou de load zo goed als niks moeten zijn.
Voor mij is dit het eerste java project ;) ik heb er een boek over gelezen en onder tussen redelijk wat kunnen spelen. Je hebt inderdaad gelijk dat hij er "lukraak" staat ik heb er nog niet echt een gevoel bij wat de invloed is van een Thread. Dit is de reden ook waarom dit topic geopend is ;)

Ik heb de thread uit de TcpServer gehaald en het ziet er nu zo uit als hieronder. Wat mij nu op valt is ik kan de server starten als ik er een berichtje heen stuur gaat de load +/- 20% omhoog verstuur ik er nog een gaat hij weer 20% omhoog etc etc. Dus daar hebben we hem... er blijft iets hangen alleen wat? Ik ga straks even wat dingen uit schakelen / verplaatsen eens kijken wat er gebeurt.
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    public TcpServerNEW() {
        try {
            ss = new ServerSocket(4444);    // create the ServerSocket
            System.out.println(InetAddress.getLocalHost().getHostAddress());    // print the IP-Address
            startAccept();
        } catch (Exception ignored) { }
    }

    public void startAccept() {
        while (true) {      // listen to new clients
            try {
                Socket   so = ss.accept();    // accept the clients and create reader/writer
                DataInputStream readBuffer  = new DataInputStream(so.getInputStream());
                DataOutputStream writeBuffer = new DataOutputStream(so.getOutputStream());
                new Thread(new Runnable() {     // start new thread for reading from the client
                    public void run() {
                        try {
                            if (readBuffer.available() > 0) {
                                byte[] message = new byte[64];
                                int len = readBuffer.read(message, 0, 64);            // read receive buffer
                                if (len > 0) {
                                    byte[] received = new byte[len];

                                    for (int i = 0; i < len; i++) {                 // temporary the rebuild reply (just send the same message back)
                                        received[i] = message[i];
                                        System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format
                                    }
                                    System.out.println(" end");

                                    byte[] confirm = received; //handleMessage(received,len);

                                    writeBuffer.write(confirm); // write the message to send
                                    writeBuffer.flush();
                                }

                            }
                        } catch (IOException ignored) { }
                    }
                }).start();     // don't forget to start the thread
            } catch (Exception ignored) { }
        }
    }

Acties:
  • Beste antwoord
  • +1 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 16:32

Janoz

Moderator Devschuur®

!litemod

Verwijderd schreef op maandag 4 november 2019 @ 13:58:
De PLC houd de verbinding in stand. Als ik alle berichten uit schakel zie je ook "keep alive" berichtjes langs komen.
1. Waarom zou dat niet gaan? heeft dit met de opbouw van de threads te maken? of heeft het te maken met de plaats van de accept()
OM het allemaal even niet te druk te maken focus ik even hier op. Als eerste kijken we even wat de verschillende methoden doen:

ServerSocket.accept() is ene blocking call. Deze wacht tot er een verbinding binnenkomt. In dit geval is dat dus een PLC die een verbinding maakt met je applicatie. Vervolgens kan de binnegekomen data uit de inputstream gelezen worden en de uitgestuurde data naar de outputstream geschreven worden. Dit kan doorgaan totdat de verbinding afgesloten is (doordat de java de verbinding sluit, of doordat de plc de verbinding sluit).

Kijken we nu even naar je code (ik gebruik de code uit Verwijderd in "[java] TCP socket server problemen..." )


Op regel 12 wacht de code tot er een verbinding gemaakt wordt. Wanneer de verbinding gemaakt wordt wordt er een input en outputstream opgevraagd en start je een nieuwe thread. Deze thread wordt gelijk gestart. De originele lus gaat parallel verder en wacht vervolgens weer bij regel 12 tot een volgende verbinding binnen komt.

Binnen de thread wordt gekeken of er data beschikbaar is. Laten we voor nu even aannemen dat dit zo is. op regel 20 wordt vervolgens 1x een poging gedaan wat te lezen tot een maximum van 64 bytes. Dit bericht kopieer je en stuur je vervolgens terug op regel 32 en daarna is deze thread afgelopen en wordt er niks meer gelezen. Ook wordt de socket niet geclosed dus de PLC kan nog rustig dingen blijven sturen, maar dat zal nooit gelezen worden.

En dit is alleen nog wanneer de data gelijk beschikbaar is. We praten hier over IO. Het moment waarop alles aankomt is niet zomaar vastgelegd. Misschien is de socket al wel open maar moet de eerste byte nog binnen komen. Regel 18 zal dan false opleveren en op dat moment wordt er uberhaupt niks gelezen, maar zeker ook niks opgeruimd.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Janoz schreef op maandag 4 november 2019 @ 14:21:
...
ServerSocket.accept() is ene blocking call. Deze wacht tot er een verbinding binnenkomt. In dit geval is dat dus een PLC die een verbinding maakt met je applicatie. Vervolgens kan de binnegekomen data uit de inputstream gelezen worden en de uitgestuurde data naar de outputstream geschreven worden. Dit kan doorgaan totdat de verbinding afgesloten is (doordat de java de verbinding sluit, of doordat de plc de verbinding sluit).
Ok nu weet ik wat een block is, het staat ook in de documentatie... link
Kijken we nu even naar je code (ik gebruik de code uit Verwijderd in "[java] TCP socket server problemen..." )

Op regel 12 wacht de code tot er een verbinding gemaakt wordt. Wanneer de verbinding gemaakt wordt wordt er een input en outputstream opgevraagd en start je een nieuwe thread. Deze thread wordt gelijk gestart. De originele lus gaat parallel verder en wacht vervolgens weer bij regel 12 tot een volgende verbinding binnen komt.

Binnen de thread wordt gekeken of er data beschikbaar is. Laten we voor nu even aannemen dat dit zo is. op regel 20 wordt vervolgens 1x een poging gedaan wat te lezen tot een maximum van 64 bytes. Dit bericht kopieer je en stuur je vervolgens terug op regel 32 en daarna is deze thread afgelopen en wordt er niks meer gelezen. Ook wordt de socket niet geclosed dus de PLC kan nog rustig dingen blijven sturen, maar dat zal nooit gelezen worden.

En dit is alleen nog wanneer de data gelijk beschikbaar is. We praten hier over IO. Het moment waarop alles aankomt is niet zomaar vastgelegd. Misschien is de socket al wel open maar moet de eerste byte nog binnen komen. Regel 18 zal dan false opleveren en op dat moment wordt er uberhaupt niks gelezen, maar zeker ook niks opgeruimd.
- Een lang verhaal kort... de ss.accept() wil ik dus 1 keer aanroepen en niet na ieder berichtje opnieuw wachten op een nieuwe verbinding... deze heb ik dus in de TcpServer gezet. Ik wacht nu op verbinding en schop dan startAccept() aan (die nu een andere naam moet hebben :+) om dit voor meerdere verbindingen mogelijk te maken moet ik de accept() dus ook in een thread gooien. Dit laat ik nu nog even achterwege ik als ik het begrijp haak ik hier wel weer op in.
- Om de berichten te blijven ontvangen moeten de controle (available()) of er data is om te verwerken in een while true loop. deze heb ik er (weer) in staan.

Helaas zit mijn raspberry CPU nog in de stres maar het "opbouwen" van de stres is niet meer van toepassing. Mijn i7 kan hem heb wel redelijk goed handelen

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public TcpServerNEW() {
    try {
        ss = new ServerSocket(4444);    // create the ServerSocket
        so = ss.accept();    // accept the clients and create reader/writer
        Socket so = ss.accept();    // accept the clients and create reader/writer
        System.out.println(InetAddress.getLocalHost().getHostAddress());    // print the IP-Address
        System.out.println("4444");     // print the Port
        startAccept();
    } catch (Exception ignored) { }
}

/**
 * startAccept wil start the TCP socket server and does the reading and writing of the buffers.
 */
public void startAccept() {
    while (true) {      // listen to new clients
        try {
            DataInputStream readBuffer = new DataInputStream(so.getInputStream());
            DataOutputStream writeBuffer = new DataOutputStream(so.getOutputStream());
            new Thread(new Runnable() {     // start new thread for reading from the client
                public void run() {
                    while (true) {
                        try {
                            if (readBuffer.available() > 0) {
                                byte[] message = new byte[64];
                                int len = readBuffer.read(message, 0, 64);            // read receive buffer
                                if (len > 0) {
                                    byte[] received = new byte[len];

                                    for (int i = 0; i < len; i++) {                 // temporary the rebuild reply (just send the same message back)
                                        received[i] = message[i];
                                        System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format
                                    }
                                    System.out.println(" end");

                                    byte[] confirm = received; //handleMessage(received,len);

                                    writeBuffer.write(confirm); // write the message to send
                                    writeBuffer.flush();
                                }
                            }
                        } catch (IOException ignored) {
                        }
                    }
                }
            }).start();     // don't forget to start the thread
        } catch (Exception ignored) {
        }
    }
}

Acties:
  • +1 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
Je applicatie accepteert nog steeds maar 1 verbinding en probeert na het wegvallen van de verbinding deze in stand te houden.

Ik zou het als volgt doen:

TCPServer:

Deze maakt een server socket aan. Vervolgens maak je een loop die binnenkomende verbindingen accepteert. Zodra een binnenkomende verbinding opgezet is maakt deze een TCPServerConnection aan.

TCPServerConnection (Runnable):

Deze ontvangt een client socket en maakt de read en write objecten aan. Vervolgens maak je een loop die de binnenkomende berichten afhandelt.

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Alain bedankt voor je reactie. Ik moest even kijken wat je bedoelde maar ik snap hem helemaal. Ik heb het nu aangepast zoals hier onder. Het probleem wat ik nu nog heb is dat als ik de verbinding verlies (stekker eruit), blijft de oude thread nog actief op de achter grond. Als de verbinding dan hersteld wordt dan start er wel een nieuwe thread die goed communiceert.

Ik heb in regel 57 een if(read()<0) toegevoegd (bron) maar deze voorwaarde treed helaas niet op. Hoe kan ik dit nou detecteren en zo de Thread stoppen?

De server belasting op de Pi is nog vrij hoog (java 80-100% van CPU 1 kern bij 1 verbinding). Iemand hier nog tips voor? Ik was al even aan het prutsen met een sleep maar ik geloof niet dat dit de oplossing is.

Het begint (eindelijk) een beetje te dagen hoe het met een thread werkt.
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public TcpServerNEW() {
    try {
        ss = new ServerSocket(4444);    // create the ServerSocket
        System.out.println(InetAddress.getLocalHost().getHostAddress());    // print the IP-Address
        System.out.println("4444");     // print the Port

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        int i = 0;
                        Socket  so = ss.accept();// wait for incoming connections
                        startAccept(so);
                        System.out.println("start " + i);
                        i++;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * startAccept wil start the TCP socket server and does the reading and writing of the buffers.
 */
public void startAccept(Socket socket) {
        new Thread(new Runnable() {     // start new thread for reading from the client
            public void run() {
                try {
                    boolean check = false;
                    DataInputStream readBuffer = new DataInputStream(socket.getInputStream());
                    DataOutputStream writeBuffer = new DataOutputStream(socket.getOutputStream());
                    while (true) {
                        try {
                            if (readBuffer.available() > 0) {
                                byte[] message = new byte[64];
                                int len = readBuffer.read(message, 0, 64);            // read receive buffer
                                if (len > 0) {
                                    byte[] received = new byte[len];

                                    for (int i = 0; i < len; i++) {                 // temporary the rebuild reply (just send the same message back)
                                        received[i] = message[i];
                                        System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format
                                    }
                                    System.out.println(" end");

                                    byte[] confirm = received; //handleMessage(received,len);

                                    writeBuffer.write(confirm); // write the message to send
                                    writeBuffer.flush();
                                } else {
                                    if ((readBuffer.read()<0) && !check) {
                                        System.out.println("end of connection");
                                        check = true;
                                    }
                                    Thread.sleep(1);
                                }
                            }
                        } catch (IOException | InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();     // don't forget to start the thread
}

Acties:
  • +1 Henk 'm!

  • Gropah
  • Registratie: December 2007
  • Niet online

Gropah

Admin Softe Goederen

Oompa-Loompa 💩

Het is een tijd geleden dat ik java heb gedaan, maar:

available lijkt niet een blocking method te zijn. Als er niets available is, doet hij dus basically een while true. Ja, dan blijft je processor wel bezig. Je hebt dus een blocking call nodig op je input stream. Gelukkig heeft je DataInputStream daar genoeg van, dus ik zou zeggen, pak daar 1 van. Dan blockt de call tot er een fout op treed, of er data binnen komt, en volgens mij is dat precies wat je zoekt...

Acties:
  • +1 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Een extra blocking call hoeft niet, om het snel te fixen kun je waarschijnlijk regel 40 en 63 gewoon weghalen. Aangezien je toch wacht op available > 0, kun je net zo goed gelijk een blocking call doen. "byte[] message = new byte[64];" in de loop is niet heel efficiënt, want die kan hergebruikt worden. Wat er nu bij EOF gebeurd snap ik ook niet, lijkt me dat dit juist met die available check mis gaat, maar ook daarna lijkt het mij dat je wil stoppen met proberen te lezen en niet in de loop wil blijven hangen.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • +1 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
Verwijderd schreef op dinsdag 5 november 2019 @ 21:23:
Alain bedankt voor je reactie. Ik moest even kijken wat je bedoelde maar ik snap hem helemaal. Ik heb het nu aangepast zoals hier onder. Het probleem wat ik nu nog heb is dat als ik de verbinding verlies (stekker eruit), blijft de oude thread nog actief op de achter grond. Als de verbinding dan hersteld wordt dan start er wel een nieuwe thread die goed communiceert.
Op regel 57 zou je op len moeten checken. Nu probeer je eerst weer te lezen (blocking) en hangt je thread.

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Alain schreef op dinsdag 5 november 2019 @ 22:50:
[...]


Op regel 57 zou je op len moeten checken. Nu probeer je eerst weer te lezen (blocking) en hangt je thread.
Op zich een verbetering, maar de documentatie lezen die hierboven gelinkt staat is vast een goed idee:
If no byte is available because the end of the stream has been reached, the value -1 is returned. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.
Nu gebruik ik altijd iets als netty en nooit direct een socket, en heb ik dit dus nooit uitgeprobeerd, maar het lijkt me dat de thread niet hangt door read() aan te roepen. Deze hangt waarschijnlijk doordat available <= 0 blijft bij EOF.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
pedorus schreef op dinsdag 5 november 2019 @ 23:01:
[...]

Op zich een verbetering, maar de documentatie lezen die hierboven gelinkt staat is vast een goed idee:

[...]

Nu gebruik ik altijd iets als netty en nooit direct een socket, en heb ik dit dus nooit uitgeprobeerd, maar het lijkt me dat de thread niet hangt door read() aan te roepen. Deze hangt waarschijnlijk doordat available <= 0 blijft bij EOF.
Klopt. Er zijn meerdere mogelijkheden waarop de thread kan hangen. :P

Ik vond het alleen niet nodig om jou / jullie reactie te herhalen. ;)

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
pedorus schreef op dinsdag 5 november 2019 @ 22:20:
Een extra blocking call hoeft niet, om het snel te fixen kun je waarschijnlijk regel 40 en 63 gewoon weghalen. Aangezien je toch wacht op available > 0, kun je net zo goed gelijk een blocking call doen. "byte[] message = new byte[64];" in de loop is niet heel efficiënt, want die kan hergebruikt worden. Wat er nu bij EOF gebeurd snap ik ook niet, lijkt me dat dit juist met die available check mis gaat, maar ook daarna lijkt het mij dat je wil stoppen met proberen te lezen en niet in de loop wil blijven hangen.
Ik moet bekennen dat ik hier een typo in de bracelets heb zitten... eigenlijk was dit de bedoeling:
Java:
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
if (readBuffer.available() > 0) {
    byte[] message = new byte[64];
    int len = readBuffer.read(message, 0, 64);            // read receive buffer
    if (len > 0) {
        byte[] received = new byte[len];

        for (int i = 0; i < len; i++) {                 // temporary the rebuild reply (just send the same message back)
            received[i] = message[i];
            System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format
        }
        System.out.println(" end");

        byte[] confirm = received; //handleMessage(received,len);

        writeBuffer.write(confirm); // write the message to send
        writeBuffer.flush();
    }
} else {
    if ((readBuffer.read()<0) && !check) {
        System.out.println("end of connection");
        check = true;
    }
    Thread.sleep(1);
}
uiteraard is dit nog niet handig omdat read() blocking is en wacht op inkomende data. Op deze manier zul je alleen met geluk wat data "vangen" ;)

Ik ga opzoek naar een goed methode om de thread af te sluiten. Ik heb begrepen dat Thread.stop() niet de manier is. Ik ga in ieder geval hier mee verder:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
boolean check = false;
DataInputStream readBuffer = new DataInputStream(socket.getInputStream());
DataOutputStream writeBuffer = new DataOutputStream(socket.getOutputStream());
byte[] message = new byte[64];
while (true) {
    try {
            int len = readBuffer.read(message, 0, 64);            // read receive buffer
            if (len > 0) {
                byte[] received = new byte[len];

                for (int i = 0; i < len; i++) {                 // temporary the rebuild reply (just send the same message back)
                    received[i] = message[i];
                    System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format
                }
                System.out.println(" end");

                byte[] confirm = received; //handleMessage(received,len);

                writeBuffer.write(confirm); // write the message to send
                writeBuffer.flush();
            }  else {
                if (len < 0 && !check) {
                    System.out.println("end of connection");
                    check = true;
                }
            }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Ik heb dit nog niet getest op de Pi maar op mijn Celeron laptopje hier het ziet het er nog wat "zwaar" uit. (Java gebruikt 100% van 1 core)

Kleine vraag nog: ik zie dat jullie linken naar de "FilterInputStream", hier zitten minder blocking calls in dan in InputStream. De Thread.read() zoals ik hem nu gebruik uit geeft in FilterInputStream geen -1 terug dus deze zal niet werken. Was dit bedoeld als schop in de goede richting? (zo ja dan snap ik hem even niet ;) )

EDIT:
deze thread beëindigen is eigenlijk vrij simpel 8)7 [link] :
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
boolean threadKeepAlive = true;
DataInputStream readBuffer = new DataInputStream(socket.getInputStream());
DataOutputStream writeBuffer = new DataOutputStream(socket.getOutputStream());
byte[] message = new byte[64];
while (threadKeepAlive) {
    try {
        int len = readBuffer.read(message, 0, 64);            // read receive buffer
        if (len > 0) {
            byte[] received = new byte[len];

            for (int i = 0; i < len; i++) {                 // temporary the rebuild reply (just send the same message back)
                received[i] = message[i];
                System.out.print(String.format("%02X", message[i]) + " "); // show message bytes in hex format
            }
            System.out.println(" end");

            byte[] confirm = received; //handleMessage(received,len);

            writeBuffer.write(confirm); // write the message to send
            writeBuffer.flush();
        }  else {
            if (len < 0) {
                System.out.println("end of connection");
                threadKeepAlive = false;
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

[ Voor 15% gewijzigd door Verwijderd op 06-11-2019 09:38 . Reden: verandering inzicht ]


Acties:
  • +1 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Verwijderd schreef op woensdag 6 november 2019 @ 07:25:
Ik ga opzoek naar een goed methode om de thread af te sluiten. Ik heb begrepen dat Thread.stop()
niet de manier is. Ik ga in ieder geval hier mee verder:
Een methode in een thread is niet anders dan iedere andere java-methode. Met return komt je er uit, of door uit de while loop te komen. Wat ook nog zou kunnen is dat op een gegeven moment het schrijven vastloopt bijvoorbeeld omdat er meer dan 64 bytes aan input was en er niet gelezen wordt en de buffers vollopen. write+flush kan ook blocking zijn.
Ik heb dit nog niet getest op de Pi maar op mijn Celeron laptopje hier het ziet het er nog wat "zwaar" uit. (Java gebruikt 100% van 1 core)
wat opties:
-in de debugger op pauze drukken en kijken wat er op dat moment gebeurd
-met een profiler als visualvm kijken wat die threads doen
-https://helpx.adobe.com/nl/experience-manager/kb/TakeThreadDump.html
Kleine vraag nog: ik zie dat jullie linken naar de "FilterInputStream", hier zitten minder blocking calls in dan in InputStream. De Thread.read() zoals ik hem nu gebruik uit geeft in FilterInputStream geen -1 terug dus deze zal niet werken. Was dit bedoeld als schop in de goede richting? (zo ja dan snap ik hem even niet ;) )
hoezo? misschien inderdaad logischer om niet de bovenliggende te pakken:
This method blocks until input data is available, end of file is detected, or an exception is thrown.

If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at end of file, the value -1 is returned; otherwise, at least one byte is read and stored into b.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • +1 Henk 'm!

  • igmar
  • Registratie: April 2000
  • Laatst online: 29-09 20:15

igmar

ISO20022

Als ik je een advies mag geven : Gebruik https://netty.io/
Dat lost in een keer een hoop issues op, en is waarschijnlijk ook nog eens een stuk sneller.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
@pedorus Bedankt voor de tip "VisualVM" een erg mooie tool om de boel inzichtelijk mee te krijgen. Ik had de rest van de code om CSV bestandjes te vullen (OpenCSV) ook geïmplementeerd op de Pi. Het bleek dat hier een CsvMalformedException uit een oud CSV bestand kwam. Door deze af te handelen en het bestand aan de kant te zetten en vervolgens nieuwe aan te maken, komt hij niet iedere keer met deze dump op de proppen. (dit was waarschijnlijk het resultaat van een eerdere vastloper). Met als resultaat dat het CPU gebruik nu op schommelt rond de 15%!

@Janoz Bedankt voor het inzichtelijk maken van wat ik nu eigenlijk aan het doen was ;) Ik weet nu wat een een blocking methode is en hoe een thread werkt en hoe ze het beste toe te passen zijn. Dit was toch een beetje de oplossing voor het probleem. Ik heb jouw antwoord daarom.

@igmar bedankt voor de tip Netty.io, dat ziet er goed en bruikbaar uit. Ik heb het nu redelijk draaien ik verwacht met kerst wat tijd te hebben dan ga ik er wat beter naar kijken.

Iedereen bedankt voor het meedenken!
Pagina: 1