Frage zu ReceiveNetworkData

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
Stevie63
Beiträge: 86
Registriert: 06.05.2009 23:02
Computerausstattung: Win 10, i7, fsc celsius

Frage zu ReceiveNetworkData

Beitrag 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
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Frage zu ReceiveNetworkData

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Stevie63
Beiträge: 86
Registriert: 06.05.2009 23:02
Computerausstattung: Win 10, i7, fsc celsius

Re: Frage zu ReceiveNetworkData

Beitrag 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
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Frage zu ReceiveNetworkData

Beitrag 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 ...
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
X360 Andy
Beiträge: 1206
Registriert: 11.05.2008 00:22
Wohnort: Bodensee
Kontaktdaten:

Re: Frage zu ReceiveNetworkData

Beitrag 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....
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Frage zu ReceiveNetworkData

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Stevie63
Beiträge: 86
Registriert: 06.05.2009 23:02
Computerausstattung: Win 10, i7, fsc celsius

Re: Frage zu ReceiveNetworkData

Beitrag 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
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Frage zu ReceiveNetworkData

Beitrag 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
Jilocasin
Beiträge: 665
Registriert: 13.05.2006 16:04
Kontaktdaten:

Re: Frage zu ReceiveNetworkData

Beitrag 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.
Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Frage zu ReceiveNetworkData

Beitrag 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Antworten