Forum: Offtopic Nur eine GET-Anfrage auf Webserver zulassen


von josefk(_nologin?) (Gast)


Lesenswert?

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?!

von *.* (Gast)


Lesenswert?

Der Browser setzt keine Pakete zusammen, das macht die Netzwerkschicht. 
Er öffnet einen zweiten TCP-Kanal (siehe source port) um das Bildchen 
parallel abzuholen.

von Εrnst B. (ernst)


Lesenswert?

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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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.

von Josef K. (josefk)


Lesenswert?

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 ;)

von Josef K. (josefk)


Lesenswert?

@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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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.

von Εrnst B. (ernst)


Lesenswert?

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

von josefk (Gast)


Lesenswert?

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?!

von Εrnst B. (ernst)


Lesenswert?

@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.

von Josef K. (josefk)


Lesenswert?

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?

von Klaus (Gast)


Lesenswert?

Du bekommst immer noch Ports und TCP Verbindungen durcheinander. Das 
eine hat mit dem anderen nicht viel zu tun.

von Chris (Gast)


Lesenswert?

> 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.

von Josef K. (josefk)


Lesenswert?

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...

von Josef K. (josefk)


Lesenswert?

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
Noch kein Account? Hier anmelden.