Monitoring Problem



  • Hallo Leute,

    ich hoffe ihr könnt mir bei folgender Problemstellung helfen:

    Aus einer LabView-Anwendung sollen aus einer C-Anwendung heraus über das Netzwerk Statuswerte abgeholt werden. Dabei ist es wichtig das die Daten in der richtigen Reihenfolge ankommen, also TCP für den Weg von LabView zum C-Programm.

    LabViw-seitig sieht es momentan so aus, dass das Programm wartet bis ein TCP Port geöffnet bzw. angeprochen wird. Wenn das der Fall ist sendet die LabView Anwendung aller 2 Sekunden die Statuswert via TCP Socket heraus.

    In einem C-Programm (Linux) habe ich versucht den TCP Port von LabView anzusprechen und empfange im nachhinein auch mehr oder weniger ein paar Daten. Allerding dauert es eine ganze Weile (ca. 20 Sekunden) bis dann die Pakete ausgegeben werden.

    Ziel des Ganzen ist es, dass ich mit dem C-Programm einmalig LabView sagen "das ich da bin" und dann sendet die LV-Anwendung solange, bis die C-Anwendung beendet wird bzw. bis kein Teilnehmer mehr am anfangs beschriebenen Port verbunden ist. Bei letzterem weiß ich allerdings nicht wie ich das realisieren könnte.

    Eine zyklische Anfrage über TCP kommt auch nicht in Frage, da jede Anfrage im einzelnen zu lange dauert.

    Mein bisheriger C-Code:

    /* 
     * Aufruf: tcpclient <host> <port>
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h> 
    
    #define BUFSIZE 4096
    
    void error(char *msg) {
        perror(msg);
        exit(0);
    }
    
    int tcp = 0;
    int datasize = 0;
    void ProcessTCP(int);
    
    int main(int argc, char **argv) {
        int sockfd, portno, n;
        struct sockaddr_in serveraddr;
        struct hostent *server;
        char *hostname;
        //char buf[BUFSIZE];
        unsigned char *buf = (unsigned char *) malloc(65536);
        // Argumente lesen
        if (argc != 3) {
           fprintf(stderr,"Syntax: %s <hostname> <port>\n", argv[0]);
           exit(0);
        }
        hostname = argv[1];
        portno = atoi(argv[2]);
    
        // Socket erstellen
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) 
            error("[ Fehler ] Socket konnte nicht erstellt werden - socket()\n");
    
        // DNS Eintrag lesen
        server = gethostbyname(hostname);
        if (server == NULL) {
            fprintf(stderr,"[ Fehler ] Host konnte nicht aufgeloest werden - gethostbyname()\n", hostname);
            exit(0);
        }
    
        // Socket konfigurieren
        bzero((char *) &serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        bcopy((char *)server->h_addr, 
    	  (char *)&serveraddr.sin_addr.s_addr, server->h_length);
        serveraddr.sin_port = htons(portno);
    
        // Verbindung Server - connect()
        if (connect(sockfd, &serveraddr, sizeof(serveraddr)) < 0) {
          error("[ Fehler ] Verbindung fehlgeschlagen - connect()\n");
        }
    
        // Response empfangen
        bzero(buf, BUFSIZE);
    	while(1) {
        n = recv(sockfd, buf, 65536,0);
        if (n < 0) {
          error("[ Fehler ] Keine Daten empfangen - read()\n");
    	}
        //printf("[Response]\n %s\n", buf);
    	tcp=tcp+(n/1500)+1;
    	datasize = datasize + n;
    	printf("[TCP]  %d Bytes empfangen -> %d Pakete\n\n", n, tcp);   
    	}
    
        close(sockfd);
        return 0;
    }
    

    Ich hoffe auch eure Hilfe und gerne auch andere Ideen!

    Gruß

    Tony


  • Mod

    Was genau ist deine Frage?

    tooony schrieb:

    Ziel des Ganzen ist es, dass ich mit dem C-Programm einmalig LabView sagen "das ich da bin" und dann sendet die LV-Anwendung solange, bis die C-Anwendung beendet wird bzw. bis kein Teilnehmer mehr am anfangs beschriebenen Port verbunden ist. Bei letzterem weiß ich allerdings nicht wie ich das realisieren könnte.

    Ist letzteres denn ein Problem? Prinzipiell kann ein Server nicht feststellen, ob der Kommunikationspartner noch aktiv ist, falls dieser seine sockets nicht sauber schließt. Falls der Partner beim Beenden die sockets korrekt geschlossen hat, kann man dies da dran feststellen, dass
    recv und read 0 zurück geben. Falls der Client ein Ressourcenleck hat (auch Sockets sind eine Ressource!): Tja. Irgendwann wird deine Verbindung ein Timeout bekommen. Bis dahin sendest du eben nutzlos Daten ins Nirvana, sofern du dir Daten bloß auf Verdacht und nicht auf Anfrage sendest. Siehe nächster Absatz.

    tooony schrieb:

    Eine zyklische Anfrage über TCP kommt auch nicht in Frage, da jede Anfrage im einzelnen zu lange dauert.

    Wow, du hast aber hohe Anforderungen für eine Nachricht, die sowieso nur alle 2 Sekunden raus geht. Warum muss es denn derart exakt sein? Ein kurzes "Hi! Die letzte Nachricht ist angekommen. Ich bin noch da. Schick mir mehr!" sollte doch nur so lange dauern, wie die Netzwerklatenz, also irgendwas im Milisekundenbereich.

    tooony schrieb:

    In einem C-Programm (Linux) habe ich versucht den TCP Port von LabView anzusprechen und empfange im nachhinein auch mehr oder weniger ein paar Daten. Allerding dauert es eine ganze Weile (ca. 20 Sekunden) bis dann die Pakete ausgegeben werden.

    Suchst du eine Erklärung dafür? Beschreib mal genauer, was passiert und wie genau der Server die Daten versendet.



  • Hast Glück, dass ich gerade ein paar Tests mit einem alten HTTP-Server von mir mache.

    Der Client, den du da hast, ist komplett kaputt. Keine Ahnung, ob du hier versucht hast, ein Minimalbeispiel aufzuziehen, aber so kann das nicht funktionieren.

    1. Du verwendest bzero . Ich zitiere:

    man bzero schrieb:

    4.3BSD. This function is deprecated (marked as LEGACY in POSIX.1-2001): use memset(3) in new programs. POSIX.1-2008 removes the specification of bzero().

    2. Beim connect fehlt dir eine Konvertierung von &serveraddr in einen struct sockaddr* . Ein guter Compiler wirft hier eine Fehlermeldung; meiner wirft eine. 🙂

    3. Warum initialisiert du überhaupt den Speicher mit 0 vor dem Eintreten in die while-Schleife? Der Kernel sagt dir ja bereits, wo er dir wie viele Bytes geschrieben hat, beziehungsweise du sagst es bereits dem Kernel. Und wenn der Kernel hier Scheiße baut, hast du ganz andere Sorgen als uninitialisierten Speicher im Userland. Einen Nullterminator brauchst du beim besten Willen nicht.

    1. recv kann auch 0 zurückliefern, zum Beispiel dann, wenn keine Daten vom Socket gelesen werden können. Wenn beispielsweise noch nichts vorliegt (weil recv non-blocking liest), oder weil die Gegenseite einfach ihren Socket geschlossen hat.

    2. recv merkt sich nicht, wie viele Bytes für den Socket schon geschrieben wurde. Es wird daher immer den Wert in buf überschreiben. Du musst die Adresse nach dem letzten Byte, das geschrieben wurde, angeben.

    6. Ein Blocking von Client-Seite konnte ich nicht feststellen. Der Client holt sich blitzschnell meinen HTTP-Response. Mögliche Ursache:

    - langsames Netz im Produktivsystem
    - die LabView-Anwendung hat eine Macke, liefert die Daten nicht korrekt aus.
    - Kombination von beidem.

    Noch etwas, dass ich nicht ganz verstehe:
    Laut deiner Aussage sendet die Anwendung an alle Socket alle 2 Sekunden neue Daten. Wie kannst du dann innerhalb von 20 Sekunden den kompletten Datensatz erhalten, wenn dieser nach 2 Sekunden wieder veraltert ist? Oder bekommst du die Daten, die vor 20 Sekunden aktuell waren?

    Ansonsten: siehe SeppJ. Mehr Informationen bitte.


  • Mod

    dachschaden schrieb:

    1. recv kann auch 0 zurückliefern, zum Beispiel dann, wenn keine Daten vom Socket gelesen werden können. Wenn beispielsweise noch nichts vorliegt (weil recv non-blocking liest), oder weil die Gegenseite einfach ihren Socket geschlossen hat.

    Ich bin ja nicht gerade der große Socketexperte, aber ist recv nicht blockierend, außer wenn man das entsprechende Flag gesetzt hat (was hier nicht der Fall ist)? Es sollte warten, bis irgendeine Nachricht kommt (bzw. eine bereits vorhandene Nachricht aus dem Empfangspuffer holen).



  • dachschaden schrieb:

    Wenn beispielsweise noch nichts vorliegt (weil recv non-blocking liest), oder weil die Gegenseite einfach ihren Socket geschlossen hat.

    Kannst du auch durch "vollständigerweise" ersetzen. Ist bei mir übrigens der Fall, dass mein Server den Socket schließt. Ich prüfe einfach gesamt auf <1, weil ein recv auf einen Socket, auf den ich vorher mit select/epoll geprüft habe, dass sich was geändert hat, und bei einer ordentlichen Protokollprüfung alles unter 1 einen Fehler angibt.

    Entweder wird das Ende der Übertragung durch ein Schließen des Sockets signalisiert (0), oder man macht eine ordentliche Protokollprüfung nach jedem Stück Daten, die man eingeladen hat. Beides sehe ich hier nicht. Stattdessen eine Prüfung, ob ein Fehler vorlag. Aber ein Schließen des Sockets ist kein Fehler. Was, wenn die LabView-Anwendung plötzlich den Socket schließt? Bei seinem Programm geht der dann in eine Endlosschleife, wie ein non-blocking recv halt.

    Deswegen wollte ich das einmal komplett rumreißen - wir wissen nicht, wie die Originalimplementierung aussieht.
    Übrigens kannst du den Socket laut manpage auch mit O_NONBLOCK erstellen, zumindest unter POSIX-Systemen funzt das, weil diese keinen effektiven Unterschied zwischen Dateisocket und Netzwerksocket machen.



  • Ich bedanke mich erstmal recht herzlich für eure Antworten!

    Ich versuch die Grundsituation nochmal etwas anders zu formulieren.
    Es handelt sich um eine Steuereinheit die mit LabView arbeitet. Nun soll eine Schnittstelle entstehen, an die man sich via Netzwerk mit zum Beispiel einer C-Applikation verbindet und alle Satusdaten der Steuereinheit monitoren kann.

    Die bisherige Grundidee (wie zu beginn geschildert) ist nur ein erster Ansatz und noch nix festes. Auf Grund dessen das es sich um eine Monitoring-Anwendung handelt sollte zunächst eine zyklische Anfrage vermieden werden: Also einmal den Netwerkport Labvieseitig ansprechen und dann beginnt LabView die Statusdaten aller 2 Sekunden zyklisch zu senden, wobei die 2 Sekunden auch erstmal nur willkürlich festgelegt sind. Besser wäre wenn es schneller gehen würde.

    Das eine regelmäßige Anfrage her muss seh ich langsam ein. Wichtig ist aber das die von LabView gesendeten Statusdaten in der richtigen Reihenfolge fehlerfrei ankommen, also TCP.

    Wäre es sinnvoll via UDP die Anfragen zu gestalten und das senden der Stausdaten via TCP? Wie könnte solch ein Code richtig aussehen?

    Danke für eure Hilfe!



  • Ich versuche, das mal umzuformulieren:

    Deine Anwendung soll ich auf die LV-Anwendung verbinden, und die geht dann alle Sockets durch, die die Statusdaten abonniert haben, und sendet einmal alle 2 Sekunden das volle Paket (nicht TCP-Paket, sondern das Statusdatenpaket, was mal locker ein paar TCP-Pakete sprengen könnte, aber nicht muss)?

    Also, ein paar Fehler habe ich ja schon genannt. Du machst keine Protokoll- oder Längenprüfung. Du liest so lange ein, bis der Socket geschlossen wird. Hat die LV-Anwendung einen Timeout? Musst du alle paar Sekunden (20?) einen "mich gibt's noch" senden, damit die Anwendung weiß, dass es dich ... nun ja ... noch gibt? Wenn die dann den Socket schließt, kommst du raus aus der Schleife und hättest dann womöglich das letzte Paket vollständig im Speicher. Womöglich. Weil recv kein Versprechen darüber macht, dass es dir die Daten zurückliefert, wenn nichts mehr zu lesen ist. Entweder blockiert es dann bis zum Kernel-Timeout (mit select kannst du deinen eigenen Timeout definieren, nur ganz nebenbei), oder bis der Socket geschlossen wird. Du musst prüfen, wann du denkst, dass nicht mehr eingeladen werden muss. Ein recv zu viel, und dein Programm blockiert. Einer zu wenig, und du hast nicht genug Daten. recv kann dir einmal ein komplettes LV-Paket geben, und dann wieder nur ein Teil dieses Paketes.

    UDP wird dir, glaube ich, kaum helfen. UDP/TCP arbeiten auf einer anderen Protokollebene. Dein Problem ist nicht UDP/TCP, sondern dass du die Daten nicht korrekt prüfst.
    Als Beispiel: HTTP kann die Übertragung der Datenlänge über zwei Konzepte machen: entweder Content-Length im Header und dann die Anzahl der Bytes nach dem ersten "\r\n\r\n", oder über Chunk-weise übertragung, und wenn es einen Chunk mit Größe 0 gibt, wird abgebrochen. Das sind ordentliche Abbruchbedingungen, damit kann man arbeiten. Damit kann man sagen: OK, ich habe hier genug Bytes, mehr brauche ich nicht.
    Genau das fehlt dir.



  • Hallo Leute,

    Danke für eure Ratschläge! Die Situation hat sich nun dahingehend entwickelt das der Client eine Anfrage an die LabView Anwendung sendet und diese dann darauf reagiert. Also ganz normale Client Server Anwendung. Dabei habe ich nun ein Problem, dass wahrscheinlich mit einer Längenprüfung zusammenhängt.

    Labviewseitig sieht das ganze momentan und der Einfachhalber so aus:

    1. TCP: Listener warten am Port
    2. TCP: Lesen
    3. Empfangene Daten ausgeben
    4. TCP: Verbindung beenden

    In einem in C geschriebenem Client erfolgt die Verbindung erfolgreich. Daten kann ich ebenfalls senden. Allerdings werden die empfangenen Daten erst dann ausgegeben, wenn ich die C-Anwendung beende, sprich das Socket schließe. Hängt dies mit einer nicht vorhandenen Langenprüfung zusammen? Ich bitte um konkrete Codeschnippsel bzw. Verbesserungen.

    #include<stdio.h> //printf
    #include<string.h>    //strlen
    #include<sys/socket.h>    //socket
    #include<arpa/inet.h> //inet_addr
    
    int main(int argc , char *argv[])
    {
        int sock;
        struct sockaddr_in server;
        char message[1000] , server_reply[2000];
    
        //Create socket
        sock = socket(AF_INET , SOCK_STREAM , 0);
        if (sock == -1)
        {
            printf("Could not create socket");
        }
        puts("Socket created");
    
        server.sin_addr.s_addr = inet_addr("192.168.2.78");
        server.sin_family = AF_INET;
        server.sin_port = htons(55003);
    
        //Connect to remote server
        if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
        {
            perror("connect failed. Error");
            return 1;
        }
    
        puts("Connected\n");
    
        //keep communicating with server
        while(1)
        {
            printf("Enter message : ");
            scanf("%s" , message);
    
            //Send some data
            if( send(sock , message , strlen(message) , 0) < 0)
            {
                puts("Send failed");
                return 1;
            }
    
            //Receive a reply from the server
            //if( recv(sock , server_reply , 2000 , 0) < 0)
            //{
            //   puts("recv failed");
            //  break;
            //}
    
            //puts("Server reply :");
            //puts(server_reply);
        }
    
        close(sock);
        return 0;
    }
    


  • Die Nachricht muss nicht in einem Rutsch übertragen werden.
    Es kann also nötig sein, recv mehrmals aufzurufen.
    Dann musst du die Nachricht selber zusammen basteln.

    Sendest du einen Nullterminierte Nachrich?
    Nur dann (und wenn auch alles empfangen wurde) kannst du Stringfunktionen wie puts benutzen.

    Zum Testen von Netzwerkverbindungen bietet sich auch das Konsolentool netcat an. http://de.wikipedia.org/wiki/Netcat
    Bei Linux ist es meist dabei als nc



  • Inwiefern muss ich recv mehrmals aufrufen? Prinzipiell muss ich ja erstmal die Daten zum Server (LabvView Anwendung) bekommen. Un LabView gibt mir ja nur meine gesendete Nachricht aus, wenn ich den C-Code beende, sprich ide Verbindung beende.

    Zum Beispiel möchte ich einfach "Hallo" senden. Doch die Ausgabe bleibt bei LV aus. Ich habe den String an der letzten Stelle nullterminiert.

    ch steh grade auf dem Schlauch. Ein konkretes Codebeispiel würde mir vermutlich mehr helfen.



  • Worauf reagiert denn Labview?
    Evtl auf '\n' das du nicht mitsendest? (von C nach LV)

    Sendest du die '\0' auch mit? (von LV nach C)

    Probier mal netcat nach LV aus.

    nc 192.168.2.78 55003
    

    und dann kannst du deine Befehle eintippen. Oder du nimmst putty im RAW-modus.



  • Du machst mich grad wütend.
    Wenn du meine Posts nicht verstanden hast, dann sagt das direkt, und versuche nicht, etwas ausprobieren, was meine Posts bereits beschreiben, und frag nicht, warum die Implementierung, die die von mir beschriebenen Fallen beinhaltet, nicht funktioniert.

    while(1)
    {
        printf("Enter message : ");
        scanf("%s" , message);
    
        //Receive a reply from the server
        /*Falsch, falsch, falsch. Sagte ich nicht bereits, dass du dich nicht darauf
        **verlassen kannst, dass dir recv direkt die komplette Nachricht gibt? Wieso
        **ignorierst du das?*/
        //if( recv(sock , server_reply , 2000 , 0) < 0)
        //{
        //   puts("recv failed");
        //  break;
        //}         
    }
    

    Noch einmal:

    dachschaden schrieb:

    Du musst prüfen, wann du denkst, dass nicht mehr eingeladen werden muss. Ein recv zu viel, und dein Programm blockiert. Einer zu wenig, und du hast nicht genug Daten. recv kann dir einmal ein komplettes LV-Paket geben, und dann wieder nur ein Teil dieses Paketes.

    Da, da hast du es. Hier steht exakt dein Problem. Du prüfst nicht, ob deiner Meinung nach die Nachricht vollständig ist, du gehst davon aus, dass "das schon so passen wird". Nein. Tut es nicht. Nicht unbedingt zumindest. Deswegen musst du nach dem recv die Prüfung machen, ob das so in Ordnung ist.

    Hier mal ein Beispiel - kannst du so nicht anwenden, weil ich halt das Protokoll nicht kenne, aber es sollte dir eine Idee geben (schamlos aus einem meiner älteren Projekte geklaut):

    #define CLEAR_RESPONSE (*response_length)=0;free((*response))
    do
    {
        /*Timeouthandling*/
        mtv=(*tv);
        readfds=savefds;
        select(max_socket,&readfds,0,0,&mtv);
        if(!FD_ISSET(socket,&readfds))
        {
            /*OK, über Timeout, damit sind wir RAUS.*/
            CLEAR_RESPONSE;
            return ETIMEDOUT;
        }
    
        /*Prüfen, ob wir genug Speicher haben - damit langweile ich dich jetzt
        **nicht, da stehen genug Abstraktionen hinter.*/
    
        /*Dann endlich lesen. Wie ich bereits sagte, wenn select sagt, dass sich
        **was am Socket getan hat, aber keine Daten eingelesen werden konnten, wenn
        **ich sie erwarte, ist das ein Fehler. Ob in der Protokollprüfung oder bei
        **der Gegenseite, das ist mir egal.*/
        if((cur_pack_length=recv(socket,(*response)[*response_length],max_reserved_bytes-(*response_length),0))<1)
        {
            CLEAR_RESPONSE;
            return ECONNRESET;
        }
    
        /*Merken, dass sich unser Response vergrößert hat.*/
        (*response_length)+=cur_pack_length;
    
        /*Jetzt wird es interessant, die Protokollprüfung.
        **Wir haben im Grunde drei Stadien: 1. Alles in Ordnung, der Response ist
        **so gültig. 2. Sieht bisher ganz gut aus, aber da fehlt noch was - mach noch
        **weiter recv, bis ich "Stop" sage. 3. Habe hier Müll bekommen, kann ich
        **nicht verarbeiten, muss weg.
        **
        **Ich verwende hier einen Funktionspointer, damit die Funktion, die diese
        **Funktion aufruft, ihr eigenes Protokoll verwenden kann. Sie muss nur sagen, wann
        **genug ist und wann nur Blödsinn vorliegt. current_protocol_state ist eine Helfer-
        **Variable für Spezialfälle wie HTTP, weil man hier nicht ständig nach \r\n und
        **\r\n\r\n suchen muss, wenn man diese Information schon längst hat.*/
        enum PROTOCOL_STATUS current_result=check(request,(*response),(*response_length),current_protocol_state);
    
        /*Komplett untauglich.*/
        if(current_result==PROTOCOL_STATUS_FAILED)
        {
            CLEAR_RESPONSE;
            return EPROTO;
        }
    }
    /*Tja - und die Funktion macht dann halt so lang weiter, bis
    **PROTOCOL_STATUS_OK zurückkommt.*/
    while(current_result==PROTOCOL_STATUS_CONTINUE);
    

    Der check kann dann so aussehen wie bei SOCKS:

    enum PROTOCOL_STATUS socks4_check
    (
            UNUSED_PARAMETER(const char*request),
            UNUSED_PARAMETER(const char*response_so_far),
            const size_t response_length,
            UNUSED_PARAMETER(USES_PERSISTENT_STATE)
    )
    {
            /*Für SOCKS4 müssen wir glücklicherweise nur die Länge prüfen.*/
            if(response_length<8)
                    return PROTOCOL_STATUS_CONTINUE;
            else if(response_length==8)
                    return PROTOCOL_STATUS_OK;
            else
                    return PROTOCOL_STATUS_FAILED;
    }
    

Anmelden zum Antworten