Hallo, ich bin gerade dabei einen kleinen Webserver zu schreiben. Alles funktioniert eigentlich schon ganz gut nur ein kleines Problem gibt es nocht. Wenn jemand eine Datei anfordert, die nicht im Browser dargestellt werden soll oder kann, wird diese mit dem MIME-Type application/octet-stream verschickt. Firefox3 schickt mir dann aber noch eine Get-Anfrage auf favicon.ico. Wahrscheinlich um im Downloadfenster ein lustiges Bildchen anzeigen zu können. (IE7 macht das nicht!) Diese Anfrage verwirrt aber den Browser. Ich schicke die Daten nämlich einfach als TCP Packete (in Wireshark werden diese mit "[TCP segment of a reassembled PDU]" dargestellt) und solange keine GET Anfrage dazwischen funkt, setzt der Browser diese wieder zusammen. Kommt aber eine solche Icon Anfrage dazwischen weißt der Browser anscheinend mindestens ein Packet als Antwort auf diese favicon.ico Anfrage zu. Blöd. Damit ist der Download fehlerhaft. Kann ich dem Browser irgendwie sagen, dass er das unterlassen soll, oder sequentiell machen soll? Mein Response sieht so aus: HTTP/1.0 200 Document follows Server: avr Content-Type: application/octet-stream Content-length: 1234567890 datadatadatadata..... Dachte HTTP/1.0 sagt scon aus, dass ich nicht parallel machen will?!
Der Browser setzt keine Pakete zusammen, das macht die Netzwerkschicht. Er öffnet einen zweiten TCP-Kanal (siehe source port) um das Bildchen parallel abzuholen.
Wirklich parallele Requests über nur eine TCP-Verbindung (HTTP/1.1 Pipelining) macht eh so gut wie kein Browser. Bei dir kommt der Favicon-Request über eine ZWEITE TCP-Verbindung, und nicht der Browser, sondern dein Webserver kommt durcheinander! Also: Entweder Webserver so konfigurieren, dass nur eine TCP-Verbindung angenommen wird. (Zweite einfach verwerfen). Oder Webserver reparieren, dass der pro TCP-Verbindung auch einen getrennten Zustand verwaltet, und nicht mehr durcheinander kommt.
Dein Webserver wird hundertprozentig nicht pro TCP-Socket funktionieren. Sprich: Mehrere HTTP Verbindungen zur gleichen Zeit. Das wäre zu einfach gewesen! ;) Es gibt viele viele Hindernisse, die man erkennen und umschiffen muss mit selbstgeschriebenen Webservern, wenn diese auf minimalistischen Stacks wie dem uip laufen (und das unterstelle ich dir jetzt ein mal). PS: Kleiner Hinweis: Dein HTTP Server MUSS auch noch funktionieren, wenn der Client als GET-Request drei(!) einzelne(!) Pakete jeweils mit den Buchstaben G E und T an den Server schickt. Normalerweise wird das über die TCP Schicht als Stream an die Nächste Schicht übertragen, sodass diese eigentlich nichts mehr damit am Hut hat. Aber bei minimalistischen TCP Stacks ist das anders.
Hmm. Solange ich noch nicht alle Daten bzw nicht alle Packete verschickt habe, werden von meinem Programm keine Get Anfragen bearbeitet. Der Stack (uIP) hingegen nimmt die Anfrage aber entgegen. Führt das evtl. schon zu einem Problem? Dass mein Webserver die Daten durcheinanderbringt kann ich mir nicht vorstellen. (Kann man sich aber bei eigenen Programmen eh fast nie ;)
@simon Ahh ok. Da war ich kurz zu spät. Wie kann man so ein Problem dann "umschiffen"? Die windowsize auf 0 setzen ist wohl keine Lösung, da kann ich ja auch nichts mehr senden :) Was anderes fällt mir aber nicht ein.
Zu dem GET Problem? Da hilft entweder für jeden Socket einen Puffer bereitstellen und die ganzen Zeichen sammeln, bis \r\n kommt (oder für die Light-Variante bis der Pfad beginnt (beginnend mit "/"). Alternativ (so mache ich es immer) eine kleine Statemachine, sprich Statusvariable im Endeffekt, die für jeden Socket existiert. Die von HTTP_G über HTTP_GE nach HTTP_GET springt und jedes Zeichen einzeln aus dem Paket holt, soweit noch Buchstaben im Paket vorhanden sind. Als Test ob es funktioniert kannst du einfach telnet 192.168.x.x 80 (bzw dein HTTP Port) benutzen und per Hand GET /<Enter> eintippen.
Richtige Lösung: deinen Webserver in Ordnung bringen, so dass jede Verbindung getrennt und unabhängig von den anderen gehandhabt wird. So kann dein zweiter GET-Request den ersten nicht unterbrechen. Also, in deiner uip-callback-funktion dürfen keine Zugriffe auf globale oder static variablen mehr vorkommen (Ausser du sorgst selber dafür, dass das funktioniert...) Alle status-informationen im uip_conn->appstate speichern, dass ist dann bei jedem callback-Aufruf das richtige... z.B.
1 | // webserver.h
|
2 | void httpd_appcall(void); |
3 | #define UIP_APPCALL httpd_appcall
|
4 | struct httpd_state { |
5 | uint8_t state; |
6 | char *dataptr; |
7 | ...
|
8 | };
|
9 | typedef struct httpd_state uip_tcp_appstate_t |
10 | |
11 | // webserver.c
|
12 | void httpd_appcall(void) { |
13 | struct httpd_state * s = (struct httpd_state *)(&(uip_conn->appstate)); |
14 | ...
|
15 | if (uip_newdata()) { |
16 | switch (s->state) ... |
17 | |
18 | |
19 | }
|
20 | }
|
21 | ...
|
Oder den Quick&Dirty Hack:
1 | // uip-conf.h
|
2 | #define UIP_CONF_MAX_CONNECTIONS 1
|
Ich bin mir nicht sicher, ob wir nicht aneinander vorbei reden. Ich habe 2 offene Ports. Jeden Port bearbeite ich seperat zu dem anderen und jeder Port hat seinen eigenen Puffer. Ich bearbeite zudem auch Acknowledgments und Retransmissions bezogen auf die Ports. Ebenso beantworte ich keine Get-Anfragen solange die Alte Anforderung des entsprtechenden Ports nicht komplett abgearbeitet wurde. @E. Bachmann Zitat: Bei dir kommt der Favicon-Request über eine ZWEITE TCP-Verbindung, und nicht der Browser, sondern dein Webserver kommt durcheinander! Also: Entweder Webserver so konfigurieren, dass nur eine TCP-Verbindung angenommen wird. (Zweite einfach verwerfen). Oder Webserver reparieren, dass der pro TCP-Verbindung auch einen getrennten Zustand verwaltet, und nicht mehr durcheinander kommt. Wenn ich dich richtig verstehe dann soll ich folgendens:
1 | /**
|
2 | * The maximum number of simultaneously open TCP connections.
|
3 | *
|
4 | * Since the TCP connections are statically allocated, turning this
|
5 | * configuration knob down results in less RAM used. Each TCP
|
6 | * connection requires approximatly 30 bytes of memory.
|
7 | *
|
8 | * \hideinitializer
|
9 | */
|
10 | |
11 | #define UIP_CONNS 3
|
12 | |
13 | /**
|
14 | * The maximum number of simultaneously listening TCP ports.
|
15 | *
|
16 | * Each listening TCP port requires 2 bytes of memory.
|
17 | *
|
18 | * \hideinitializer
|
19 | */
|
20 | |
21 | #define UIP_LISTENPORTS 5
|
auf
1 | #define UIP_CONNS 1
|
2 | #define UIP_LISTENPORTS 2 //ich habe Port 80 und 50000 offen
|
festelgen?!
@josefk:
> Ich habe 2 offene Ports. Jeden Port bearbeite ich seperat zu dem anderen und
jeder Port hat seinen eigenen Puffer. Ich bearbeite zudem auch Acknowledgments und
Retransmissions bezogen auf die Ports. Ebenso beantworte ich keine Get-Anfragen
solange die Alte Anforderung des entsprtechenden Ports nicht komplett abgearbeitet
wurde.
Du musst das nicht nur nach Ports getrennt abarbeiten, sondern auch nach
Verbindungen!
Über einen Port können mehrere, voneinander unabhängige,
TCP-Verbindungen laufen, die darfst du nicht durcheinander bringen!
Also:
* Browser öffnet TCP-Verbingung, Source-Port 23456, Destination-Port 80.
Schickt "GET /File/zum/Download"
* Webserver schaufelt Daten von seinem Port 80 zum Port 23456 auf dem
Client-Recher
* Während dieser Transfer läuft, öffnet der Browser eine zweite
TCP-Verbindung, Source-Port: 32154, Destination-Port 80. Schickt hierauf
"GET /favicon.ico"
* Der Webbrowser muss nun auf der Zweiten Verbindung antworten, die
Antwort also an den 32154er Port zurückschicken!
* Die Bestehende Verbindung <Browser>:23456 <-> Server:80 darf davon
NICHT beeinflusst werden.
Und ja,
1 | #define UIP_CONNS 1
|
2 | #define UIP_LISTENPORTS 2
|
Würde das Problem verstecken. Richtig ist dein Code dann zwar immer noch nicht, aber der Fehler fällt dann nicht mehr auf.
Achso. Klingt logisch. Da muss ich doch gleich mal am Montag nachsehen ob Firefox wirklich einen zweiten Port nutzt. Wäre die Anfrage ebenfalls von Port 80 aus würde das mein Problem ja nicht beschreiben, oder?
Du bekommst immer noch Ports und TCP Verbindungen durcheinander. Das eine hat mit dem anderen nicht viel zu tun.
> Wäre die Anfrage ebenfalls von Port 80 aus würde das > mein Problem ja nicht beschreiben, oder? Die Anfrage ist immer von irgendeinem zufällig gewählten Port auf Port 80 an deinem Server. Firefox macht nichts falsch. Lies Ernst Bachmanns Posting nochmal, er hat das erklärt. Beachte den Unterschied zwischen source port und destination port.
Ahh. Ok. Jetzt hab ichs hoffentlich verstanden. Naja. Der Quick and Dirty Hack reicht mir. Ich will ja keinen perfekten Webserver herstellen. Falls jemand Daten will, muss er eben warten bis die bestehende verbindung getrennt wird. Das kann ja nicht so lange dauern, außer ich schaufe mal ein paar Megabyte übers Netz. Aber eine Frage noch. Bezieht sich evtl. UIP_CONNS generell auf die TCP Verindung unabhängig vom Port. Also wenn ich 2 Ports offen habe und UIP_CONNS 1 ist, kann ich dann nur jeweils eine TCP Verbindung zu jedem offenen Port oder generell nur eine TCP-Verbindung zu einem der beiden Ports zulassen? Wahrscheinlich beschreibt diese Definition die gesamte Anzahl an Verbindungen...
OK. Quick and dirty ist scheiße. Ich machs richtig. :) verdammt
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.