Seite 1 von 2

Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 07:22
von Stevie63
Hallo zusammen,
in einer PBServer-Applikation empfange ich über ReceiveNetworkData normale HTTP- und XML-HTTP-Requests.
Ich hatte jetzt einen Fall, wo mir ein Browser-Client einen POST-XML-HTTP-Request gesendet hat, der die content-length von 2379 Bytes hatte. Da dieser content von ReceiveNetworkData nicht komplett empfangen wurde, habe ich mit wireshark den eingehenden port abgehört und da ist mir folgendes aufgefallen: Der Request wurde in 3 sequences zerlegt mit der Angabe LEN=1440,1440,86, s.Bild

Bild

(da sind wahrscheinlich auch die Header-Längen dabei). Ich konnte sehen, daß ich mit ReceiveNetworkData nur die erste sequenz auslesen konnte (obwohl der Puffer 65kB groß ist und die Ausleseroutine auch sonst funktioniert). Meine Frage ist jetzt: Hat jemand eine Ahnung, ob diesem Verhalten von TCP bestimmte einstellbare Parameter zugrundeliegen? Und ist es möglich, mit ReceiveNetworkData mehrere Sequenzen nacheinander auszulesen?

Grüße

Stevie

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 10:20
von STARGÅTE
Das ReceiveNetworkData etwas, was komplett gesendet wurde, auch komplett empfängt, ist nicht immer gesagt.

Um alles zu erhalten musst du selber ReceiveNetworkData in einer schleife ausführen, um alles zu sammeln was kommt.

Beispiel: Sendne von 100kB

Code: Alles auswählen

InitNetwork()

#Length = 102400

If CreateNetworkServer(0, 7000) 
  Connection = OpenNetworkConnection("localhost", 7000)
  *Buffer = AllocateMemory(#Length)
  Debug "Gesendet: "+Str(SendNetworkData(Connection, *Buffer, #Length))
  Delay(100)
  Repeat
   Delay(10)
   Select NetworkServerEvent()  
    Case #PB_NetworkEvent_Data
     Repeat
      Length = ReceiveNetworkData(EventClient(), *Buffer+Shift, #Length)
      Debug "Empfangen: "+Str(Length)
      If Length > 0
       Shift + Length
      EndIf
     Until Length <= 0 Or Shift = #Length
   EndSelect
  Until Shift = #Length
  Debug "Gesamt: "+Str(Shift)
Else
  Debug "geht nicht"
EndIf
Gesendet: 102400
Empfangen: 79680
Empfangen: 12300
Empfangen: -1
Empfangen: 10420
Gesamt: 102400
In diesem Beispiel muss er 3 mal empfangen in 4 versuchen.
Trotzdem habe cih so das komplette Paket.

Dabei ist es egal wie groß der empfangesbuffer ist den du bei ReceiveNetworkData() angibst.

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 13:43
von Stevie63
STARGÅTE hat geschrieben:Das ReceiveNetworkData etwas, was komplett gesendet wurde, auch komplett empfängt, ist nicht immer gesagt.

Um alles zu erhalten musst du selber ReceiveNetworkData in einer schleife ausführen, um alles zu sammeln was kommt.

Beispiel: Sendne von 100kB

Code: Alles auswählen

InitNetwork()

#Length = 102400

If CreateNetworkServer(0, 7000) 
  Connection = OpenNetworkConnection("localhost", 7000)
  *Buffer = AllocateMemory(#Length)
  Debug "Gesendet: "+Str(SendNetworkData(Connection, *Buffer, #Length))
  Delay(100)
  Repeat
   Delay(10)
   Select NetworkServerEvent()  
    Case #PB_NetworkEvent_Data
     Repeat
      Length = ReceiveNetworkData(EventClient(), *Buffer+Shift, #Length)
      Debug "Empfangen: "+Str(Length)
      If Length > 0
       Shift + Length
      EndIf
     Until Length <= 0 Or Shift = #Length
   EndSelect
  Until Shift = #Length
  Debug "Gesamt: "+Str(Shift)
Else
  Debug "geht nicht"
EndIf
Gesendet: 102400
Empfangen: 79680
Empfangen: 12300
Empfangen: -1
Empfangen: 10420
Gesamt: 102400
In diesem Beispiel muss er 3 mal empfangen in 4 versuchen.
Trotzdem habe cih so das komplette Paket.

Dabei ist es egal wie groß der empfangesbuffer ist den du bei ReceiveNetworkData() angibst.

Hallo Stargate,

vielen Dank für dein Beispiel!

Zwei Fragen hätte ich noch dazu:

1.) Du benutzt als Abbruchkriterium deiner beiden Repeat-Schleifen (Empfänger) die Größe des Sendepuffers, der dir aber im Normalfall nicht bekannt ist. Wie würdest du die Abbruchkriterien deiner Schleifen gestalten, wenn dir als Empfänger #Length unbekannt ist?

2.) Könnte es vorkommen, daß bei bei quasi-gleichzeitigen Requests von mehreren clients du innerhalb deiner NetworkServerEvent()-Schleife verschiedene client-id's bekommst?

Grüße

Stevie

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 14:19
von STARGÅTE
>> "Du benutzt als Abbruchkriterium deiner beiden Repeat-Schleifen (Empfänger) die Größe des Sendepuffers, der dir aber im Normalfall nicht bekannt ist. Wie würdest du die Abbruchkriterien deiner Schleifen gestalten, wenn dir als Empfänger #Length unbekannt ist?"

Wenn du selber dafür verantwortlich bist, was du sendest, ist es Ratsam immer die Länge mitzusenden, damit beim emfangen das "Paket" wieder rekonstruiert werden kann.
Denn es ist auch durchaus möglich, das zwei unterschiedliche Sends zu einem Receive verschmelzen.
Wenn dann die Länge nicht bekannt war, kannst du die nicht mehr trennen.

Code: Alles auswählen

InitNetwork()

#Length = 1024

If CreateNetworkServer(0, 7000) 
  Connection = OpenNetworkConnection("localhost", 7000)
  *Buffer = AllocateMemory(#Length)
  Debug "Gesendet: "+Str(SendNetworkData(Connection, *Buffer, 50))
  Debug "Gesendet: "+Str(SendNetworkData(Connection, *Buffer, 50))
  Delay(100)
  Repeat
   Delay(10)
   Select NetworkServerEvent()  
    Case #PB_NetworkEvent_Data
     Repeat
      Length = ReceiveNetworkData(EventClient(), *Buffer+Shift, #Length)
      Debug "Empfangen: "+Str(Length)
      If Length > 0
       Shift + Length
      EndIf
     Until Length <= 0 Or Shift = 100
   EndSelect
  Until Shift = 100
  Debug "Gesamt: "+Str(Shift)
Else
  Debug "geht nicht"
EndIf
Die beiden 50Bytes kommen als ein "Block" 100Bytes an ...

Eine Abbruchebedingung ohne bekannter Länge ist eigentlich nich möglich, weil du nie weiß, ob das nun schon das letzte Byte war.
Denn wie du siehst muss durchaus mehrmals NetworkServerEvent() aufgerufen werden um alles zu erhalten.
Es reicht daher nicht nur bei ReceiveNetworkData abzuwarten bis nix mehr da ist.

Ich habe für meine Bedürfnisse, ein Include geschrieben, in dem Daten "sicherer" gesendet/empfangen werden.
Dabei wird halt immer die Länge des Pakets als Long in den ersten 4 Bytes gesendet.

>> "Könnte es vorkommen, daß bei bei quasi-gleichzeitigen Requests von mehreren clients du innerhalb deiner NetworkServerEvent()-Schleife verschiedene client-id's bekommst?"
Nein.
Dann bekommst du zwei mal ein #PB_NetworkEvent_Data bei NetworkServerEvent() mit verschiedenen EventClient()

Dabei hat jeder Client einen Eingangsbuffer von irgendwas mit 100KB ist dieser voll kann nichts mehr gesendet werden, erst wenn mit ReceiveNetworkData der Buffer ausgelesen wird.
EDIT: Im echten Netzwerk (kein localhost) ist der Buffer sogar noch kleiner ...

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 15:08
von X360 Andy
Könnte man Abschlussevent / Prüfung nicht einfach ein besonders zeichen als abschluss mitsenden ?

"Hallo ich bin da%"
sobald dann % vorkommt ist der String komplett da, die Schleife wird beendet.

% ist da natürlich schlecht.... aber ist ja nur ein verständliches Beispiel.


Ähnlich wie bei jedem HTTP Request, die 2 Zeilenumbrüche....

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 15:15
von STARGÅTE
Für einen AbschlussByte muss aber der Inhalt bekannt sein.
Ansonsten kann es zu fehlinterpretationnen kommen wennn Zahlen (Long, Float, ..) gesendet werden, statt normalen Strings.

Wenn man sich hingegen auf Strings beschrenkt, ist das senden einer abschluss NULL sicher ein einfachsten.

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 15:41
von Stevie63
Wie ich schon in der ersten post erwähnt habe, geht es bei mir um POST-HTTP-Requests. Da ich als Server die genaue Länge der gesendeten Daten nicht kenne, habe ich mir folgendes überlegt: Da ich ja bei POST-Requests "Content-length" mitgeliefert bekomme, kann ich diesen Wert für #Length nehmen (analog Stargate). Allerdings muss ich erst Header-Daten einlesen, bevor ich an diesen Wert komme. Habe ich aber erst mal diesen Wert eingelesen und kann genau bestimmen, an welchen Positionen des Eingangpuffers sich die Zeichenkette "Content-Length: xycycxx" befand, weiss ich jetzt, wieviele Bytes noch einzulesen sind und kann jetzt die Abbruchkriterien entsprechend setzen. Ich muss natürlich darauf aufpassen, daß ich evtl. schon content-Daten beim ersten ReceiveNetworkData eingelesen haben könnte; die muss ich dann von dem Wert von Content-Length abziehen.

Grüße

Stevie

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 17:21
von Nino
STARGÅTE hat geschrieben:Eine Abbruchebedingung ohne bekannter Länge ist eigentlich nich möglich, weil du nie weiß, ob das nun schon das letzte Byte war.
Denn wie du siehst muss durchaus mehrmals NetworkServerEvent() aufgerufen werden um alles zu erhalten.
Es reicht daher nicht nur bei ReceiveNetworkData abzuwarten bis nix mehr da ist.
In der Hilfe zu ReceiveNetworkData() steht:
'Ergebnis' gibt die Anzahl an tatsächlich bereits gelesenen Bytes an. Ist 'Ergebnis' gleich 'DatenPufferLaenge', dann sind noch mehr Daten zum Einlesen verfügbar.
Danach müsste ja sowas gehen (vereinfacht):

Code: Alles auswählen

Result$ = ""
Repeat
   Anzahl = ReceiveNetworkData(Verbindung, *DatenPuffer, DatenPufferLaenge)
   Result$ + PeekS(*DatenPuffer, Anzahl)
Until Anzahl <> DatenPufferLaenge
Aber das scheint ja nicht zu funktionieren. Habe ich etwas über sehen, oder ist diese Angabe in der Hilfe falsch?

Grüße, Nino

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 17:26
von Jilocasin
Soweit ich weiß haben HTTP-Requests & Co. doch ohnehin immer zwei abschließende CRLFs.
Damit dürftest du schon mal beim Server das Ende deiner Anfrage finden.

Die 1440 Byte könnten im Übrigen an der eingestellten MTU liegen. (interessant: "PPPoE (z. B. DSL) ≤ 1492")
Ist sogar sehr warscheinlich, da 1440 recht häufig vorkommt. Dann wird das gesendete Client-Paket gesplittet und kommt oftmals auch so "einzeln" an.

Re: Frage zu ReceiveNetworkData

Verfasst: 31.08.2010 17:31
von STARGÅTE
@Nino

Deine Variante wäre die richtige Variante abzubrechen wenn der Buffer des von ReceiveNetworkData() zuende ausgelesen wurde.
Aber nur weil nichts mehr im Buffer steht, heißt nicht, dass alles empfangen wurde, wie ja auf meinem ersten Beispiel hervorgeht.

Deine Variante ist vorallem dafür geeignet wenn du eine kleine "DatenPufferLaenge" wählst,
Ist diese zB nur 16 Byte und du sendest zB 40 byte
Bekommst du volgende Anzahl:
16
16
8
Nach der 8<>DatenPufferLaenge weißt du dann, dann nichts mehr im Buffer steht.
Ob allerdings alles emfangen wurde geht daraus nicht hervor !

Und so steht es ja auch in der Hilfe:
"'Ergebnis' gibt die Anzahl an tatsächlich bereits gelesenen Bytes an."

Da gehts nur ums lesen des Buffers, nicht um das eigentlich emfangen von Daten.