C files einlesen



  • Also ich schicke eine Anfrage in der Form "GET <filename>". Diese Anfrage soll korrekt sein also nicht sowas "xy GET <filename>" oder "GET <filename> xy". Ich versuche die Anfrage mit strtok zu zerlegen um den Filenamen zu erhalten nur wenn ich das File oeffnen will kann es einfach nicht gefunden werden?? Benutze eclipse zur Programmierung also hab ich das File im Obergeordneten Ordner des Quellcodes abgelegt. Habs aber auch mit "gcc -std=c99 ..." versucht (das file dann im selben Ordner abgelegt) doch es kann nicht gefunden werden... Hier mal der Code:

    #define MAX_BUFFER 1024
    
    void send_Response(char *file_name) {
    
    	FILE *fp;
    
    	fp = fopen(file_name, "r");
    	if (fp != NULL) {
    		char line[60];
    		fscanf(fp, "%s", line);
    		printf("%s", line);
    	} else {
    		printf("NO IT DIDNT\n");
    	}
    }
    
    void recive_Request(int connectFd) {
    
    	char rcvmes[MAX_BUFFER];
    	char *token;
    	int rcvmes_len;
    	int count_tok = 0;
    	char *file_name = malloc(sizeof(char) * 30);
    
    	if((rcvmes_len = recv(connectFd, rcvmes, MAX_BUFFER, 0)) < 0) error_exit("Failed to recive a request"); //error_exit ist nur ne Fehlerhandling Methode...
    	rcvmes[rcvmes_len] = '\0';
    
    	token = strtok(rcvmes, " ");
    	while (token != NULL) {
    
    		if (strcmp(token, "GET") == 0 && count_tok == 0) {
    			count_tok++;
    		} else if (count_tok == 1) {
    			file_name = token;
    			count_tok++;
    		} else {
    			close(connectFd);
    			error_exit("shit");
    		}
    		token = strtok(NULL, " ");
    	}
    
    	send_Response(file_name);
    }
    

    Hat vlt wer ne Idee?



  • Kannst du das File denn einfach so öffnen? Was sagt der Debugger zum Inhalt von file_name? Sicher dass da kein '\n' oder sowas drin steckt? Solche Infos bräuchte man schon..



  • Hast du dir mal die übertragenen Daten angesehen (mit printf)?
    Was wird da denn hin und her geschickt?
    Ist da noch ein \r\n in rcvmes?

    printf("rcvmes :<%s>\n", rcvmes);
    

    Durch die <> kannst du ein \n erkennen.



  • also

    printf("rcvmes :<%s>\n", rcvmes);
    

    liefert:

    rcvmes :<GET ff.txt
    
    >
    

    Also bei der Eingabe wird die Bestaetigung (also das "\n") noch drangehaengt oder?
    wie kann man dass den wegschneiden?



  • Typ222 schrieb:

    rcvmes :<GET ff.txt
    
    >
    

    Also ist sogar zweimal ein \n und/oder \r drin. Die solltest du noch rausschmeissen. (mit strrchr() geht das ganz gut)



  • Immer wieder dasselbe: strtok ist nichts für Anfänger!
    Was tust du, falls der Dateiname Leerzeichen enthält? (Anfänger und Theoretiker würden jetzt antworten: kommen bei mir nie vor)
    Wo gibst du

    char *file_name = malloc(sizeof(char) * 30);
    

    wieder frei?
    Für fopen()==NULL gibt es die aussagekräftige Variante

    perror(file_name)
    

    .
    recv ist kein C Standard, du musst sicherstellen, dass hier nicht ein '\0' in den Nutzdaten vorkommen kann, außerdem solltest du recv in einer while-Schleife abfragen und den String zusammensetzen.
    Für deinen Anwendungsfall ist strtok ungeeignet, viel besser wäre strncmp bzw. sscanf, also z.B.

    if( !strncmp(rcvmes,"GET ",4) ) puts(rcvmes+4);
    

    oder

    if( 1==sscanf(rcvmes,"GET %[^\n]",filename) ) puts(file_name);
    


  • sscanf ohne Längenbegrenzung auf aus dem Netzwerk gelesene Daten? Du bist ja völlig übergeschnappt. Von Buffer-Overflows noch nichts gehört, was?

    Nein, strtok bzw. strtok_r o.ä. (strtok ist nicht reentrant) ist hier schon sinnvoller. Denkbar wäre beispielsweise

    char *filename;
    
    if(strncmp("GET ", request, 4) == 0 &&
       (filename = strtok(request + 4, "\r\n"))) /* kein eigener Buffer notwendig! */
    {
      FILE *fd = fopen(filename, "r");
    
      /* ... */
    
      fclose(fd);
    }
    

    Ganz abgesehen von all diesen Vorschlägen muss ich aber sagen: generell ist es beim Verarbeiten von Daten aus dem Netzwerk sinnvoll, so paranoid wie möglich an den Datensatz heranzugehen. Ich sähe an deiner Stelle davon ab, mir hier eine solche Frickellösung zusammenzuhacken; stattdessen muss eine vernünftige Sprachspezifikation und ein sie umsetzender Parser her. Kennst du dich mit formalen Sprachen aus?



  • Danke fuer die Antworten...
    @seldon:
    Bei strtok(request + 4, "\r\n") wird aber nicht beruecksichtig, dass <GET <filename> xy> auch nicht vorkommen darf oder?
    Wir muessen eben alle Faelle abdecken deswegen das herumgeschnipsel 😉



  • So, wie es da steht, fasst es alles, was nach GET bis zum Ende der Zeile kommt, als Teil des Dateinamens auf. Leerzeichen können in Dateinamen ja auch vorkommen.

    Wenn das nicht gewollt ist, splitte statt nach "\r\n" halt nach " \r\n". (Leerzeichen in die Menge aufgenommen)

    Allerdings: wie gesagt, eigentlich sind derartige Dinge Fälle für sauber definierte Parser - abhängig von der Komplexität des Protokolls kann man vielleicht mit Regexes auskommen, aber mit einfachen String-Funktionen da ranzugehen, ist ziemlich gefährlich. Ich gehe so weit, es in Netzwerkanwendungen als unverantwortlich zu bezeichnen.



  • seldon schrieb:

    sscanf ohne Längenbegrenzung auf aus dem Netzwerk gelesene Daten? Du bist ja völlig übergeschnappt. Von Buffer-Overflows noch nichts gehört, was?

    Du könntest dich auch mal wieder auf die Netiquette besinnen und deine Verbesserungen in sachliche Worte fassen. Hat dich hier niemand angepisst, oder?

    sscanf() auf die die überprüfung von <Get > anzusetzen muß kein Sakrileg sein, wie du es ausdrückst. Die Längenbegrenzung folgt aus MAX_BUFFERS für rcvmes.

    Schwache Leistung



  • forenseeker schrieb:

    Schwache Leistung

    Nee, seldons Beiträge lese ich immer gerne, seine Kenntnis ist unbestritten.
    Wutz hat sich aber seinerseits des Öftereren gegenüber "Newbies" im Ton vergriffen, wenn es sich um einen anderen User gehandelt hätte, wäre der Ton in seldons Post mit Sicherheit milder.



  • Ich hätte das freundlicher formulieren können, mea culpa. Den Vorwurf, dass mein Ton mit der Person Wutzs zusammenhängt, muss ich mir auch gefallen lassen; er ist mir in der Tat mehrfach negativ aufgefallen. Das ist aber natürlich keine Entschuldigung.

    In der Sache ist sscanf trotzdem eine gefährliche Wahl, und im konkreten Fall hilft dir auch MAX_BUFFER nicht weiter, weil MAX_BUFFER deutlich größer ist als der für filename allozierte Speicher. Man könnte sich hier mit

    if(1 == sscanf(rcvmes, "GET %29[^\n]", filename)) { /* filename hat 30 Byte Platz */
      puts(file_name);
    }
    

    behelfen, aber ich sehe wirklich keinen Grund dazu, die Länge des Dateinamens auf diese Weise künstlich zu beschränken. Außerdem kann es bei Netzwerkkommunikation leicht passieren, dass nicht \n, sondern \r\n oder \r als Zeilenumbruch übermittelt wird - \r\n taucht in einer ganzen Reihe RFCs auf, beispielsweise RFC 822 (E-Mail) und RFC 2616 (HTTP) - so dass die Beschränkung auf \n als Zeilenumbruchszeichen problematisch sein kann. Auch dass lässt sich leicht beheben (%29[^\r\n]), aber die völlig unnötige Längenbeschränkung und der zusätzliche Rechenaufwand bleiben.


Anmelden zum Antworten