AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Delphi TServerThread.ClientExecute; Buffer Problem
Thema durchsuchen
Ansicht
Themen-Optionen

TServerThread.ClientExecute; Buffer Problem

Ein Thema von Gimp · begonnen am 4. Okt 2011 · letzter Beitrag vom 5. Okt 2011
Antwort Antwort
Gimp

Registriert seit: 28. Dez 2007
7 Beiträge
 
Delphi 2006 Personal
 
#1

TServerThread.ClientExecute; Buffer Problem

  Alt 4. Okt 2011, 23:15
Hallo zusammen!

Ich beschäftige mich momentan mit den Komponenten TServerSocket und TClientSocket. Damit alle Daten alle zuverlässig ankommen, erstellt der Server für jeden Client Socket einen neuen Thread. Im Internet habe ich dazu eine nette Lösung gefunden:
Delphi-Quellcode:
procedure TServerThread.ClientExecute;
var
  ac, readlen : integer;
  fRequest: Array[0..1023] of Byte;
begin
  inherited FreeOnTerminate := TRUE;
  try
    fSocketStream := TWinSocketStream.Create(ClientSocket, 100000);
    // 100000 is the timeout in miliseconds.
    try
      while ( not Terminated ) and ( ClientSocket.Connected ) do
      try
        if fSocketStream.WaitForData(10000) then
        begin
          FillChar(fRequest, 1024, 0);
          ac := 0;
          repeat
            readlen := fSocketStream.Read(fRequest[ac], 1024);
            inc(ac, readlen);
            ShowMessage(IntToStr(readlen));
          until (readlen = 0) or (ac = 1024);
        end;
      except
        on e:exception do
        begin
          // An error has occurred, close and exit
          ClientSocket.Close;
          Terminate;
        end;
      end;
    finally
      fSocketStream.Free;
    end;
  except
    on e:exception do
    begin
      // An error has occurred, close and exit
      ClientSocket.Close;
      Terminate;
    end;
  end;
end;
Der Code an sich funktioniert soweit ganz gut. Wenn ich das erste Datenpaket von 2 Bytes sende, erhalte ich mittels meiner ShowMessage auch eine Datengröße von 2. Sende ich während die Meldung geöffnet ist weitere Daten, z.B. 10x 2 Byte große Datenpakete, wird in der nächsten Meldung 20 angezeigt. Die Pakete werden also alle in einen Buffer gequetscht, statt die Funktion 10x aufzurufen. Gibt es da einen komfortablen Weg um die Pakete wieder zu splitten? Die Größe der Daten ist nicht immer gleich, die Daten in 2 Byte große Häppchen zu zerschneiden ist also keine Lösung.

Liebe Grüße

Geändert von Gimp ( 5. Okt 2011 um 00:48 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: TServerThread.ClientExecute; Buffer Problem

  Alt 5. Okt 2011, 01:30
Wie soll der jeweilige Empfänger denn wissen, wie lang die Datenpakete sind - hellsehen kann der auch nicht.

Du musst also noch ein Protokoll drübersetzen.

minimal so was
Code:
[Länge][.....Daten.....]
besser wäre noch das hier
Code:
[Länge][.....Daten.....][Prüfsumme]
Hat der Server die Daten empfangen, schickt der dem Client eine Rückmeldung ob die Übertragung erfolgreich war.
Wenn nicht, muss der Client die Daten erneut senden.

Damit der Client weiß, was nicht korrekt angekommen ist, sollte im Protokoll evtl. auch eine Paket-ID enthalten sein.
Und damit man die Daten-Sendungen von den Bestätigungsmeldungen unterscheiden kann, auch einen Nachrichten-Typ
Code:
Client -> Server [Länge][..C-ID..][Typ][.....Daten.....][Prüfsumme]
Server -> Client [Länge][..S-ID..][Typ][..C-ID..]OK[Prüfsumme]
Weiterhin ist es nicht ratsam innerhalb eines Threads eine MessageBox mal eben so zu öffnen.
Dafür sollte man ein entsprechendes Logging implementieren.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Gimp

Registriert seit: 28. Dez 2007
7 Beiträge
 
Delphi 2006 Personal
 
#3

AW: TServerThread.ClientExecute; Buffer Problem

  Alt 5. Okt 2011, 01:40
Ja danke, die Idee mit dem Protokoll ist perfekt. Ich setze vor jedem Paket nun einen kleinen Header, welcher die id sowie die restliche Größe des Pakets enthält. Die Lese-Position kann so einfach inkrementiert werden und anschließend jedes Paket einzeln verarbeitet werden.

Wie ich in das Erinnerung habe, soll man für jede Frage einen neuen Thread erstellen, ich denke aber das passt hier noch ganz gut rein...

Wie sieht es mit dem ClientSocket aus, wenn der Server mehrere Pakete auf einen Haufen sendet, gibt es da noch eine Möglichkeit, damit die Daten nicht verloren gehen? Ok, bisher habe ich es nicht getestet ob es überhaupt Probleme gibt, aber zumindest beim Server gab es sie ja.

Grüße
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#4

AW: TServerThread.ClientExecute; Buffer Problem

  Alt 5. Okt 2011, 02:34
Der Client sollte genau wie der Server eine Bestätigungsmeldung senden.
Insofern verhalten sich beide gleich.

Hier mal die Kommunikation für ein Datenpaket vom Client zum Server
Code:
Client -> Server [Länge][..C-ID..][Typ][.....Daten.....][Prüfsumme]
Client <- Server [Länge][..S-ID..][Typ][..C-ID..]OK[Prüfsumme]
Client -> Server [Länge][..C-ID..][Typ][..S-ID..]KO[Prüfsumme]
Schickt der Server ein Datenpaket zum Client, dann sieht die Kommunikation genau gleich aus, nur mit vertauschten Rollen.

Code:
Server -> Client [Länge][..S-ID..][Typ][.....Daten.....][Prüfsumme]
Server <- Client [Länge][..C-ID..][Typ][..S-ID..]OK[Prüfsumme]
Server -> Client [Länge][..S-ID..][Typ][..C-ID..]KO[Prüfsumme]
Die Meldung mit dem KO sollte natürlich nicht mehr beantwortet werden sonst melden die sich bis zum Terminate tot
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo ( 5. Okt 2011 um 02:37 Uhr)
  Mit Zitat antworten Zitat
Gimp

Registriert seit: 28. Dez 2007
7 Beiträge
 
Delphi 2006 Personal
 
#5

AW: TServerThread.ClientExecute; Buffer Problem

  Alt 5. Okt 2011, 02:51
Mhm, unbedingt auf jedes Paket antworten zu müssen finde ich nicht gerade optimal. Sende ich im Clienten in einer while Schleife ohne Sleep() Pakete, kommen falsche Daten an. Ich denke, der ClientSocket schafft es nicht, die ganzen Daten so schnell zu senden, der Server Part sollte denke ich für einen kleinen Test und weck vom copy pasta Code aus dem Internet soweit in Ordnung sein. Zumindest wüsste ich nicht, wo sich hier noch ein Fehler verstecken könnte. Der Client sendet die Daten sehr simpel über Socket.SendBuf(...), gibt es da ab einer gewissen Geschwindigkeit Probleme?

edit: Die Pakete sind nie größer als 100~ Bytes, ein size als Byte reichte mir also im Paket Header

Delphi-Quellcode:
procedure TServerThread.ClientExecute;
type
  PClientPacket = ^TClientPacket;
  TClientPacket = packed record
    ID : Byte;
    Size : Byte;
  end;
var
  readlen, readpos: Integer;
  fRequest: Array[0..1023] of Byte;
  clientPacket: TClientPacket;
const
  timeout = 30 * 1000;

  CPKT_TEST = 1; // Client-Paket 1
  CPKT_TEST2 = 2; // Client-Paket 2
begin
  inherited FreeOnTerminate := true;

  fSocketStream := TWinSocketStream.Create(ClientSocket, timeout);
  try
    while (not Terminated) and (ClientSocket.Connected) do
    begin
      try
        if (fSocketStream.WaitForData(timeout)) then
        begin
          readpos := 0;
          FillChar(fRequest, 1024, 0);
          readlen := fSocketStream.Read(fRequest, 1024);

          while (readlen - readpos > 0) do
          begin
            if (readlen - readpos < SizeOf(TClientPacket)) then
            begin
              // kein header
              break;
            end;

            clientPacket := PClientPacket(@fRequest[readpos]))^;
            {$IFDEF DEBUG}
            MessageBox(0, PChar('packet code: ' + IntToStr(clientPacket.ID) + ' size: ' + IntToStr(clientpacket.Size)), nil, 0);
            {$ENDIF}

            case clientPacket.ID of
              CPKT_TEST: ;
              CPKT_TEST2: ;
            else
              MessageBox(0, PChar('Ungültigen Paket Code (' + IntToStr(clientPacket.ID) + ') empfangen!'), nil, 0);
            end;

            inc(readPos, clientPacket.Size + SizeOf(TClientPacket));
          end;
        end;
      except
        on E:Exception do
        begin
          ClientSocket.Close();
          Terminate();
        end;
      end;
    end;
  finally
    fSocketStream.Free();
  end;
end;
Nach einigen Paketen wird bereits die MessageBox aufgerufen, welche mir mitteilt das ein ungültiges Paket übertragen wurde. Setze ich im Clienten ein kleines Sleep passiert dies nicht.

Der Client, welcher stupide 2 unterschiedliche Pakete in einer while Schleife sendet:

Delphi-Quellcode:
var
  // paket code (1, 2), restliche grö0e, daten
  buf1: Array[0..9] of Byte = (1, 8, 1, 2, 3, 4, 5, 6, 7, 8);
  buf2: Array[0..7] of Byte = (2, 6, 1, 2, 3, 4, 5, 6);

procedure TForm1.Button2Click(Sender: TObject);
begin
  while true do
  begin
    ClientSocket1.Socket.SendBuf(buf1[0], Length(buf1));
    ClientSocket1.Socket.SendBuf(buf2[0], Length(buf2));
  end;
end;
edit: Nach dem ich nun die Buffergröße etwas angehoben habe, klappt es wie gewünscht Ok... mein PC friert fast ein bei der Masse an Kommunikation, aber scheint soweit sehr stabil. Bin gespannt ob der Client die Daten auch so einfach fressen wird, welche vom Server kommen. Danke nochmal Sir Rufo

edit 2: Den sendbuf Fehler habe ich auch eingesehen, ich hoffe die folgende Funktion ist so korrekt, zumindest gab es mit mehreren Clients und einer while Schleife keine Übertragungsfehler. Ich werde mich jetzt mal dransetzen und daraus eine sorgfältigere Umsetzung basteln

Delphi-Quellcode:
procedure SendPacket(pData: Pointer; iLength: Integer);
var
  nSentBytes: Integer;
  buffer: Array of Byte;
begin
  nSentBytes := 0;

  SetLength(buffer, iLength);
  Move(pData^, buffer[0], iLength);

  while (nSentBytes < nLength) do
  begin
    nSentBytes := Form1.ClientSocket1.Socket.SendBuf(buffer[nSentBytes], iLength - nSentBytes);

    if (nSentBytes = SOCKET_ERROR) then
      break;
  end;
end;

Geändert von Gimp ( 5. Okt 2011 um 06:26 Uhr) Grund: Leicht überarbeiteter Code, Client Part hinzugefügt
  Mit Zitat antworten Zitat
FredlFesl

Registriert seit: 19. Apr 2011
293 Beiträge
 
Delphi 2009 Enterprise
 
#6

AW: TServerThread.ClientExecute; Buffer Problem

  Alt 5. Okt 2011, 08:04
Ich habe bessere Erfahrungen mit einem anderen Protokoll gemacht. Bei dem von Sir Rufo vorgeschlagenen hat man Probleme, wenn die Länge nicht korrekt übertragen wird oder es kurzzeitige Aussetzer gibt. Denn entgegen der Theorie bietet TCP alles andere als 'QoS'.

Der Empfänger muss neben dem Ende eines logischen Paketes auch den Anfang sicher erkennen können.

Ich verwende Frames, also
[Start-Bytes] [Daten] [Prüfsumme][Ende-Bytes]

1. Der Empfänger verwirft alles, bis er die Start-Bytes-Sequenz empfängt. Sodann liest er alles bis zur Ende-Bytes-Sequenz.
2. Sollt die Start- bzw. Ende-Byte-Sequenz übertragen werden, so wird anstelle dieser Sequenz (sie würde ja den Empfänger durcheinanderbringen) einfach etwas anderes übertragen (Stichwort: Escapen). Das "Andere" ist dem Empfänger bekannt und wenn er das empfängt, übersetzt er das wieder zurück.

Wir arbeiten in Fabriken, bei denen das Netzwerk sehr instabil ist und daher haben wir so ziemlich alles erlebt, was übertragungstechnisch passieren kann. Hier wird sogar noch das Datenpaket über einen Längenpräfix abgesichert.. Es wäre denkbar, das die Endesequenz verschluckt wird und dann würde o.g. Verfahren endlos weiterlesen.

Als letzten Notnagel haben wir eine maximale Paketlänge von 1MB vereinbart.
Das Bild hängt schief.
  Mit Zitat antworten Zitat
Gimp

Registriert seit: 28. Dez 2007
7 Beiträge
 
Delphi 2006 Personal
 
#7

AW: TServerThread.ClientExecute; Buffer Problem

  Alt 5. Okt 2011, 19:00
Naja bei mir gab es bisher keine Probleme mehr. Ich habe über 5 offene Clients zusammen rund 5 Millionen Pakete an den Server gesendet, alle sind sie heile angekommen. So lange die Größe des Paketes übermittelt und anschließend überprüft wird ob auch noch genügend Bytes im Buffer verfügbar sind, sollte es ja keine Probleme geben.

Was mich nur nicht so ganz erschließt, ist wie man den Clienten nun im Thread laufen lässt. Ich habe einfach einen eigenen Thread mittels BeginThread() erstellt, um dann ähnlich wie beim Server auf Pakete zu warten. TServerClientThread funktioniert wie ich das mitbekommen habe nur beim Server, zumindest bietet das ClientSocket keine Option um automatisch einen Thread zu starten. Die Beispiele im Internet sind da auch sehr rar oder veraltet..
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:39 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