Hallo Forum, ich baue mit WinSock (Windows2000-Rechner) eine TCP/IP-Verbindung zu einem Ethernetgerät auf. Den Code zum Verbindungsaufbau und zum Datenaustausch habe ich mir im Internet zusammengesucht - funktioniert alles. Jetzt möchte ich Nutzdaten >512Bytes verschicken (mittels send()). Das Betrachten der Netzwerkdaten mit Wireshark zeigt mir, dass Windows aus meinen 529Byte verschickten Nutzdaten, leider 2 Pakete macht: Einmal mit 512Bytes und sofort im Anschluss ein weiteres mit den restlichen 17Bytes. Frage: Welchen Verbindungsparameter muss ich ändern, damit ich Pakete mit größerem Nutzdatenanteil verschicken kann? (Ich möchte mindestens 1k Ethernetframes verschicken). Von den vielen Parametern für setsockopt() habe ich leider auch keinen passenden gefunden. Habe ich wichtige Angaben vergessen? Danke, Gruß, Gaston.
>Frage: Welchen Verbindungsparameter muss ich ändern, damit ich Pakete >mit größerem Nutzdatenanteil verschicken kann? (Ich möchte mindestens 1k >Ethernetframes verschicken). Antwort: Die MTU Diese Antwort wird dir nur nichts bringen. Du willst eine TCP/IP Verbindung aufbauen. Da bist du in einer ganz anderen Schicht, warum interessiert du dich dafür wie er es in tieferen Schichten in Ethernetframes zerlegt? Das ist Angelegenheit der Verbindung zwischen den beiden Geräten auf Ethernetlevel. Kann ja durchaus sein das dein anderes Gerät (oder die Verbindung) nur maximal 512Byte Pakete verträgt. Also komm nicht auf die Idee die MTU ändern zu wollen.
TCP ist ein streamorientiertes Protokoll. Das heißt, daß du da einfach die Daten auf der einen Seite reinschiebst, und am anderen Ende kommen sie in derselben Reihenfolge und ohne Fehler wieder raus. Es ist nicht deine Sache, was dazwischen passiert. Wenn du einzelne Pakete mit ganz bestimmter Größe verschicken willst, mußt du stattdessen UDP verwenden. Dann mußt du dich aber um alles selbst kümmern.
> Du willst eine TCP/IP Verbindung aufbauen. Da bist du in einer ganz > anderen Schicht, warum interessiert du dich dafür wie er es in tieferen > Schichten in Ethernetframes zerlegt? Stimmt eigentlich. Ok, dann muss ich an einem anderen Punkt ansetzen. > Kann ja durchaus sein das dein anderes Gerät (oder die Verbindung) nur > maximal 512Byte Pakete verträgt. Nein, das Embedded-Ethernetdevice kann größere Ethernetframes empfangen und es will das sogar, da es mir die Firmware erleichtert wenn >512Byte Nutzdaten auf einmal kommen. Da der PC, der mit dem Ethernetgerät spricht, im selben lokalen Netz hängt, dürfte es auch kein Problem sein, wenn ich die MTU entsprechend groß einstelle. > Also komm nicht auf die Idee die MTU ändern zu wollen. Hoppla. ;-) Im Ernst: 1.) Wie kann ich prüfen, ob meine MTU windowsseitig tatsächlich auf die 512Byte begrenzt ist? 2.) Kann ich die MTU nur für diese eine Verbindung ändern? 3.) Wie kommt diese MTU von 512Bytes überhaupt zustande? Die wird ja nicht von den beiden Ethernetkommunikationspartnern ausgehandelt!?! Danke, Gaston
>> Kann ja durchaus sein das dein anderes Gerät (oder die Verbindung) nur >> maximal 512Byte Pakete verträgt. >Nein, das Embedded-Ethernetdevice kann größere Ethernetframes empfangen >und es will das sogar, da es mir die Firmware erleichtert wenn >512Byte >Nutzdaten auf einmal kommen. Wie Rolf Magnus schon schrieb, TCP ist streamorientiert. Das ist ein Bytestream, da ist nichts mit Paketen!!! (Auch wenn es in einer tieferen Schicht zerlegt wird) Niemand hindert dich in deiner Firmware mit (TCP Protokoll)Paketen >512Byte zu arbeiten. Nur so als Anmerkung: Wenn man etwas programmiert: Soll man es als erstes sauber schreiben. Dann überprüft man die Performance. Sollte dies nicht ausreichen, dann: 1. Optimierung des Algorithmus wenn es dann immer noch nicht reicht 2. Optimierung tieferliegenderer Schichten (da kommt dann meist ein unsauberer Code raus für den man sich bei der nächsten Änderung verflucht) 2. Bringt nur wirklich was, wenn man das Zusammenwirken der einzelnen Schichten verstanden hat. Solltest du trotzdem mit der MTU rumspielen wollen. Die Einstellungen sind in der Registry. Für Ethernet gilt 1500 Byte als (größte) MTU. Wenn du nach draußen über einen DSL gehst max. 1488 einstellen.
Die MTU ist bei Ethernet üblicherweise 1500 und etwas mehr, aber sowas sollte bei TCP völlig Schnuppe sein. Wenn z.B. mehrere send mit kleinen Datenmengen ausgeführt werden dann sammelt der IP Stack die erst (üblicherweise 200ms lang) bis die Daten verschickt werden. Es ist eben ein kontinuierlicher Datenstrom. Wenn man Pakete haben möchte muß man die halt selbst da rein definieren. Also einen kleinen Header mit z.B. Messagetyp als int32 und Messagelength als int32. Dann muß der Empfänger erstmal solange lesen bis der Header komplett ist und dann nochmal mit der Messagelength lesen bis diese komplett ist. Das macht man in einer Schleife weil die Gesamtlänge ja häppchenweise kommen kann. Absichern sollte man das evtl. noch durch ein Timeout beim Lesen und die Messagelenth plausibilisieren. Nach einem Verbindungsabbruch (beim Lesen sind empfange Pakete mit Länge 0 z.B. ein Abbruch vom Gegner) muß die Statusmaschine rückgesetzt werden, d.h. Empfangenes verwerfen und wieder auf Header warten. Und auch beim Senden mit einer Schleife arbeiten, ein send mit 1kB z.B. bedeutet nicht das wirklich alles mit einem send weggeschrieben wird, das ist ein gern gemachter Fehler.
> Solltest du trotzdem mit der MTU rumspielen wollen. Ich will NICHT global die MTU verstellen, sondern nur für diese eine Verbindung. Aber das geht ja leider nicht separat. Eigentlich möchte ich nur verstehen warum ausgerechnet in dieser Tcp-Verbindung die Pakete nach 512Nutzdatenbytes (bzw. 566Bytes "on wire") aufgeteilt werden. Mache ich eine Tcp(http)-Verbindung zu google.de auf, dann unterteilt mir Windows auch nicht nach 512(Nutzdaten)Bytes die Ethernetframes, sondern beispielsweise nach 1419Bytes "on wire" (Wireshark). > TCP ist streamorientiert. Das ist ein Bytestream, da ist nichts mit > Paketen!!! (Auch wenn es in einer tieferen Schicht zerlegt wird) Inzwischen wurde mir klar, dass TCP und die Ethernetgröße keinen direkten Zusammenhang haben, aber Windows scheint hier doch irgendwie zwischen den einzelnen Verbindungen zu unterscheiden?!? Wie ist sonst das Beispiel oben mit Google zu erklären? > Niemand hindert dich in deiner Firmware mit (TCP Protokoll)Paketen > >512Byte zu arbeiten. Doch, vermutlich Windows, da es verhindert, dass mein PC-Programm an mein Ethernetdevice größere Ethernetframes schickt. Und hier möchte ich an Windows drehen, dass größere Frames ankommen. Hintergrund: Mein EthDevice empfängt per Tcp Kommandos, die maximal 1040Bytes lang sind. Die Auswertung der empfangenen Eth-Frames geschieht alle 10ms durch den embedded IP-Stack. Anschliessend wird das übertragene Kommando ausgewertet. Treffen jetzt 2 Eth-Frames innerhalb <10ms ein, so überschreibt das eine Frame das vorhergehende, da der IP-Stack nur alle 10ms Daten ausspuckt (Laufzeit/Performancegründe). Ich kann vom PC aus sicherstellen, dass keine 2 Kommandos innerhalb von 10ms abgesetzt werden. (Der fertige IP-Stack in der Firmware stellt von sich aus schon sicher, dass ich keine Daten geliefert bekomme, die nicht aus dem Socket kommen). Was ich aber nicht sicherstellen kann, ist, dass irgendeine Schicht im PC die Daten aufteilt und in mehreren Eth-Frames verschickt. @JojoS: Im Prinzip passiert das ja alles schon so wie du schreibst, allerdings werden die Kommandos, die über TCP transportiert werden soll, en bloc und einmalig mit z.B. 529Bytes übertragen. Ich hätte gehofft, das dass dann ausreicht, um in EINEM Ethernetframe übertragen zu werden...
ein Grund kann sein das dein Device nur wenig Speicher hat und dem Sender eine kleine Windowsize mitteilt. Das sieht man auch in der Analyse mit dem Wireshark. Wenn der Empfänger seinen Buffer voll hat macht der die Windowsize immer kleiner um den Sender auszubremsen, d.h. bei TCP braucht man sich üblich auch nicht um eine Sendebegrenzung zu kümmern (nur auf höherem Level um z.B. in der App. keine Fifos zum Überlauf zu bringen).
> Für Ethernet gilt 1500 Byte als (größte) MTU.
Das ist übrigens inklusive TCP/IP-Header. Für Nutzdaten bleiben davon
nur 1460 Bytes übrigen.
> dem Sender eine kleine Windowsize mitteilt.
Genau das war der entscheidende Hinweis! Im embedded IP-Stack war der
Parameter MTU auf 512 eingestellt (default). Nach Änderung auf einen
größeren Wert klappt alles wie gewünscht! Ich suchte den Parameter auf
Windowsseite, obwohl das zwischen den beiden Geräten ausgetauscht
wird... Jetzt läufts. :-)
Danke.
Als wäre es nicht schon gesagt worden: Wolfram wrote: > Kann ja durchaus sein das dein anderes Gerät (oder die Verbindung) nur > maximal 512Byte Pakete verträgt. Also komm nicht auf die Idee die MTU > ändern zu wollen. Und um deine Frage oben zu beantworten, die MTU wird indirekt ausgehandelt. Sprich: Mit jedem Ack des Empfängers wird der noch vorhandene freie Speicher (schon genannte "Window-Size") mitübertragen. Siehe Wireshark.
So, weiter gehts. Nachdem das Problem mit der MTU nun geklärt ist, kann ich problemlos Pakete verschicken/empfangen, die größer als 512Bytes sind. ABER: Die nächste Grenze liegt bei 536Bytes. Windows schickt mir keine Ethernetframes mit mehr als 536Bytes Nutzdaten. RFC879 sagt, dass alle Geräte mind. 536Bytes (=576-20-20) abkönnen müssen. Wollen sie mehr übertragen, müssen sich beide Teilnehmer einig sein... Das bedeutet, dass dieser Parameter auch ausgetauscht werden muss. Ich vermute: IP-Header OPT-Field: Maximum segment size. Leider unterstützt mein embedded IP-Stack keine IP-Opt-Parameter. :-( Bevor ich jetzt den IP-Stack aufbohren muss, würde ich Windows gerne so einstellen, dass für diese Verbindung die MSS von 1460Bytes gelten soll, ohne dass dies explizit ausgetauscht werden muss/wurde. Frage: Gibt es diesen Parameter MSS und wie kann ich diesen für die aktuelle Socketverbindung einstellen? Danke, Gaston.
so ganz verständlich ist dein Problem nicht. Wenn auf der Empfängerseite eine while Schleife gebaut wird ist die Paketgrösse doch völlig unerheblich. Geräte die sich auf irgendwelche Annahmen verlassen sind als Komm.Gegner ein Graus. Im groben sowas:
1 | int ReceiveMessage(byte* buffer, int messageSize) |
2 | {
|
3 | int received=0; |
4 | while (received < messageSize) |
5 | {
|
6 | int n; |
7 | n = recv(socket, &buffer[received], messageSize-received); |
8 | received += n; |
9 | }
|
10 | |
11 | return received; |
12 | }
|
Fehlerbehandlung und andere Details habe ich mal weggelassen.
> so ganz verständlich ist dein Problem nicht. Ok, dann versuch ichs nochmal anders: > Hintergrund: Mein EthDevice empfängt per Tcp Kommandos, die maximal > 1040Bytes lang sind. Die Auswertung der empfangenen Eth-Frames geschieht > alle 10ms durch den embedded IP-Stack. Anschliessend wird das > übertragene Kommando ausgewertet. > Treffen jetzt 2 Eth-Frames innerhalb <10ms ein, so überschreibt das eine > Frame das vorhergehende, da der IP-Stack nur alle 10ms Daten ausspuckt > (Laufzeit/Performancegründe). Ich kann vom PC aus sicherstellen, dass > keine 2 Kommandos innerhalb von 10ms abgesetzt werden. (Der fertige > IP-Stack in der Firmware stellt von sich aus schon sicher, dass ich > keine Daten geliefert bekomme, die nicht aus dem Socket kommen). Was ich > aber nicht sicherstellen kann, ist, dass irgendeine Schicht im PC die > Daten aufteilt und in mehreren Eth-Frames verschickt. Und weiter: Der PC ist der Client, das embedded Device (eD) ist der Server. Der PC initiiert eine TCP-Verbindung ans eD und schickt seine Anfrage/Request/Kommando ab. Dabei kommt es vor, dass diese Anfrage in 2 Ethernetframes aufgeteilt wird. Das führt im eD zu Problemen, da der dortige IP-Stack aus Laufzeitgründen nur alle 10ms die Sockets bedienen kann. Ich kann also praktisch nicht 2 aufeinanderfolgende Ethernetframes im eD verarbeiten. Darum muss ich von aussen sicherstellen, dass die Anfrage an einem Stück eintrifft. Technisch gesehen spricht ja nichts dagegen zu fordern, dass der PC die (max.) 1040Byte in EINEM Frame abliefern muss - Windows kann das ja. Hierzu muss beim TCP-Verbindungsaufbau zwischen den Teilnehmern die MSS abgesprochen werden (hier: vom PC in Richtung embedded). Soweit ist mir das (inzwischen) klar geworden. Jetzt kommt das Neue gegenüber meinen Fragen von gestern: Der embedded IP-Stack hat eine unschöne Einschränkung: dieser kann von Hause aus nur IP-Frames ohne OPT-Header, in dem (vermutlich) der von mir gesuchte Parameter übertragen wird. Deswegen fällt es zunächst mal flach die MSS auf regulärem Wege beim Verbindungsaufbau auszutauschen. Ich wollte ich dem PC irgendwie auf anderem Wege mitteilen, dass er für die Verbindung zum eD eine andere MSS, wie die standardmäßig verwendete (vermutlich: 576Bytes), verwenden soll. Frage: Wie komme ich an die Windows-Socket-Verbindungsparameter und was muss ich da einstellen? (Ich denke dabei an irgendwas per setsockopt() oder so ähnlich). [while-Schleife mit:] > n = recv(socket, &buffer[received], messageSize-received); > received += n; Wie gesagt, das embedded Device ist der Server, d.h. dein geposteter Code läuft eigentlich dort. Und weiter: die Variable n erhalte ich nur alle 10ms (Zykluszeit IP-Stack), oder andersrum: das zweite eintreffende Eth-Frame überschreibt das erste und ich erhalte quasi nur das "n" für das zweite Frame. Konnte ich mein Problem inzwischen etwas deutlicher formulieren? Ist noch was unklar? Danke.
Ich denke du solltest auf UDP gehen. Dein Geraet erfuellt eigentlich keine TCP Anforderungen. TCP bedeutet : -Connection protokol -out of order packets -beliebige meldungsgroesse < 2^32 -retry Wenn den oberen schichten gemeldet wird es ging, dann ist es gegangen. Es wird auch innerhalb Limiten versucht, dass es geht. wahrend UDP bedeutet : -ohne connection -ohne retry -keine packetordnung Der Inhalt wird rausgeblasen, ob es auf der anderen Seite ankam interesssiert nicht.
das würde ich auch so sehen wie mein Vorredner. Wenn du nackte Frames bekommst hat das doch nix mehr mit TCP zu tun. Die z.B. 2 Pakete werden ja üblicherweise innerhalb weniger µs gesendet und auf der anderen Seite innerhalb eines Abfragezyklus (10ms sind ja eine Ewigkeit bei Ethernet) schon wieder zusammengebaut. recv() liefert keine Pakete sondern die Anzahl Bytes die man haben will, wie der Sender das geschickt hat bekommt man auf diesem Level gar nicht mehr mit. Und recv ist auch unabhänig von Server oder Client, die Aussage das das nicht gehen soll kann ich auch nicht nachvollziehen. C/S unterscheidet bei TCP doch nur wer die Verbindung bereitstellt (Server, passiv, listen/accept) und wer die aktiv herstellt (Client, aktiv, connect). Die genannten Funktionen sind in der Annahme das der IP-Stack die Standard Berkley Sockets unterstützt. Wenn du aber eher was zyklisches haben möchtest dann ist UDP einfacher, das ist paketorientiert und kann einfacher auf Frame Level ausgewertet werden.
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.