UDP Broadcast nejen pro Android za pomocí System.Net.Socket

vložil Radek Červinka 11. května 2023 20:48

Vždycky jsem UDP psal pomocí Synapse, ale chtěl jsem zkusit něco nového a chtěl jsem to pro Android za pomoci inhouse řešení - i když je Synapse pro Android je dostupná.

Server byl existující synapse UDP server (tady je sample public:howto:udpserver) pro windows, který na zaslaný řetezec <ident> vrátil název počítače, takže jsem během chvilky dostal seznam dostupných stanic i s IP adresou pro připojení z Androidího klienta.

Upozorním na takové dva body, které jsou oříšky v implementaci System.Net.Socket a UDP TSocket, což je platformově nezávislá implementace BSD socketů (UDP, TCP …). Moc jsem o ní ještě nepsal, tak to dnes napravíme.

Celý kod je obsluha tlačítka, které slouží jako Start - Stop. Tlačítko spouští anonymní vlákno kde je jádro kódu. S anonymními vlákny ale pozor na jeden problém - Delphi je sami neukončí při konci programu, protože o nich prostě nemají přehled. Je to celkem známá věc (netýká se to jen anonymních vláken, alesnad vláken obecně) - je nutno před ukončením aplikace nastavit něco na co vlákna reagují a ukončí se, nebo je jinak ukončit a nebo být připraven, že vlákno skončí později, když už nebude např. hlavní vlákno (ba i hůře). Jsou to blbé chyby typu AV, osobně jsem na to narazil při špatném ukončování vlákna se synapsí, kdy anonymní vlákno čekalo na timeout a mezitím se zbytek ukončil. Zajímé obecné řešení publikovala známá autorka knih o Delphi Dalija Prasnikar - Anonymous Threads: Use or Lose?, ale to jen tak mimochodem.

Kód není složitý, takže jen ty dva body.

První trik je oSocket.OpenBroadcast(44009), jedná se o workaround, protože správné řešení by bylo s oSocket.Connect, které funguje, ale zároveň udělá Bind, což je ale problém, když máte server a client pro test na jednom windows počítači. Řešení s OpenBroadcast je jakoby Connect, ale bez interního Bind.

Druhý trik je oSocket.ReceiveFrom s -1, což získá počet čekajících dat, takže se na to dá reagovat, což jsem získal čtením zdrojáků.

A bonus: Copy(buf, 2) dopočítá délku řetezce, tj. klasický třetí parametr.

U TSocket v constructoru specifikuje typ TSocketType.UDP nebo TSocketType.TCP …, případně další varianty s cílovou adresou, endpointem.

Toto je takový základ, hlavně metod okolo ReceiveFrom a Receive je mraky.

Pozor: veškeré updaty UI nutno synchronizovat v případě volání z vlákna

FMX…

… 
FScan: Boolean;
lstModules: TListBox;
…

uses
  System.Net.Socket, System.IOUtils;

procedure TfrmUDPScan.btnScanClick(Sender: TObject);
begin
  FScan := not FScan;
  if FScan then
    btnScan.Text := 'Stop'
  else
    btnScan.Text := 'Scan';

  if not FScan then
    Exit;
  lstModules.Clear;
  TThread.CreateAnonymousThread(
    procedure
    var
      oSocket: TSocket;
      nep: TNetEndpoint;
      by: TBytes;
      buf: ansistring;
      i, iCount: Integer;
      b: Byte;
    begin
      try
        oSocket := TSocket.Create(TSocketType.UDP);
        try

  //      ep := TNetEndpoint.Create(TIPAddress.Create(255, 255, 255, 255), 22009);
        oSocket.ReceiveTimeout := 1000;
        oSocket.SendTimeout := 1000;
  //      oSocket.Connect('', '255.255.255.255', '', 44009 );
        nep := default(TNetEndpoint);
        oSocket.OpenBroadcast(44009);                 // workaround for Connect x Bind    
        by := TEncoding.ANSI.GetBytes('<ident>');
        oSocket.SendTo(by);
        repeat
          iCount := oSocket.ReceiveFrom(b, -1, nep);  // !!!!!!! -1 -> get count of waiting data
          if iCount > 0 then
          begin
            setlength(buf, iCount);
            i := oSocket.ReceiveFrom(buf[1], iCount, nep);   // get data and sender info
            if i > -1 then
            begin
              TThread.Synchronize(nil,
               procedure
               begin
                 lstModules.Items.Add(nep.Address.Address+':'+Copy(buf, 2));
               end
               );
            end;
          end;
          sleep(10);
        until not FScan;
      finally
        oSocket.Free;
//        mLogInfo('broadcast end');
      end;
    except
      on E: Exception do
      begin
        TThread.Synchronize(nil,
         procedure
         begin
           ShowMessage('Exception:'+E.Message);
         end);
      end;
    end;
    end).Start;
end;

Tagy: , ,

Praxe

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců