Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Zuverlässiger UDP Client gesucht (https://www.delphipraxis.net/149642-zuverlaessiger-udp-client-gesucht.html)

Poolspieler 25. Mär 2010 20:56


Zuverlässiger UDP Client gesucht
 
Hallo,
ich sende mit der Indy UDP Client Komponente einen Befehl zu einem UDP-Server.
Dann kommen viele UDP-Pakete zurück, die ich alle empfangen muss.

Delphi-Quellcode:
// UDP Befehl senden
UDPClient.SendBuffer(udp_ziel_ip, udp_ziel_port, _daten);
// UDP Echo empfangen
while UDPClient.ReceiveBuffer(UDPClient_empfangspuffer_tmp, 1000) > 0 do begin
  // UDPClient_empfangspuffer_tmp verarbeiten
  // ...
  // empfangene Daten auf einem Memo (dekodiert) ausgeben
  // ...
  application.processmessages;
end;
Leider kommt es bei obigem Codeschnipsel öfter vor, dass nicht alle Pakete empfangen werden (mit Wireshark habe ich es überprüft --> Die Pakete kommen definitiv auf dem richtigen Port an - werden aber nicht von TidUDPClient empfangen...)

Hat jemand eine Idee woran es liegen könnte? Oder kennt jemand eine bessere Komponente? Gerne auch Komeriell - wenn sie gut ist :-D

Gruß,

Poolspieler

arbu man 25. Mär 2010 21:01

Re: Zuverlässiger UDP Client gesucht
 
Das liegt am UDP Protokoll. Wenn du das nicht willst nimm TCP!

Poolspieler 25. Mär 2010 21:11

Re: Zuverlässiger UDP Client gesucht
 
Hallo,
danke für Deine Antwort.
Leider kann ich aus Performancegründen kein TCP nehmen - wäre mir auch lieber!

Aber ganz Recht geben kann ich Dir nicht, dass es in diesem Fall am UDP Protokoll liegt:
Denn das Paket kommt doch am PC an (mit Wireshark getestet) --> es wird nur vom Client nicht empfangen.
Für mich sieht es so aus, dass die Funktion "UDPClient.ReceiveBuffer" bereits aufgerufen sein muss BEVOR das Paket ankommt.
Wenn nun die Verarbeitung (oder das application.processmessages) etwas länger dauert, dann ist dies evtl. nicht gegeben - und das Paket geht verloren. Oder sehe ich da was falsch?

Ich habe auch schon das ganze mit einem TidUDPServer versucht. Leider kann ich dann nicht in der selben procedure (wie im Beispielcode) Daten senden UND empfangen. Die Daten werden erst empfangen, wenn die procedure mit dem SendBuffer beendet wurde. Das liegt wohl an dem recht seltsamen Messages-Konzept von den Indykomponenten. Etwas in der Richtung habe ich schon gelesen. Eine Lösung habe ich leider noch nicht gefunden :cry:

Mit einem TidUDPServer NUR empfangen, geht wunderbar - bringt mir aber nichts...

Gruß,

Poolspieler

Klaus01 25. Mär 2010 21:16

Re: Zuverlässiger UDP Client gesucht
 
Guten Abend,

vielleicht kannst Du die Methode zum Empfangen ausgliedern
und in einen Thread einbauen. Dieser kann dann in einer (endlos)Schleife
schauen ob UDP Packete im Empfangspuffer angekommen sind.

In etwas so:

Code:
while not terminated do
  begin
    schaue in Empfangspuffer
    Wenn Daten da sind verarbeite oder verschiebe diese
  end;

Grüße
Klaus

Poolspieler 25. Mär 2010 21:22

Re: Zuverlässiger UDP Client gesucht
 
Guten Abend Klaus,
danke für Deine Antwort.
Hier ist aber die Frage, in wie weit TidUDPClient Threadsicher ist. Und welcher Teil mit synchronize() synchronisiert werden muss.
Weißt Du es - oder sonst jemand?

Gruß,

Poolspieler

Klaus01 25. Mär 2010 22:31

Re: Zuverlässiger UDP Client gesucht
 
Hallo Poolspieler,

da der TidUdpServer kein Ereignis, welches anzeigt das Daten anliegen hat.
Bleibt da nicht viel anders übrig als zu pollen.

Um sicher vor Konflikten zu sein ist es wohl angebracht.
Einen udpClient für das Senden und einen zum Empfangen der Datagramme zu haben.

Den udpClient für das Empfangen der Datagramme würde
ich in dem Thread erstellen. Die empfangenen Daten würde
ich nicht im in diesem Thread bearbeiten sonder zu einem gesonderten
Verarbeitungsthread bzw. zum Haupthread durchreichen.

Grüße
Klaus

Poolspieler 25. Mär 2010 23:32

Re: Zuverlässiger UDP Client gesucht
 
Hallo Klaus,
vielen Dank für Deine Anregungen!
Ich werde es morgen mal mit einem Thread probieren.
Was ist Deine Meinung zu TUDPSocket?
Ist diese Komponente eventuell den Indy-Komponenten vorzuziehen?

Gruß,

Poolspieler

DataCool 26. Mär 2010 09:20

Re: Zuverlässiger UDP Client gesucht
 
@Poolspieler:

Ich weiß nicht wo Dein Problem beim IdUDPServer liegt,
Du kannst im OnUDP ankommende Nachrichten empfangen und in dieser Procedure
auch gleich wieder antworten.
Hier ein Bsp. Code, aber Achtung der ist noch von Indy9:

Delphi-Quellcode:
procedure TfrmUdpXYZ.IdUDPSvrUDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
Var   DataStringStream: TStringStream;      
      sRequest : String;
      sReply  : String;
begin
   DataStringStream := TStringStream.Create('');
   try
      DataStringStream.CopyFrom(AData, AData.Size);
      sRequest := DataStringStream.DataString;
   finally
      FreeAndNil(DataStringStream);
   end;
        // Request auswerten und Antwort schicken
   sReply := 'MyRESPONSE';
   ABinding.SendTo(ABinding.PeerIP, ABinding.PeerPort, sReply[1], Length(sReply));
end;
Das sollte relativ einfach nach Indy10 zu portieren sein.(WriteTIdBytesToStream)

Einen entsprechenden Client würde ich gründsätzlich, egal ob UDP oder TCP in einen Thread auslagern, der sich ums SEnden und empfangen kümmert.

Greetz Data

sirius 26. Mär 2010 09:52

Re: Zuverlässiger UDP Client gesucht
 
TUDPSocket hat das gleiche "Problem" wie die Indy-Komponente (version 9). Es gibt kein Ereignis, wenn Daten empfangen werden können.
Du müsstest diese Komponente also auch in den Thread legen um dort zu warten. Beide dürften aber threadsicher sein, ist also kein Problem.

Allerdings glaube ich nicht, dass dein Fehler in der Komponente liegt. Hast du bedacht, dass du auch mehrere PAkete gleichzeitig empfangen kannst, sodass sie zugleich im Buffer landen? Meines Wissens musst du sie dann auch in einem Ritt auslesen. Also wähle mal deine Empfangsbuffer so groß, wie der Buffer des Sockets (i.A. 8kB = 8192Bytes). Und dann musst du die Nachrichten auseinanderbasteln. Evtl. kommt ein Stück der Nachricht auch bei unterschiedlichen Aufrufen.

Wenn du das bereits alles bedacht hast, kannst du ja noch die Komponente von mir ausprobieren:
http://www.delphipraxis.net/internal...t.php?t=138107
Die hat ein Ereignis onReceiving. Dies wird ausgelöst, wenn eine neue Nachricht angekommen ist (bei SocketMode=smEvent).
Aber dein Problem liegt denke ich nicht an der verwendeten Komponente.

Poolspieler 27. Mär 2010 11:20

Re: Zuverlässiger UDP Client gesucht
 
Hallo nochmal,
folgenden Threadansatz habe ich ausprobiert:
Delphi-Quellcode:
while not terminated do
  begin
    schaue in Empfangspuffer
    Wenn Daten da sind verarbeite oder verschiebe diese
  end;
Ergebnis: Auch hier gehen (in Abhängigkeit von der Systemlast) Daten "verloren". Allerdings habe ich für das Senden und für das Empfangen des selben UDPClient benutzt.

Die Komponente von sirius habe ich noch nicht ausprobiert (aber schon mal herunter geladen...). Falls ich so etwas einsetze, möchte ich die auch verstehen - was einige Zeit kosten wird...

Ein weiterer Ansatz:
Das Empfangen von Nachrichten mit einem TidUDPServer funktioniert bei mir sehr gut.
Frage: Ist es möglich, einem TidUDPClient mitzuteilen, dass er mit einem bestimmten Sourceport (nämlich der, auf dem der Server "lauscht") senden soll?
Ich habe es persönlich nicht hinbekommen. Es kommt eine Fehlermeldung, dass der Port belegt sei --> ist er ja auch vom TidUDPServer...

Nur nebenbei: wenn ich direkt vom TidUDPServer aus sende, dann wird die Antwort (egal ob Threaded Event an oder aus) erst empfangen, wenn die ausführende procedure beendet ist und das Programm wieder im Hauptthread läuft. Das liegt wohl an dem "etwas seltsamen" Message Handling der Indy-Komponenten.

Über weitere Ideen würde ich mich freuen...

Beste Grüße,

Poolspieler

Rudirabbit 27. Mär 2010 16:48

Re: Zuverlässiger UDP Client gesucht
 
Hallo Poolspieler, habe vor einiger Zeit auch mal sowas gebraucht.

Bei mir funktioniert dies super:

Delphi-Quellcode:
 
type TMessageReceived = procedure(Sender: TObject; Msg: String) of object;

 TClientThread = class(TThread)
  protected
    FClient: TIdUDPClient;        //Client socket
    FOnReceive: TMessageReceived; //Wird bei Empfang aufgerufen
    FTimeout: Integer;            //Verbindung-Timeout
    procedure Execute; override;
  public
    constructor Create(AHost: String; APort: Word; ATimeout: Integer = -1; AOnReceive: TMessageReceived = nil; ASuspended: Boolean = False);

    property OnReceive: TMessageReceived read FOnReceive write FOnReceive;
    property ClientSocket: TIdUDPClient read FClient;
    property Timeout: Integer read FTimeout write FTimeout;
  end;



constructor TClientThread.Create(AHost: String; APort: Word; ATimeout: Integer = -1; AOnReceive: TMessageReceived = nil; ASuspended: Boolean = False);
begin
  inherited Create(True);
  FClient := TIdUDPClient.Create(nil);

  FTimeout := ATimeout;
  FOnReceive := AOnReceive;
  FClient.Host := AHost;
  FClient.Port := APort;

  if not ASuspended then
    Resume;
end;

procedure TClientThread.Execute;
var LRead: String;
    LCS: TCriticalSection;
begin
   FClient.Active:=true;
   FClient.ReceiveTimeout:=FTimeout;
  LCS := TCriticalSection.Create;

  while not (Terminated) and (FClient.Active)  do //Bis Thread or Verbindung beendet
  begin

    LRead := FClient.ReceiveString;
     If Lread='' then sleep(1);    // wegen Cpu Last
   LCS.Acquire;

    if Assigned(FOnReceive) then
      FOnReceive(Self, LRead);
   LCS.Release;

  end;

 LCS.Acquire;

  if FClient.Active then
    FClient.Active:=false;

  FClient.Free;

  LCS.Release;
  LCS.Free;
end;
Diese TClientThread Class erzeugst du im Hauptformular, und hängst beim "createn" die Empfangsprocedure ein.
Delphi-Quellcode:
dein_thread:=TClientThread.Create(ip,port,0,receive,true);
.....
procedure TForm1.receive(Sender: TObject; Msg: String);
begin
.....
end;
Dort kannst du deine Daten zuverlässig, abholen.
Besser wäre es wenn du nicht mit Strings arbeitest (so wie ich in diesem Beispiel), sondern mit einem Bytearray.

mfg

thkerkmann 27. Mär 2010 19:04

Re: Zuverlässiger UDP Client gesucht
 
@rudirabbit:
Verräts du mir, wozu die CriticalSection gut ist ? Die macht überhaupt keine Sinn, und kann meiner Meinung nach total entfallen.

Gruss

Rudirabbit 27. Mär 2010 20:16

Re: Zuverlässiger UDP Client gesucht
 
@thkerkmann: Ich hatte damals den Code im Web gesehen (aber Verstanden ! ) und für meine Zwecke angepasst.
Die CriticalSection habe ich einfach so übernommen .. :oops:

Nachtrag:

Zitat von Poolspieler:
Zitat:

Das Empfangen von Nachrichten mit einem TidUDPServer funktioniert bei mir sehr gut.
Frage: Ist es möglich, einem TidUDPClient mitzuteilen, dass er mit einem bestimmten Sourceport (nämlich der, auf dem der Server "lauscht") senden soll?
Ich habe es persönlich nicht hinbekommen. Es kommt eine Fehlermeldung, dass der Port belegt sei --> ist er ja auch vom TidUDPServer...
Ich hatte auch so ein Problem:
Wenn ich mich als Client zum Server so Connecte;
Delphi-Quellcode:
...TClientThread.Create(ServerIP,1000,-1,receive,true);
In diesem Beispiel also via Port 1000, auf der Serverseite ermittle ich den Port zum Zurückschicken der Daten so:
Delphi-Quellcode:
IdUDPServer1.Binding.PeerPort
Dieser ist in diesen Beispiel nicht der Port 1000 sondern ein anderer (1740).

So funktioniert dies zwar, ich habe leider zu wenig Wissen warum :gruebel:
Also mein Server lauscht auf Port 1000 und sendet die Daten via 1740 Port zum Client.
Gibt es eine Regel für diese Portverteilung ?
Also wenn Eingangsport 1000 ist - dann ist Ausgangsport immer 1740 ?

Poolspieler 30. Mär 2010 21:46

Re: Zuverlässiger UDP Client gesucht
 
Hallo nochmal,
es hat ein wenig gedauert, bis ich alle (bis jetzt bekannten) Ideen ausprobiert habe... :?

@Rudirabbit:
Ich habe Dein Beispiel getestet mit folgendem Ergebnis:
- ich habe in der Empfangsroutine alle empfangenen Pakete in eine Datei (im lesbaren Hexformat) mitgeschrieben.
- dabei kam heraus, dass von drei zu empfangenen Paketen etwa ZWEI VERWORFEN (bzw. nich empfangen) werden :pale:
- Die Datenpakete, um die es sich handelt, haben eine Größe von netto 8 Bytes
- Es wird alle 10 Millisekunden so ein Paket vom anderen Host zum Delphiprogramm gesendet

zu Deiner Frage der "Portverteilung":
Bei der TidUDPServer-Komponente kannst Du über die Eigenschaft "Defaulport" einstellen, auf welchem Port er "lauschen" soll (listen on).
Standardmäßig ist hier 0 eingestellt --> heißt dynamische Zuweisung. Es wird bei "active:=true" ein gerade freier Port zugewiesen.
Zielport und Sourceport sind nicht das selbe:
Zielport --> ist der Port, zu dem die Daten gesendet werden. Der Empfänger muss auf diesem Port "lauschen"
Sourceport --> ist der Port, auf dem der Sender der Nachricht eine eventuelle Antwort erwartet (Dein Peerport).
Ich hoffe, ich habe Deine Frage richtig verstanden - und beantwortet...

:?: Hat irgend jemand noch eine Idee für mich (wie gesagt, gerne auch Kommerziell!!!)?
Es kann doch nicht so schwer sein, über einen UDP-Client Daten zu senden und in der selben Routine auf die Antwort zu warten - oder???

Beste Grüße von einem frustrierten

Poolspieler

taveuni 31. Mär 2010 07:45

Re: Zuverlässiger UDP Client gesucht
 
Zitat:

Zitat von Poolspieler
Hallo,
Leider kann ich aus Performancegründen kein TCP nehmen - wäre mir auch lieber!
Poolspieler

Wo siehst Du diesbezüglich Probleme?

Wir verwenden die Sockets von Piette ICS.
Allerdings habe ich noch nie was mit UDP gemacht.
Kollegen von mir setzen aber den ICS Udp Socket seit Jahren in kommerziellen Applikationen ein.
Da sind auch Beispiele dabei.
http://www.overbyte.be/
Dann Products/ICS
Um die ganzen genialen Komponenten zu nutzen verlangt der Author nur eine Postkarte von Dir.
Auch für die kommerzielle Nutzung.
Die Sourcen sind ebenfalls mit dabei.

Poolspieler 31. Mär 2010 10:31

Re: Zuverlässiger UDP Client gesucht
 
Hallo taveuni,
die Performanceprobleme liegen beim angesprochenen Host --> das ist ein Embedded System... Bei den vielen Handshakes, die für TCP nötig wären, würde ich das geforderte Timing nicht mehr einhalten.

Wegen den ICS-Komponenten: Die werde ich gleich mal ausprobieren. Danke!
Gestern bin ich noch auf diese Seite gestossen: www.activexperts.com Eine Standard License würde 165€ kosten. Das wäre bezahlbar.
Ich werde testen und Euch meine Erkenntnisse mitteilen.

Beste Grüße,

Poolspieler

Rudirabbit 31. Mär 2010 19:32

Re: Zuverlässiger UDP Client gesucht
 
@Poolspieler:
Zitat:

- dabei kam heraus, dass von drei zu empfangenen Paketen etwa ZWEI VERWORFEN (bzw. nich empfangen) werden Pale
- Die Datenpakete, um die es sich handelt, haben eine Größe von netto 8 Bytes
- Es wird alle 10 Millisekunden so ein Paket vom anderen Host zum Delphiprogramm gesendet
Dies kann doch nur passieren wenn die Receive Procedure noch nicht beendet ist, und neue Daten eintreffen :gruebel:

Zitat:

Es kann doch nicht so schwer sein, über einen UDP-Client Daten zu senden und in der selben Routine auf die Antwort zu warten - oder???
Das wäre dann z.b eine repeat until schleife, also ein polling. Dies hast du ja schon getestet.

Zitat:

Ich werde testen und Euch meine Erkenntnisse mitteilen.
Ich wäre schon sehr interessiert, ob dieses Timeing mit anderen Komponenten zu schaffen ist.

mfg Rudi

Btw: Was für ein Embedded System hast du da - Atmel mit ENC28J60 ?
Bin nur neugierig - Arbeite mit den ATMega's und hatte auch schon mal vor diese mit dem ENC ins Lan zu bringen.

Poolspieler 4. Apr 2010 12:34

Re: Zuverlässiger UDP Client gesucht
 
Hallo Rudirabbit,
von dem ENC28J60 würde ich erstmal die Finger weg lassen.
Dafür brauchst Du einen Software TCP/IP Stack. Sowas habe ich auch schon mal gemacht. Der Datendurchsatz war damals etwas mau...
Je nach Anwendung (z.B. einfaches Webinterface zur Konfiguration, etc...) kann es natürlich ausreichen.
Für einen Datenstream mit relativ hohem Datendurchsatz ist sowas mit Vorsicht zu genießen (aber natürlich möglich...).
Schau Dir mal das an: Ethernet Modul
Damit ist es eine recht überschaubare Sache, einen µC ins Ethernet zu bringen...

Gruß,

Poolspieler

sirius 7. Apr 2010 11:15

Re: Zuverlässiger UDP Client gesucht
 
Liste der Anhänge anzeigen (Anzahl: 1)
Wie sieht es aus? Schon eine Lösung gefunden?

Im Anhang ist mal eine an das Problem angepasste Unit.

Kann man wie folgt einsetzen:
Delphi-Quellcode:
FUDPSocketThread:=TUDPSocketThread.Create(True);
FUDPSocketThread.IP:='127.0.0.1';
FUDPsocketThread.Port:=21000;
FUDPSocketThread.SendData:='Hello World';

//je nach Ereignis, welches man mitbekommen will
FUDPSocketThread.OnSocketMessage:=SocketMessage;
FUDPSocketThread.OnUDPMessage:=UDPMessage; //evtl. rauslassen, weil es zu viel Zeit beansprucht
FUDPSocketThread.EventHandle:=Handle;
FUDPSocketThread.Resume;
Und die Ereignisbehandlungen:
Delphi-Quellcode:
//für EventHandle
procedure TForm1.CMUDP(var Msg: TMessage); //message CM_UDP
var i:Integer;
begin
  memo1.lines.add('UDP-Event');
  memo1.lines.add('------------------------------------------');
  for i:=0 to FUDPSocketThread.ReceivedMessages.Count-1 do
  begin
    memo1.lines.Add(FUDPSocketThread.ReceivedMessages[0]);
    FUDPSocketThread.ReceivedMessages.Delete(0);
  end;
  memo1.lines.add('------------------------------------------');
end;

procedure TForm1.SocketMessage(Sender: TObject; aMessage: string);
begin
  Memo1.Lines.Add('Socket: '+aMessage);
end;

procedure TForm1.UDPMessage(Sender: TObject; aMessage: string);
begin
  Memo1.lines.add('UDP: '+aMessage);
end;
Thread Beenden natürlich nicht vergessen.

Rudirabbit 7. Apr 2010 19:58

Re: Zuverlässiger UDP Client gesucht
 
@Poolspieler:
Zitat:

.. von dem ENC28J60 würde ich erstmal die Finger weg lassen.
Dafür brauchst Du einen Software TCP/IP Stack...
Mein AVR Compiler hat die Lib's für den ENC drin, deshalb hätte ich mit dem ENC Arbeiten wollen.
Die Anbindung via SPI wäre ja auch praktisch, wobei dein Link (WIZ830MJ Modul) mich auch sehr interessiert.
Hätte einige Fragen dazu, will aber deinen Thread hier nicht zumüllen - wenn du nichts dagegen hast werde ich dich disbezüglich via Pn nerven :wink:

@sirius: Dein Code arbeitet direkt mit der WinSock, wenn du diesen Code so mal eben "aus dem Ärmel geschüttelt hast" - dann :thumb:

Damit müsste das Timeing von Poolspieler funktionieren, also keine Pakete verloren gehen.

mfg. Rudi

sirius 9. Apr 2010 14:34

Re: Zuverlässiger UDP Client gesucht
 
Zitat:

Zitat von Rudirabbit
@sirius: Dein Code arbeitet direkt mit der WinSock, wenn du diesen Code so mal eben "aus dem Ärmel geschüttelt hast" - dann :thumb:

Merci, aber es sind ja nur 3 Befehle die man dazu kennen muss :oops:

Zitat:

Damit müsste das Timeing von Poolspieler funktionieren, also keine Pakete verloren gehen.
mfg. Rudi
Oder zumindest eine saubere Fehlermeldung kommen (die in diversen Komponenten hin und wieder als unwichtig unterdrückt wird)

Rudirabbit 10. Apr 2010 19:27

Re: Zuverlässiger UDP Client gesucht
 
Zitat von sirius:
Zitat:

Merci, aber es sind ja nur 3 Befehle die man dazu kennen muss
Du reduzierst dies (deinen Code) evtl. zu stark auf die Benutzung der DLL Funktionen.

Wenn es sooo simpel wäre, würde keiner z.b die Jedi's benutzen.

Mal abwarten was Poolspieler zu berichten hat....

ConnorMcLeod 16. Feb 2016 14:00

AW: Re: Zuverlässiger UDP Client gesucht
 
Zitat:

Zitat von sirius (Beitrag 1012968)
Im Anhang ist mal eine an das Problem angepasste Unit.

Hallo Sirius und danke für das Beipiel. Ich habe es mir als Starthilfe genommen, um mal einen Anfang zu haben ;-)
Für mich funktioniert es besser, wenn die Zeile 107 so aussieht:
Delphi-Quellcode:
for i:=0 to length(NonRaise)-1 do
fg cml


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:16 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