Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Ist der Server zu "schnell"? (https://www.delphipraxis.net/159456-ist-der-server-zu-schnell.html)

NightroadSora 28. Mär 2011 21:24

Ist der Server zu "schnell"?
 
Ich hab ein Netzwerkprojekt. Der Client stellt bestimmte Anfragen, sendet erst nen Text und danach sofort nen Buf. So Wenn ich beim Server nen Haltepunkt einstelle und schrittweise durchgehe, klappts alles problemlos, wenn ich den rausnehme krieg ich ne Class Exception!

Kann ich delphi irgendwie sagen dass es n paar millisekunden oder so warten soll, damit evtl das DAtenpaket Zeit hat anzukommen?
Ich verzweifle an diesem verdammten Problem schon den ganzen Abend...


Das macht der server beim lesen...
Delphi-Quellcode:
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
    Info:TInfo;
    Zahl:Integer;
begin
IDMSG:=Socket.receivetext;
if ClientReadCount < 1 then
case StrToInt(IDMSG) of
1 :
    begin
    Socket.ReceiveBuf(Info,SizeOf(Info));
    CustomerErstellen(Kaeuferzaehler,Info);
    read_customer(Kaeuferzaehler);
    Kaeufer:=KSearch(Kaeuferzaehler);
    memo2.Lines.add(Kaeufer.get_name);
    KDelall;
    end;
2: begin
    Socket.ReceiveBuf(Info,SizeOf(Info));
    Kaeufer:=Customerfinden(StrToInt(Info.name),Kaeuferzaehler); //Name enthält hier einfach nur die KID!
    with Info do begin
                  Name:=Kaeufer.get_name;
                  Vorname:=Kaeufer.get_vorname;
                  Adresse:=Kaeufer.get_adresse;
                  end;
    Socket.Sendtext('0201');
    Socket.Sendbuf(Info,SizeOf(Info));
    end;
end;
inc(ClientreadCount);
end;
Er kriegt von nem Clientprogramm entsprechend einen sendtext um ins richtige Case zu springen, und den Buf mit namen info.
Im Prinzip ist alles untere egal, denn das Ding ist, wenn ich wie gesagt nen Haltpunkt bei der Zeile:
IDMSG:=Socket.receivetext; setze und dann F9 drücke um den Rest durchzurattern geht alles hervorragend... wenn ich den Haltepunkt wegmache krieg ich den Fehler '' ist kein gültiger Integerwert ...

alzaimar 28. Mär 2011 21:36

AW: Ist der Server zu "schnell"?
 
Umschließe den Quellcode bitte mit Delphi-Tags.
Dazu einfach den Quelltext markieren und auf das Delphi-Icon klicken. Danke!

Zu deinem Problem: Die Daten purzeln nicht so aus dem Socket, wie Du sie hereinschüttest. Es kann also sein, das Du nur immer mal wieder ein Byte bekommst. Du musst selbst sicherstellen, das Du alle Daten bekommst.

Du kannst zuerst die Anzahl der zu übertragenen Bytes schicken, z.B. als INT und danach die Daten selbst.

Auf der Empfangsseite liest Du zuerst 4 Bytes, dekodierst die in einen INT und anschließend wartest Du, bis alle Bytes da sind. Erst dann kannst Du die Daten interpretieren.

NightroadSora 28. Mär 2011 21:55

AW: Ist der Server zu "schnell"?
 
Kannst du mir mal bitte n beispiel geben wie das aussehen soll? Ich hab grade echt keinen Dunst wie ich das machne soll, ich meine was soll ich da mitm INT? Das is noch nur die Größe des Datensatzes was soll mir das bringen?
und wenn du selbst sagt die Daten kommen nich so raus wie ich sie reinsende, wie komm ich denn dann immer an den Wert Datei?

Satty67 28. Mär 2011 22:25

AW: Ist der Server zu "schnell"?
 
Soweit ich das verstanden habe, wird OnClientRead ausgelöst, wann immer der Client Daten sendet (bereits wenn dazu bereit ist), nicht nur wenn alle Daten gesendet wurden. Das erste Auslösen des Ereignis kann also bereits sein, wenn der Lesepuffer noch leer ist

Wenn IDMSG sowieso immer ein Integer (als String) ist, dann reicht auch ein EOD Zeichen (z.B. ein Punkt). Auf den warten und vorm StrToInt ausschneiden.

PS: alzaimars Vorschlag wäre sinnvoller, wenn Du Dir gleich eine allgemeine Funktion schreibst, die einen Datenblock liest. Du scheinst ja mehrere unterschiedliche Datenblöcke zu senden.

NightroadSora 28. Mär 2011 22:47

AW: Ist der Server zu "schnell"?
 
Jaaa okay kann ja sein dass das mehr Sinn machen würde, aber WIE schreibe ich so eine Funktion?
WIe lese ich so einen Datenblock denn aus?!
Das wurde in keinem der 3 Netzwerktutorials erläutert, die ich gelesen habe, da haben se einfach das Record reingehaun, ausgelesen so wie ichs gemacht habe, drauf zugegriffen und die ganze Sache lief!

Wieso gehts bei mir nicht, was is bei mir anders?

Kann ich denn auch SendText und Sendbuf gleichzeitig iwie verwenden? Bei mir läufts ja im Client programm nacheinander...

wicht 29. Mär 2011 00:03

AW: Ist der Server zu "schnell"?
 
Woher hast du das Beispiel da oben denn? Ich würde jedenfalls sagen, dass das nur in den seltensten Fällen so funktionieren kann. Versuch es doch mal nach folgendem Schema...

Client:
Code:
procedure SendeDaten;
var
  Rec: TInfo;
begin
  Rec.Zahl := 123;
  Rec.Zahl2 := 456;
  Client.SendBuf(Rec, SizeOf(Rec));
end;
Server:
Code:
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Info: TInfo;
begin
  if Socket.Peek >= SizeOf(Info) then
    Socket.ReadBuf(Info, SizeOf(Info));
end;
Du musst eben immer prüfen, ob schon alle Daten da sind, die du einlesen möchtest (hier mit der Methode Peek(), die es vermutlich so nicht gibt an deiner Klasse). Einfach in einen Record lesen, obwohl keine Daten da sind, geht dann schief, und deine Exception "'' ist kein gültiger Integerwert" deutet darauf hin, dass ReceiveText() keine Daten bekommt und StrToInt() daneben geht. Achja, ausserdem dürfen keine normalen Strings in dem Record enthalten sein, wenn man ihn so verschickt/empfängt.
Wenn du flexibel sein möchtest, solltest du das Protokoll vielleicht auch etwas ändern. Man könnte vielleicht Kommandolänge+Kommandotyp+Nutzdaten nehmen, also schickt der Client zwei Integers und dann die Nutzdaten, der Server wartet mit dem Auslesen, bis er die Mindestlänge empfangen hat (den ersten Integer für die Länge). Wenn dieser da ist, weiß der Server, auf wieviele Bytes er noch warten muss. Ist dann alles da, kann man das Paket auseinandernehmen und verarbeiten.

alzaimar 29. Mär 2011 06:37

AW: Ist der Server zu "schnell"?
 
Wie ich bereits erwähnte, wird OnClientRead aufgerufen, wenn Daten da sind. Das heißt aber nicht, dass das erst passiert, wenn ALLE Daten da sind. So ein Socket ist wie ein Schlauch, durch den Kugeln rollen. Du kippst vorne welche rein und irgendwann trudeln die hinten wieder raus. Aber ob die am Stück herauskommen, oder peu-a-peu, das weiss man nicht. Das einzige, worauf Du dich (eigentlich) verlassen kannst, ist die Reihenfolge der Kugeln und das alle ankommen (meistens). Wenn aber jemand den Schlauch durchschneidet, haste Pech.

Du musst Dir selbst Gedanken machen, wie Du sicherstellst, das alle Daten angekommen sind. Dazu kannst Du auf die erste Kugel schreiben, wie viele denn nun kommen. Oder Du färbst die letzte Kugel ROT. Dann dürfen aber in den Nachrichten keine rote Kugeln vorkommen. Oder Du überklebst rote Kugeln mit einem großen Schild: "ROTE Kugel" und färbst die im Client wieder ein, nachdem die ENDE-Kugel (die ja auch rot ist) angekommen ist.

Egal wie, Du musst die Daten also erst einmal sammeln.

So zum Beispiel (Pseudocode, aber vielleicht verständlich):
Delphi-Quellcode:
...
MyBuffer.AddBytes (BytesFromSocket);
While True Do
  If TotalLength = 0 then
    If MyBuffer.Count >= 4 then begin // 4-Byte Längeninfo ist angekommen
      TotalLength := MyBuffer.FirstFourBytesAsInteger; // Die 4 Bytes als Integer interpretieren
      MyBuffer.RemoveBytes (4); // und dann vorne wegschnippeln
    end else
      break // Die Längeninformation ist noch nicht vollständig
  else
    If MyBuffer.Count >= TotalLength then begin // Alle Bytes sind angekommen
// (es könnte aber auch schon ein Teil des nächsten Blocks dabei sein)
      Data := MyBuffer.ExtractBytes (TotalLength); // Datenblock extrahieren
      MyBuffer.RemoveBytes(TotalLength); // wegschnippeln
      ProcessData (Data); // verarbeiten (sollte schnell gehen)
      TotalLength := 0; // und auf den nächsten Block warten
  End else
    break; // Die Dateninformation ist noch nicht vollständig
Die Schleife dient dazu, sofort alle vollständig empfangenen Blöcke zu verarbeiten.

Alternativ kannst Du auch mit einem speziellen Endezeichen (rote Kugel) arbeiten:
Delphi-Quellcode:
...
MyBuffer.Add (BytesFromSocket);
EndPos := MyBuffer.PositionOf (EndOfDataMarker);
While EndPos >= 0 Do Begin
  Data := MyBuffer.ExtractBytes (EndPos); // Datenblock extrahieren
  MyBuffer.RemoveBytes(EndPos+1); // Datenblock + Endmarker wegschnippeln
  ProcessData (Data); // verarbeiten (sollte schnell gehen)
  EndPos := MyBuffer.PositionOf (EndOfDataMarker);
End;

NightroadSora 29. Mär 2011 16:41

AW: Ist der Server zu "schnell"?
 
OKay die Idee an sich dahinter hab ich jetzt immerhin schonmal verstanden. Aber wie krieg ichn jetzt explizit bei nem TServerSocket raus, wie viel er schon empfangen hat?

alzaimar arbeitet ja mit ner Variable namens mybuffer, von welchem Typ soll die denn sein? Ich kenne die methoden addbytes, extractbytes usw nämlich gar nich...

alzaimar 29. Mär 2011 16:46

AW: Ist der Server zu "schnell"?
 
Das ist pseudocode. Aber wenn Du noch gar nicht richtig programmieren kannst, sind Sockets eh nix für dich.

Versuch dich an den Indy-Komponenten, das sollte einfacher für dich sein.

NightroadSora 29. Mär 2011 16:55

AW: Ist der Server zu "schnell"?
 
Programmieren kann ich schon, nur dachte ich trotzdem dass du mit Klassenobjekten arbeiten, von denen mit die Methoden einfach nur unbekannt sind mehr nich...

Die Indykomponenten lad ich mir tatsächlich grade runter, in der Hoffnung dass die besser funktionieren so wie ichs mir vorstelle...

NightroadSora 29. Mär 2011 22:10

AW: Ist der Server zu "schnell"?
 
Also mit den INdykomponenten funktioniert bei mir gar nichts...

also nochmal:
Ich muss jetzt auslesen a) Wie groß wird die gesendete Information sein die an den Server geschickt wird
b) wie groß ist der Datensatz den Server bereits empfangen hat!

So... ich möchte jetzt gern erlernen wie das bei einem TServerSocket funktionieren würde, im INternet finde ich einfach keine vollständige Methoden und Eigenschaftenübersicht...

Zumal ServerSocket.Socket hat ja die Methode recievelength, aber wieso gibts für Client/Server.Socket keinen Sendlength? Is doch völlig bescheuert... und wenn ich jetzt SizeOf(Info) bei nem recieve(Info,SizeOf(Info)) verarbeiten will, is das immer irgendwie nichts...

Man wie mach ich das denn? Das kann doch nich so schwierig sein...
Ich verstehe einfach nich wie ich den Buffer des servers auslesen soll!

wicht 29. Mär 2011 22:20

AW: Ist der Server zu "schnell"?
 
Zitat:

Ich muss jetzt auslesen
a) Wie groß wird die gesendete Information sein die an den Server geschickt wird
b) wie groß ist der Datensatz den Server bereits empfangen hat!
a) ist über. Warum muss der Client das wissen? Der schickt einfach ganz stumpf die Daten. Wenn er was wissen muss, ließt er einfach die Daten, die vom Server zurückkommen.
b) siehe hier, falls das die Komponente ist, die du benutzt. Damit weißt du dann, wieviel Daten schon da sind. Und wenn ReceiveLength > SizeOf(Integer) (Länge am Anfang des Pakets) oder ReceiveLength > SizeOf(Record) (Record empfangen), je nach dem Protokoll, das du baust, kannst du die Daten dann auch lesen.


HTH..

NightroadSora 29. Mär 2011 22:29

AW: Ist der Server zu "schnell"?
 
Zitat:

Zitat:

Ich muss jetzt auslesen
a) Wie groß wird die gesendete Information sein die an den Server geschickt wird
b) wie groß ist der Datensatz den Server bereits empfangen hat!
a) ist über. Warum muss der Client das wissen? Der schickt einfach ganz stumpf die Daten. Wenn er was wissen muss, ließt er einfach die Daten, die vom Server zurückkommen.
die Anfrage von a) soll nich der Client auslesen sondern der Server...
ich werde mir die Seite gleich mal anschauen und deinen Rat ausprobieren.

Ich bedanke mich übrigens jetzt schonmal dass ihr mich bei meinem Problem so kräftig unterstützt =)

Sir Rufo 29. Mär 2011 22:31

AW: Ist der Server zu "schnell"?
 
Wenn der Client Daten an den Server sendet und erst die Länge der Nachricht verschicken soll, dann muss der Client doch wissen, wie groß die Nachricht ist, odda? ;)

wicht 29. Mär 2011 22:42

AW: Ist der Server zu "schnell"?
 
Zitat:

die Anfrage von a) soll nich der Client auslesen sondern der Server...
Wie Sir Rufo schon sagte, der Client weiß immer, wieviel er gesendet hat. Dafür muss man sich nichtmal mit Sockets auskennen. Wenn du festlegst, dass zuerst immer ein Integer geschickt wird (Länge des Kommandos), muss der Server eben warten, bis 4 Byte (=SizeOf(Integer)) angekommen sind, und danach wartet er, bis das, was danach empfangen wurde, in der Länge dem Wert des zuerst übertragenen Intergers gleich oder größer ist.
Hätte ich meine Socket-Geschichten nicht in einer eigenen Bibliothek untergebracht und auch auf mitgelieferte Komponenten gebaut, würde ich dir hier ein dickes Beispiel posten können, aber das ist eben was komplett anderes, für 'außenstehende' kompliziert zu verstehen und eben auf mein eigenes Protokoll zugeschnitten..

LG

NightroadSora 29. Mär 2011 23:15

AW: Ist der Server zu "schnell"?
 
Immer wieder dieses SizeOf(Integer). Ich verstehe ja was damit gemeint ist. Aber wie soll das funktionieren? ReceiveLength sagt aus wie viel insgesamt ankommen soll, das hab ich kapiert.
Aber Wie komm ich jetzt ans SizeOf(Integer)?
Also für meinen Code würde es ja so lauten: ReceiveBuf(Info,SizeOf(info)), so aber ich kann ihm doch nicht sagen, dass er erst empfangen soll, quasi wenn die Länge des Empfangenem Records, das er ja noch gar nich empfangen hat, gleich groß der gesendeten Datenmenge ist...
Mein Problem ist doch, um zu wissen wie groß das Empfangene ist, muss ichs doch empfangen... aber nachdem was ihr gesagt habt, soll ich den Befehl erst nutzen wenn alles da ist, aber an die Information komme ich doch vorher gar nicht...

nur mal so nebenbei, wenn ich sequenziell jetzt durchgehe kommt bei mir generell nur noch völliger Schrott an, also wenn ich das Record empfange, was ja geht wenn ich schrittweise durchgehe wie ich ja im Startbeitrag schon geschrieben habe, und das obwohl ich an diesem Code gar nichts geändert habe... das passiert mir jetzt schon zum vierten mal, woran liegt das denn immer?

Sir Rufo 29. Mär 2011 23:38

AW: Ist der Server zu "schnell"?
 
Und genau hier liegt der Fehler

ReceiveLength sagt dir wieviel empfangen wurde!

Wenn das die Daten sind die zum Socket unterwegs sind (i = Integer-Header, d = Daten)
Code:
iiiiddddddddddddddddddddddddddddiiiiddddddddddiiiidddddddddd
Dann wird jedes Mal, wenn etwas empfangen wird, das Event aufgerufen.
Nun kann es eben auch passieren, dass eben immer nur ein Teil der Daten ankommt.

Code:
1 ii
2 iiiidddddddddddddd
3 ddddddddddddddddddddddddddddiiiiddddddddddiiiidddddddddd
Zum Zeitpunkt 1 ist ReceiveLength 2 und somit kleiner als SizeOf( Integer ) - warten
Zum Zeitpunkt 2 ist ReceiveLength 18, jetzt lesen wir vom Buffer den Integer-Wert aus
Der Buffer wird beim Lesen automatisch um die gelesenen Bytes verkleinert.
Jetzt muss man abwarten, bis der Buffer mindestens soviel Bytes enthält, wie der Integer-Wert angekündigt hat und wir können die komplette Nachricht vom Buffer lesen.

NightroadSora 30. Mär 2011 05:32

AW: Ist der Server zu "schnell"?
 
Delphi-Quellcode:
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
    Info:TInfo;
    Zahl:Integer;
    Laenge,VergleichLaenge:integer;
begin
IDMSG:=Socket.ReceiveText;
Laenge:=Length(IDMSG);
Laenge:=Laenge + Socket.ReceiveBuf(Info,SizeOf(Info));
if Socket.receivelength = Laenge then
Also könnte ich doch z.B. sone Abfrage hier gestalten, es soll nur weitergehn wenns die bufferlaenge gleich der empfangenen Länge ist. Würde das jetzt so stimmen?
ALso dass man statt mit der If-Then-Else Condition lieber mit ner Whileschleife arbeiten sollte weil man sonst aus der OnRead Prozedur längst rausgeflogen ist eh die Daten angekommen sind, das weiß ich ja selbst, aber es geht mir erstmal IMMER NOCH darum zu verstehn wie ich jetzt auf den Buffer zugreife denn das weiß ich jetzt immer noch nicht!

Wie bereits gesagt, wie ich was mit ReceiveLength vergleiche hab ich ja kapiert, aber ich muss ja irgendwo die INformation herbekommen wie viel mir gesendet werden SOLL... :wiejetzt:

Sir Rufo 30. Mär 2011 06:00

AW: Ist der Server zu "schnell"?
 
Wenn die Daten verschickt werden, ermittelt der Sender die Größe der Nachricht (ein Integer) und schickt diesen als erstes auf die Reise. Direkt danach die Daten.
Der Empfänger liest jetzt erst den Integer aus dem Buffer und weiß wie groß die Nachricht ist und empfängt - bzw. Wartet bis alle Daten da sind - die Nachricht.

wicht 30. Mär 2011 10:03

AW: Ist der Server zu "schnell"?
 
Code:
type
  TForm1 = class(TForm)
  private
    LaengeNaechsteNutzdaten: Integer;
  end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Info: TInfo;
begin
  // Gibt es genug, um die Länge auszulesen?
  if (LaengeNaechsteNutzdaten = 0) and (Socket.ReceiveLength >= SizeOf(LaengeNaechsteNutzdaten)) then
    // Dann auslesen
    Socket.ReceiveBuf(LaengeNaechsteNutzdaten, SizeOf(LaengeNaechsteNutzdaten));

  if (LaengeNaechsteNutzdaten > 0) and (Socket.ReceiveLength >= LaengeNaechsteNutzdaten) then
  begin
    // Wenn der Puffer die Länge der Nutzdaten erreicht hat, auslesen
    Socket.ReceiveBuf(Info, LaengeNaechsteNutzdaten);

    // Daten verarbeiten...
    MachWasMitDaten(Info);

    LaengeNaechsteNutzdaten:= 0;
  end;
end;
Weil das TInfo ja immer gleich groß ist, kann man sich den Integer theoretisch auch sparen, aber so müsste es aussehen denke ich...

Sir Rufo 30. Mär 2011 11:35

AW: Ist der Server zu "schnell"?
 
Allerdings sollte man fairerweise dazu bemerken, dass dieses nur bei einem Client sauber funktioniert.

Somit würde man pro Socket eine solche Variable (LaengeNaechsteNutzdaten) benötigen


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:32 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz