Forum: Mikrocontroller und Digitale Elektronik HTTP Datenübertragung beim lwIP


von ützgür (Gast)


Lesenswert?

hi

Ich habe eine Frage bezüglich der HTTP / TCP Übertragung an sich...


normalerweise geht soweit ich das verstanden hab HTTP mit TCP so:

-> SYN
<- SYN/ACK
-> ACK
-> GET bild.gif
<- HTTP header + erste Daten
-> ACK
<- FIN + restliche Daten
-> ACK
-> FIN
<- ACK

nach jedem datenpaket kommt ein ACK


der lwIP ( 1.4.0) macht hier aber soetwas:

-> SYN
<- SYN/ACK
-> ACK
-> GET bild.gif
<- HTTP header
<- rest HTTP header + erste daten
-> ACK (hier werden die 2 pakete zusammen in der seq/ack nummer gezählt)
<- FIN + restliche daten
-> ACK
-> FIN
<- ACK


ich dachte erst "sliding window"
aber hier werden die pakete "nur" später geACKed
hier werden aber mehrere Pakete zusammen geACKed
mir gehts darum wie man dieses "feature" nennt

desweiteren kann es sein das der client zwischen HTTP header und dem 
rest header auch mal ein ACK sendet .. auch wenn es in das window passt.
hier kracht es dann irgendwie ...
bzw hier kommt dann eine retransmission

von Nosnibor (Gast)


Lesenswert?

Nach jedem Datenpaket ein ACK zu senden ist recht ineffizient, das läuft 
auf ein Ping-Pong-Protokoll heraus. Normal ist das für TCP nicht; TCP 
wurde erfunden um besser zu sein. Gut, wenn ich auf einem 8bitter 
unbedingt TCP implementieren müsste, um mit einem Browser zu reden, 
würde ich das auch auf ein Ping-Pong-Protokoll herunterhandeln.

Ein eigenes ACK-Paket wird normalerweise nur gesendet, wenn die 
Datenübertragung offenbar nur in eine Richtung erfolgt, sonst steckt die 
ACK-Information einfach mit im TCP-Header der Gegenrichtung; der Platz 
dafür ist sowieso reserviert.

Außerdem bezieht sich ACK genaugenommen nicht auf Pakete, sondern auf 
die transportierten Bytes; mehrere Pakete mit einem ACK zu bestätigen 
ist also normal. Theoretisch wäre es sogar zulässig, nur ein halbes 
Paket zu ACKen; der Absender kann dann das ganze Paket wiederholen oder 
nur die hintere Hälfte, oder die hintere Hälfte + folgende Daten.

Das ACK für das GET steckt z.B. in demselben Paket wie der HTTP-Header, 
weil der Server sowieso Daten zum Client schicken muß, da kann er das 
ACK gleich mitschicken. Wenn der Server sich mit dem nächsten Paket dann 
zu viel Zeit läßt, schickt der Client schonmal ein ACK, damit der Server 
weiß, daß sein erstes Paket (das mit dem HTTP-Header) angekommen ist und 
er das nicht wiederholen muß. Wenn der Server seine Pakete schneller 
hintereinander schickt, gibt's eben nur ein gemeinsames ACK für beide. 
Einen besonderen Namen gibt es für diese Situation aber nicht.

Die "Window size" spielt dabei auch noch eine Rolle. Die steht auch in 
jedem TCP-Paket drin, und gibt an, wieviel die Gegenseite noch über das 
letzte ACK hinaus senden darf, also praktisch wieviel Platz der 
Empfänger noch in seinem Puffer hat. Je nachdem, wie schnell der 
Empfänger die Daten verarbeitet hat, kann das sehr klein werden, und der 
Absender (in diesem Fall wohl der Server) muß dann für jedes Paket 
einzeln auf ein ACK warten.

von ützgür (Gast)


Lesenswert?

ich habe den fehler eingrenzen können ...
aktuell habe ich einen fehler im MSS und windowsize



ich muss das mit dem sliding window nochmal genau durchlesen

ein einzelnes ACK nach einer contiguous übertragung von mehreren 
segemnten ist erlaubt


einzig das SACK sollte ich glabe ich für den kleinen stack weglassen
da ich sonst nur schwer die möglichkeit habe einzelne segmente 
zwischenzupuffern und die ACKs durchzuzählen

was ch aktuell nicht beachte ist der window wert des ACK vom client
nach 6 dateien mit 4 contiguous segmenten geht dem der puffer aus

das ACK sendet er weil nur noch 500 byte window frei waren
ich hab aber fröhlich 1460 bytes reingepackt
deswegen fiel mir das bei einzelnen dateien nicht auf


werde das morgen so umbauen das ich die windowsize einbeziehe und an die 
applikation weiterreiche mit der maximal möglichen anzahl an byts die 
ich senden kann

dann sollte das gehen ...

von Nosnibor (Gast)


Lesenswert?

Normalerweise kann der Applikation des Senders die Windowsize des 
Empfängers egal sein, weil der TCP-Sendepuffer dazwischen vermittelt. 
Die Applikation gibt höchstens so viele Daten an TCP wie eben noch in 
den Sendepuffer passen, TCP sendet die Daten aus dem Puffer, soweit es 
Flußkontrolle (Windowsize des Empfängers) und congestion control 
erlauben, und wenn ein ACK kommt, wird entsprechend wieder Platz im 
Sendepuffer frei.

Auf kleinen Prozessoren, wo kein Platz für den Sendepuffer ist, sieht 
das natürlich anders aus; da muß TCP für jedes Paket, das es senden 
will, die Applikation nach den entsprechenden Daten fragen.

von Jim M. (turboj)


Lesenswert?

Nosnibor schrieb:
> . Normal ist das für TCP nicht; TCP
> wurde erfunden um besser zu sein. Gut, wenn ich auf einem 8bitter
> unbedingt TCP implementieren müsste, um mit einem Browser zu reden,
> würde ich das auch auf ein Ping-Pong-Protokoll herunterhandeln.

Und dabei auf die Schnau... fallen. Moderne Betriebssysteme machen das 
nicht mehr mit.

von ützgür (Gast)


Lesenswert?

Nosnibor schrieb:
> Auf kleinen Prozessoren, wo kein Platz für den Sendepuffer ist, sieht
> das natürlich anders aus; da muß TCP für jedes Paket, das es senden
> will, die Applikation nach den entsprechenden Daten fragen.

ja
genau das versuche ich zu umgehen

bisher habe ich ja bei  eingehenden ACKs
die ack/seq nummer geprüft und kann so feststellen ob die gegenseite 
alles richtig empfangen hat
das macht das system zwar einfach aber auch langsam ..

ich habe deswegen  eine segmentliste für jede übertragung wo ich mir die 
reihenfolge , acknummer , seqnummer und flags sowie segmentsize merke
so kann ich theoretisch jedes segment durch die applikation 
wiederherstellen
wenn es korrekt geacked wurde lösche ich es aus der liste

wenn ich 2 segmente sende (contiguous ) dann erstelle ich nur 1 segment 
mit eben passender größe



ich habe heute etwas experimentiert ..
dabei habe ich zB festgestellt das manche browser bei windowsize 
1000bytes schreiben ..
 aber in den options steht ein multiplikator .. den ich bisher vergessen 
hatte


ich habe diverse wireshark caps wo 5-8 segemnte gesendet werden und dann 
erst ein ACK erfolgt
warum er aber so viel zeit hat 5-8 pakete zu senden hab ich nch nicht 
kappiert

bei mir sendet er nach 2 paketen ein ACK

ich sende eine MSS von 1460 und Window von 2*1460

liegt evtl auch daran

von Nosnibor (Gast)


Lesenswert?

Jim Meba schrieb:
> Nosnibor schrieb:
>> . Normal ist das für TCP nicht; TCP
>> wurde erfunden um besser zu sein. Gut, wenn ich auf einem 8bitter
>> unbedingt TCP implementieren müsste, um mit einem Browser zu reden,
>> würde ich das auch auf ein Ping-Pong-Protokoll herunterhandeln.
>
> Und dabei auf die Schnau... fallen. Moderne Betriebssysteme machen das
> nicht mehr mit.
Wenn es nicht auf Durchsatz ankommt, sollte das immer gehen. Als 
Empfänger kann ich jederzeit über die windowsize regeln, wann der Sender 
senden darf, und als Sender kann ich einfach aufs ACK warten, bevor ich 
das nächste Paket sende. Das wird dann allerdings langsam, weil der 
Empfänger eine Weile (200ms war mal standard) zögert, bevor er ein 
"leeres" ACK schickt.

ützgür schrieb:
> dabei habe ich zB festgestellt das manche browser bei windowsize
> 1000bytes schreiben ..
>  aber in den options steht ein multiplikator .. den ich bisher vergessen
> hatte
Das sollte einfach abzustellen gehen: beim three-way-handshake die 
entsprechende Option nicht senden, dann weiß die Gegenseite, daß du kein 
window scaling kannst und sendet die echte windowsize ohne 
Multiplikator. Der Nachteil (max. 64kb Window) dürfte auf einem kleinen 
embedded System kaum ins Gewicht fallen, weil nicht so viele Daten zu 
senden sind.

>
> ich habe diverse wireshark caps wo 5-8 segemnte gesendet werden und dann
> erst ein ACK erfolgt
> warum er aber so viel zeit hat 5-8 pakete zu senden hab ich nch nicht
> kappiert
>
> bei mir sendet er nach 2 paketen ein ACK
Der Empfänger wird natürlich in jedem Paket, das er selber sendet, die 
aktuelle Ack-Nummer mitschicken. Wenn er selber keine Daten zu senden 
hat, muß er dazu leere Pakete schicken. Eine feste Vorschrift, wann er 
das machen soll, gibt es nicht, soweit ich weiß. Es war mal üblich, das 
nach 200ms zu tun, aber es gibt bestimmt Gründe (z.B. bessere 
Unterstützung des slow-start-Algorithmus) für komplizierteres Verhalten.

Da würde ich mir einfach keinen Kopf drum machen, wann der Empfänger 
seine ACKs schickt, solange er es überhaupt tut. Und wenn, dann muß man 
sich auch die Zeitstempel der Pakete genau ansehen. Und nicht vergessen, 
wo der Wireshark sitzt: manchmal begegnen sich Pakete auf der "Leitung" 
(incl. Netzwerkadapter und Ethernet-Treiber), und im Wireshark sieht 
immer der eigene Netzwerkstack besser und vernünftiger aus als die 
Gegenseite.

von Stefan F. (Gast)


Lesenswert?

Bei Windows Vista, 7 und 8 ist der Timeout 200ms.
Bei Windows XP konnte man ihn in der Registry konfigurieren.
Bei Linux ist der Timeout viel kürzer.

Windows Programme können die Option TCP_NODELAY Setzen, um den Timeout 
ganz abzuschalten. Dann wird jedes empfangene Paket sofort quittiert. 
Telnet mahct das z.B. sinnigerweise.

Leider gibt es keine Möglichkeit, dass der Server dem Client mitteilt, 
er soll bitte jedes Paket sofort quittieren.

von Stefan F. (Gast)


Lesenswert?

> Es war mal üblich, das nach 200ms zu tun

Ursprünglich war es vorgesehen, dass der Empfänger jedes Paket einzeln 
möglichst rasch quittiert. Dann kam der Nagle Algorithmus (RFC 896 und 
RFC 1122) hinzu. Das danach neue Verhalten ist nicht mehr 100% 
kompatibel zum alten. Und deswegen gucken die Micro-Webserver (die nur 
kleine Window-Sizes unterstützen) jetzt in die Röhre.

Ich fände es gut, wenn die Web-Browser an dieser Stelle lernen würden. 
Spätestens nach ein paar Paketen müsste der Browser merken, dass der 
Server offensichtlich IMMER ein ACK für jedes Paket (oder für jedes n-te 
Paket) erwartet. Darauf kann er sich einstellen, indem er beim nächsten 
Verbindungsaufbau die Option TCP_NODELAY setzt.

Bei Curl kann man das übrigens netterweise per Kommandozeilenparameter 
steuern.

Vor einigen Jahren gab es mal einen Workaround der bei Windows Clients 
half, leider klappt er nicht mehr. Damals war Windows noch so gestrickt, 
dass es nur volle Pakete (size=MSS) verzögert beantwortete. Nach dem 
Motto " da kommt sicher noch mehr". Aber halbvolle Pakete hatte Windows 
immer sofort beantwortet. Es gab dazu mal eine Patch für µIP, aber wie 
gesagt wurde der zurück gezogen.

von ützgür (Gast)


Lesenswert?

hi

vielen dank für die antworten ...

ich bekomme einfach nicht mehr als 2 pakete raus ...
ohne das ein ACK folgt

demnach  kann man das zwar so laufe lassen ..
das ist aber enormer aufwand für die verwaltung
ebenso schwerfällig wenn doch mal ein zwischenACK kommt
den fall bekomm ich nur schwer weggefiltert

das mit  der datenlänge ist auch so ein ding was ich schonmal gelesen 
habe.
aber das scheint wirklich nur experimentell gewesen zu sein.

der µIP hatte mal einen patch das er ein 2tes ACK sendet zwar leer ..
aber eben kurz nach dem datepaket  um dem client zum anfragen zu zwingen
aber neuere browser verhalten sich da auch anders ...


ich versuche mal noch diverse  dinge ...

habe bisher auch immer versuch caps von verschiedenen verbindungen 
anzuschauen und zu verstehen

aufgrund der zigtausend RFCs blickt da glaube keiner mehr 100%ig durch

von Nosnibor (Gast)


Lesenswert?

ützgür schrieb:
> ich bekomme einfach nicht mehr als 2 pakete raus ...
> ohne das ein ACK folgt
>
> demnach  kann man das zwar so laufe lassen ..
> das ist aber enormer aufwand für die verwaltung
> ebenso schwerfällig wenn doch mal ein zwischenACK kommt
> den fall bekomm ich nur schwer weggefiltert

Das verwirrt mich jetzt langsam. Wo ist das Problem für den Sender, wenn 
mehr ACKs zurückkommen als erwartet?

Wenn du partout nicht damit klarkommst, im unerwarteten Moment auf ein 
ACK reagieren zu müssen, dann schiebe das doch einfach auf: Wenn ein ACK 
reinkommt, einfach die nötige Information irgendwo speichern, und später 
dann entsprechend reagieren. Viel Information ist das ja nicht:
 - ein Flag, daß mindestens ein ACK empfangen wurde
 - die "größte" geACKte Sequenznummer
 - das aktuelle Ende (Sequenznummer) vom receive window
Wenn in der Zwischenzeit mehrere ACKs kommen, muß man die Information 
nur updaten, aber mehr wird es davon nicht.

> aufgrund der zigtausend RFCs blickt da glaube keiner mehr 100%ig durch

In der aktuellen rfc-index.txt steht zu jedem RFC die Gültigkeit. Alles 
was nicht aufm "standards track" ist, ist eh nur Unterhaltungsliteratur, 
und zu jedem Standard-RFC steht da, durch welche es ergänzt oder ersetzt 
wurde. Alles was ausdrücklich optional ist, braucht man nicht 
implementieren, ebenso alles, wo nicht MUST oder SHOULD (gemäß RFC2119, 
IIRC) dabeisteht. Und wenn man dabei keine Fehler macht, ist man immer 
noch standardkonform, also wo ist das Problem?   :)

Im Ernst: eine verständliche und vollständige TCP-Beschreibung habe ich 
auch noch nie gesehen. Viel pessimistische Kreativität ("was könnte noch 
alles schiefgehen?") ist nötig, wenn man sein eigenes TCP bauen will, 
sonst macht man damit im Betrieb viele "interessante" Erfahrungen.

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.