Forum: PC-Programmierung Socketprogrammierung Windows: Paketgröße


von Gaston (Gast)


Lesenswert?

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.

von Wolfram (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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.

von Gaston (Gast)


Lesenswert?

> 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

von Wolfram (Gast)


Lesenswert?

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

von JojoS (Gast)


Lesenswert?

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.

von Gaston (Gast)


Lesenswert?

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

von JojoS (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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

von Gaston (Gast)


Lesenswert?

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

von Simon K. (simon) Benutzerseite


Lesenswert?

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.

von Gaston (Gast)


Lesenswert?

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.

von JojoS (Gast)


Lesenswert?

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.

von Gaston (Gast)


Lesenswert?

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

von 1333 (Gast)


Lesenswert?

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.

von JojoS (Gast)


Lesenswert?

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