Dateiinhalt über Socket schicken und vorher manipulieren?
-
Wir haben in der Uni gerade die Aufgabe bekommen, einen Webserver zu programmieren und dabei jetzt die Aufgabe erhalten, eine HTML Datei einzulesen und in den Body von ihr einige HTTP Informationen einzupflegen, wie die Protokollversion aber wie macht man das am besten? Vor allem, wie kann ich genau in den Body die Information einpflegen und die Datei erst dann danach über den Socket schicken?
So könnte man ja die Datei einlesen und über den Socket zurückschicken aber man weiß ja nicht, wie groß die Datei ist, d.h vor allem bei der Verwendung von read und write weiß ich nicht, was als 3. Argument an bytes schreiben soll ..
int send_resource(int sock, int resource) { char chars; int i; while((i = read(resource, &chars, bytes))) { if(i < 0) error("Error reading from the file."); if(write(socks, &chars, bytes) < 1 ) error("Error sending the file."); } return 0; }
Wie viele Bytes soll er dann auf einmal einlesen, was wäre da empfehlenswert oder kann man anstatt von read und write besser eine andere Funktion verwenden? Das Problem wäre ja jetzt auch noch, die HTTP Informationen in den Body der resource (html datei) einzupflegen.
Später soll es dann aber auch möglich sein, jeden Dateityp einzulesen und auch über das Socket zu schicken und dafür empfehlen sich die beiden Funktionen (write, read) dann doch oder nicht?Wir programmieren den Webserver unter Linux, nur um das mal erwähnt zu haben und wir sollen nur reines C benutzen und nur die Standard-Bibliotheken verwenden.
-
read
undwrite
werden i.A. dann eingesetzt, wenn man mehr (als ein) Byte lesen/schreiben möchte.Die Daten werden dann in einen vom Programmierer bereitgestellten Puffer gelesen.
Die Anfangsadresse vom Buffer wird als 2. Parameter übergeben,
die Größe vom Buffer wird als 3. Parameter übergeben.
-
Das will ich ja auch und das ist ja auch das Ziel. Wir sollen ja auch später alle möglichen Dateien über den Socket schicken und damit geht das dann ja auch wohl dementsprechend oder nicht? Ich habe auch schon im Internet gesucht und das wäre wohl die beste Lösung, die Datei in Chunks zu verschicken und mir read und write
Ich habe auch gerade sowieso festgestellt, dass das wohl so auch nie funktioniert hätte, wie ich das oben geschrieben habe. Das mit der while Schleife ist ja auch ein wenig komisch so und char müsste ja eigentlich ein char array sein oder ein char *. So wäre das dann doch aber richtig?
define MAX_FILE_SIZE 2048 int send_resource(int sock, int resource) { char* buffer = malloc(MAX_FILE_SIZE); if(buffer == NULL) { error("ERROR at malloc."); } int bytes_read = read(resource, buffer, sizeof(buffer)); if (bytes_read < 0) { // Fehler! } void *buf_p = buffer; // Wo im buffer sind wir while(bytes_read > 0) { int bytes_written = write(sock, buf_p, bytes_read); if(bytes_written <= 0) { // Fehler! } bytes_read -= bytes_written; buf_p += bytes_written; } free(buffer); }
Nur bei dieser MAX File Size bin ich mir nicht sicher aber ich weiß auch nicht, wie groß man die Datei dann maximal erlauben lassen sollte oder sind da sonst noch Fehler drin?
Ich würde mich sehr über Rückmeldung freuen und auch Kommentare oder wo vielleicht noch ein Fehler liegt.
-
Ein Array ist kein Pointer und ein Pointer ist kein Array!
Da buffer ein
char*
ist, bekommst du mitsizeof(buffer)
die Größe eines Pointers.
Der ist (je nach CPU-Architektur und Speichermodell) meist zwischen 2 und 8.MAX_FILE_SIZE ist auch die falsche Bezeichnung. Es ist die Puffergröße.
Da du sequentiell liest, hast du (an der Stelle) keine Beschränkung der Dateigröße.
Es muss ja noch nicht einmal eine Datei sein.
-
TCP/IP ist ein Stream-Protokoll d.h. DU musst in deinem eigenen Protokoll übermitteln wie viel Daten kommen - nur dann kannst du auf eine Menge x an Daten warten und entsprechend splitten (TCP/IP hilft dir da nicht dabei)
Beispiel:
du sendest z.B. zuerst ein int16 (2 Byte) mit der Länge der Daten und dann die Daten
der Empfänger wartet auf die 2 Bytes Datenlänge und dann auf die übermittelte Datenlänge an Bytes
Vorsicht: TCP/IP ist kein Bus d.h. dein Send-Paket kann in vielen kleinen Receive-Einzelpaketen ankommen - das ist völlig normal bei TCP/IP - kommt nur auf dein Netz und die Paketgröße an ob es passiert - also gar nicht erst denke du bekommst alles als ein Block
-
Ja, das stimmt. Da hätte ich dann wohl ein char[] anstatt eines char * nehmen sollen. Ich habe auch schon einen Buffer der die HTTP Anfrage aufnehmen soll aber den würde ich ungern dafür nehmen, was ja auch warscheinlich eher wenig Sinn macht. Wir sollten den Buffer auch für die Anfrage sinnvoll limitieren und ich habe ihn auf 2048 gesetzt, weil allein schon der URI Pfad ja auch sehr lang sein kann. Ich habe auch einmal nachgefragt und das reicht wohl, sagt der Prof, zumindest für unsere Zwecke.
FILE_MAX_SIZE ist echt eine schlechter Name und auch wohl unpassend aber was wäre denn besser? FILE_BUFFER oder FILE_BUF? Da sollen ja die Daten aus der Datei rein, keine Ahnung ob das besser ist.
Würde das denn dann so prinzipiell funktionieren, wie ich das oben geschrieben habe? Die Dateigröße braucht man ja eigentlich nicht begrenzen, höchstens den Pfad (btw. die URI), was ich ja schon woanders gemacht habe.
Was meinst du denn damit, das es sich nicht einmal um eine Datei handeln muss? Ich prüfe auch vorher mit der open Funktion, ob ich die Datei öffnen kann und ob errno vielleicht auch gesetzt wurde aber wenn nicht, dann gibt open ja den file descriptor zurück und den gebe ich dann an die Funktion (resource) weiter.
@Gast3
Das ich nicht unbedingt alles auf einmal rüber bekomme ist mir schon klar aber so wie oben geschrieben müsste es doch gehen oder nicht?
-
Ermittle doch zuerst die Länge der Datei, dann weisst du wieviel Bytes du total versenden musst. Versenden kannst du die Datei dann in Blöcken.
Unter Linux kannst du das z. B. mit stat(..), und unter Windows gibt es auch eine entsprechende Funktion.
-
Liq schrieb:
FILE_MAX_SIZE ist echt eine schlechter Name und auch wohl unpassend aber was wäre denn besser? FILE_BUFFER oder FILE_BUF?
Bei symbolischen Konstantennamen ist Vorsicht geboten, die werden oft schon in anderen Kontexten verwendet, beliebt sind sinnfreie Namensgebungen wie MAX,MIN,SIZE,BUFSIZE (liegt schon gefährlich nahe bei BUFSIZ s.u.) u.ä.,
auch der Standard definiert schon welche, z.B._IOFBF _IOLBF _IONBF BUFSIZ EOF FOPEN_MAX FILENAME_MAX L_tmpnam SEEK_CUR SEEK_END SEEK_SET TMP_MAX
und natürlich
FILE stderr stdin stdout
usw.
Während der Compiler bei FILE usw. sicher warnen wird falls der Name anderweitig verwendet wird, braucht der Präprozessor das nicht unbedingt zu tun.
Beginnende Unterstriche "_" sind sowieso tabu, ich empfehle einfach deutsche Namen zu verwenden (ohne Sonderzeichen), Kollisionen sind dann erstmal deutlich weniger wahrscheinlich.
-
Muss man denn echt die Länge oder die Größe der Datei vorausschicken? Warum muss man das? HTML Dateien werden auf jeden Fall schon einmal korrekt dargestellt aber Bilder nicht. Wir sollen unseren Server nach dem RFC 1945 entwickeln ud nicht nach dem aktuellem, welcher das auch immer ist.
Ich versende die Datei ja schon in Blöcken und ich weiß auch, dasss dabei nicht unbedingt alles auf einmal ankommt. Warum funktioniert das jetzt aber nicht mit den Bildern?
@Wutz
Danke für den Hinweis! Die meisten von denen kenne ich sogar auch schon aber ich gucke auch vorher immer noch einmal nach, ob es da nicht schon eine mit dem Namen gibt.
-
Liq schrieb:
Muss man denn echt die Länge oder die Größe der Datei vorausschicken? Warum muss man das?
Du musst irgendeinen Weg finden, dem lesenden Socket mitzuteilen, wann er aufhören kann, zu lesen.
Woher soll der sonst wissen, wann die Datei, die er empfangen soll, fertig ist? Die Anzahl der zu übermittelnden Bytes vorauszuschicken, ist nur ein Weg von vielen.
-
Liq schrieb:
wir sollen nur reines C benutzen und nur die Standard-Bibliotheken verwenden.
Das ist zwar sehr löblich, aber mit Standard-Bibliotheken allein kannst du keinen Webserver programmieren.
Da nimmst du POSIX (für socket usw.), das ist auch ein C-Standard aber nicht der C-Sprachstandard.Für das direkte Senden deiner Datei nimmst du dann einfach
#include <sys/sendfile.h> ssize_t sendfile(int out_fd, int in_fd, off_t * offset , size_t count);
, das nimmt dir das write-Gefrickel ab, musst die Datei aber zuvor manuell fertigstellen, dann mit open() öffnen und an sendfile() übergeben.
sendfile ist kein POSIX, aber in glibc enthalten.