Umíme to s Delphi: 129. díl – klientská aplikace přes sockety od A do Z

Dnešní díl seriálu je zaměřen na klientské aplikace komunikující přes sockety. Ukážeme si, které kroky je nutné učinit při vytváření takových aplikací, předvedeme si získávání informací o aktuálním socketovém spojení a naučíme klienty nejen odesílat data na server, ale také přijímat od serveru odpovědi.

V závěru předchozího dílu seriálu jsme začali vysvětlovat posloupnost kroků, kterou je třeba vykonat za účelem naprogramování síťové aplikace v Delphi. Pojmem síťová aplikace v této souvislosti myslíme aplikaci komunikující po síti prostřednictvím protokolu TCP/IP s využitím mechanismu socketů.

Pořád se pohybujeme ve vodách starších Delphi, tedy nikoliv v oblasti Delphi 7. Používáme totiž takové komponenty, které v sedmé verzi nejsou standardně k dispozici. Společnost Borland totiž pro práci se sockety v Delphi 7 doporučuje (a poskytuje) jiné komponenty, jimiž se budeme v seriálu později samozřejmě zabývat.

ClientSocket a ServerSocket v Delphi 7?

Připomeňme však, že pokud vlastníte Delphi 7 a chtěli byste si vyzkoušet postupy popisované v předchozích dílech (a v tomto dílu), není pro vás nic ztraceno – v diskusi pod některými z předchozích článků se objevily postupy, kterak doinstalovat komponenty ServerSocket a ClientSocket i do Delphi 7. Přestože je pravděpodobné, že všichni případní zájemci si tento postup už přečetli a vyzkoušeli, dovolím si jej na tomto místě zopakovat.

Chcete-li používat i v Delphi 7 komponenty ClientSocket a ServerSocket, stačí nainstalovat balík (package), který je uložen v adresáři Delphi7/Bin. Jméno balíčku je "dclsockets70.bpl".

Instalace se provede tak, že z hlavní nabídky Delphi vyberete Component – Install Packages. Následně zvolíte Add, v otevřeném dialogu najdete a označíte soubor dclsockets70.bpl a potvrdíte. Následně dojde k instalaci komponent ClientSocket a ServerSocket do palety dkomponent Delphi. Obě komponenty následně naleznete na záložce Internet.

Pokračujeme dál ve vývoji socketové aplikace

Nyní můžeme navázat tam, kde jsme skončili v předchozím dílu seriálu. Připomeňme, že se jednalo o popis vytváření klientské aplikace pomocí socketů. Vytváříme tedy aplikaci obsahující komponentu ClientSocket. Dosud jsme si popsali tyto dva kroky:

  • specifikování požadovaného serveru – v rámci tohoto kroku je nutné zvolit server, k němuž se naše klientská aplikace má připojovat. Server lze specifikovat buďto jeho IP adresou (vlastnost ClientSocket.Address) nebo jeho jménem (vlastnost ClientSocket.HostName). Kromě serveru je nutné zvolit i port, k němuž se připojujeme – známe-li jeho číslo (číslo služby), použijeme vlastnost ClientSocket.Port, známe-li jeho název (název služby), bude vhodnější vlastnost ClientSocket.Service.
  • navázání spojení – jakmile jsme specifikovali název (číslo) serveru a název (číslo) služby – portu, můžeme navázat se vzdáleným serverem síťové spojení. K tomuto účelu slouží metoda ClientSocket.Open. Stejný význam má nastavení vlastnosti ClientSocket.Active na hodnotu True.

Tolik ke shrnutí předchozího dílu seriálu. Nyní pokračujme dále – dalším krokem , který logicky následuje po navázání spojení se serverem, je získávání informací o aktuálním spojení.

Krok třetí – získávání informací o aktuálním spojení

Poté, co jsme naši klientskou aplikaci úspěšně připojili k TCP serveru, chceme často získávat informace o probíhajícím (otevřeném) spojení. K tomu můžeme použít například objekt systému Windows, který je asociován s naší (klientskou) komponentou ClientSocket: tento objekt můžeme používat k získávání informací o spojení.

Chceme-li zpřístupnit tento objekt, můžeme s výhodou využít vlastnost ClientSocket.Socket. Jakmile máme objekt k dispozici, můžeme využívat jeho nejrůznější vlastnosti a zjišťovat (ověřovat) tak informace související například s adresami a čísly portů použitými v rámci aktuálního, probíhajícího spojení. Jinak řečeno – pomocí tohoto objektu (vlatsnosti Socket) můžeme zjišťovat informace o obou koncích TCP spojení.

Kromě toho můžeme používat vlastnost SocketHandle: tak bychom získali handle na aktuální spojení. Handle je potom možné používat pro případné volání „socketových“ funkcí Windows API. Jinak řečeno – kdybychom chtěli v rámci své aplikace používat některou z nepřeberného množství Windows API funkcí pracujících se sockety, je vlastnost ClientSocket.SocketHandle vhodným začátkem, který nám zpřístupní nezbytné ukazovátko – handle, s jehož pomocí se potom budeme s funkcemi Windows API „bavit“.

Za zmínku asi stojí také vlastnost ClientSocket.Handle: z ní můžeme obdržet handle okna, které dostává zprávy týkající se socketového spojení.

Přestože se v některém z příštích dílů budeme podrobně věnovat jednotlivým vlastnostem komponent pro práci se sockety, ukážeme si na tomto místě malou ukázku. Předpokládejme, že máme spuštěný TCP server a programujeme klientskou aplikaci. Dejme tomu, že bychom chtěli využít tlačítka Button4 ke zjištění, zda je klientská aplikace zrovna připojená nebo odpojená od serveru. Můžeme využít následující kód:

  if ClientSocket1.Socket.Connected then
    ShowMessage(`Aplikace je pripojena k serveru. `)
  else
    ShowMessage(`Aplikace neni pripojena k serveru.`);

Pokud bychom chtěli vypsat i podrobnější informace o probíhajícím spojení, můžeme zdrojový kód jednoduše rozšířit. Stále používáme vlastnost Socket komponenty ClientSocket:

procedure TForm1.Button4Click(Sender: TObject);
var
  VzdalenyPocitac, VzdalenaAdresa : string;
  VzdalenyPort : integer;

begin
  if ClientSocket1.Socket.Connected then begin
    if MessageDlg(`Aplikace je pripojena k serveru. Chcete si precist podrobnejsi informace?`,
        mtInformation, [mbYes, mbNo], 0) = mrYes then begin
      VzdalenyPocitac := ClientSocket1.Socket.RemoteHost;
      VzdalenaAdresa := ClientSocket1.Socket.RemoteAddress;
      VzdalenyPort := ClientSocket1.Socket.RemotePort;

      ShowMessage(`Aplikace je pripojena k serveru ` + VzdalenyPocitac +
        `, IP adresa ` + VzdalenaAdresa +
        `. Cislo sluzby (vzdaleneho portu) je ` + IntToStr(VzdalenyPort) +
        `.`);
    end;
  end
  else
    ShowMessage(`Aplikace neni pripojena k serveru.`)
end;

Ukázku tohoto zdrojového kódu v praxi (za běhu) si můžete prohlédnout na následujícím obrázku:

Klepněte pro větší obrázek

Je samozřejmé, že ke zjištění informací o číslu portu, vzdálené adrese, a ke zjištění dalších údajů, které jsme si právě ukázali, není nutné používat přímo vlastnost Socket, protože přímo komponenta ClientSocket disponuje dostatkem prostředků pro zjišťování těchto základních údajů. Uvedenou aplikaci berte proto spíše jako demonstraci toho, že vlastnost Socket existuje a může být používána pro zjišťování mnoha informací o otevřeném socketovém spojení.

Nyní se pojďme přesunout k dalšímu kroku, který logicky následuje. Jakmile máme navázáno spojení a dokážeme o něm zjišťovat všemožné informace, napadá nás navazující otázka: jak prostřednictvím spojení posílat na server data?

Krok čtvrtý – posílání dat na server

Je samozřejmé, že když programujeme klientskou aplikaci, od níž očekáváme komunikaci s TCP serverem, máme v úmyslu posílat na server nějaká data. Jakým způsobem data na server poslat?

V předchozím odstavci jsme si popisovali vlastnost ClientSocket.Socket, která je vlastně zapouzdřením socketového objektu systému Windows. Tohoto objektu využijeme i pro poslání dat na TCP server.

V ukázkové aplikaci, kterou jsme společně programovali v předchozích dílech seriálu, jsme už poznali jednu (asi základní) metodu pro poslání dat na server; jedná se o metodu SendText. Pokud tedy potřebujeme odeslat na zvolený server textová (řetězcová) data, využijeme metody ClientSocket.Socket.SendText.

Kromě této metody však objekt Socket disponuje i některými dalšími metodami pro odesílání údajů na server, například obecnější metodou SendBuf umožňující odeslat jakoukoliv posloupnost bajtů (bez ohledu na typ přenášených dat). Dalšími metodami jsou SendStream a SendStreamThenDrop, které slouží k posílání proudu dat na server. Parametrem těchto metod je proud (parametr typu TStream): na server jsou následně odeslána všechna data, která lze přečíst z tohoto proudu. Proudy jsme se v našem seriálu dosud nezabývali, a proto ani na tomto místě nebudeme metody SendStream, resp. SendStreamThenDrop nijak podrobně rozebírat.

Důležité však je, že vlastnost Socket obsahuje nejen metody pro odesílání údajů na server, ale také metody pro příjem informací. Je tedy vidět, že zatímco z hlediska komponent Delphi se socketová komunikace rozlišuje na klientskou a serverovou, přesněji řečeno socketové spojení má dvě nezávislé, odlišné strany – klienta a server, z hlediska objektu systému Windows (který je reprezentován vlastnotí Socket) se jedná o jedno homogenní spojení, kde na každém jeho konci můžeme jak odesílat, tak i číst data.

K přijímání dat slouží tři základní metody: ReceiveText, ReceiveBuf a ReceiveLength. První metoda (ReceiveText) slouží k přečtení řetězce ze socketového spojení. Metoda ReceiveBuf je analogií k metodě SendBuf a slouží k přečtení bufferu o zadané délce ze socketového spojení. Třetí metoda – ReceiveLength – je možná nejzajímavější: neslouží přímo k přečtení údajů ze socketu, ale vrací počet bajtů, které jsou připraveny k přečtení. Pomocí této metody tedy můžeme zjistit, kolik bajtů se posílá socketovým spojením a podle tohoto údaje upravit případné následující „přijímací“ mechanismy.

Vzhledem k tomu, že metod pro posílání a čtení informací je poměrně hodně, jistě neuškodí jednoduchá ukázka. V jejím rámci si ukážeme ještě jedno zajímavé použití klientských a serverových socketů. Typická situace totiž vypadá tak, že klienti se na server obrací v okamžiku, kdy od nich něco potřebují. Jinak řečeno, klienti kontaktují server (a posílají mu svá data) v situaci, kdy chtěji od serveru vykonat nějakou operaci a vrátit její výsledky. To je typické schéma klient-server architektury – málokdy klienti pouze odesílají data na server a neočekávají žádnou odpověď (i když ani takové situaci samozřejmě nejsou nereálné).

Ukážeme si tedy takovou situaci. Předpokládejme, že chceme naprogramovat jednoduchou aplikaci, která odešle na server celé číslo a bude očekávat, že server toto číslo umocní na druhou a pošle naší slavné aplikaci výsledek (tedy druhou mocninu).

Vytvoření aplikací se nebude příliš lišit od vytvoření klientské a serverové aplikace sepsané v dílech 125 a 126. V následujícím textu proto uvedeme pouze odlišnosti, pro detailní postup vytváření aplikací doporučuji nahlédnout do zmíněných dvou dílů.

Nejprve popíšeme odlišnost u klientské aplikace. Jediná změna u klientské aplikace spočívá v tom, že nyní budeme nejen odesílat data, ale také přijímat výsledky. Toto přijetí provedeme v rámci obsluhy události OnRead komponenty ClientSocket. Obsluha této události je jednoduchá a skládá se vlastně jen ze zobrazení přijatého údaje:

procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  ShowMessage(`Vysledek operace je ` + Socket.ReceiveText);
end;

Je patrné, že v klientské aplikaci není příliš mnoho odlišností. Kromě toho si všimněte, jak úžasně jednoduché je přijímání informací klientskou aplikací – jedná se vlastně o obdobný postup, jaký používají servery – ošetří se příslušná událost, v tomto případě OnRead.

Nyní se podívejme na serverovou aplikaci. Ani u ní není příliš mnoho odlišností a změn. Upravíme totiž pouze obsluhu události OnClientRead. V jejím rámci už jen nezobrazujeme přijatá data, ale také počítáme požadovaný výsledek a posíláme jej nazpět v rámci daného socketu:

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var Cislo, Vysledek: Integer;
    Pomocna: String;
begin
  Pomocna := Socket.ReceiveText;
  ListBox2.Items.Add(`Prijato od ` + Socket.RemoteHost + `: ` + Pomocna);

  Cislo := StrToInt(Pomocna);
  Vysledek := Cislo * Cislo;

  Socket.SendText(IntToStr(Vysledek));
end;

Vidíte, že ani serverové aplikaci nedělá velký problém přijímat požadavky a vracet výsledky. Poznamenejme, že v těchto ukázkách se nezabýváme testováním správnosti, proto pokud nezbedný uživatel nezadá celé číslo, ale například řetězec, „spadne“ server, zatímco klient se o chybě nedozví a pouze nedostane žádný výsledek. To je však již věc dalšího ladění aplikací, která nesouvisí přímo s problematikou článku - se sockety.

Pokud nyní server i klienty spustíte a připojíte klienta, můžete do klienta zadat a odeslat celé číslo (viz obrázek):

Klepněte pro větší obrázek

Na serveru se vypíše informace o tom, že klient poslal číslo 12, viz obrázek:

Klepněte pro větší obrázek

Na klientovi se vzápětí vypíše informační hlášení s hodnotou, kterou poslal server, viz obrázek:

Klepněte pro větší obrázek

Tímto způsobem je možné psát aplikace komunikující se serverem v obou směrech, tj. nejen odesílající data, ale také přijímající odpovědi (výsledky).

Tím jsme probrali komunikaci se serverem. Závěrem se pojďme přesunout k poslednímu kroku v pořadí. Tím je uzavření socketového spojení.

Krok pátý – uzavření spojení

Pakliže se rozhodneme, že už spojení nepotřebujeme a že je vhodné jej ukončit (uzavřít), můžeme využít metody ClientSocket.Close. Spojení může být stejně tak dobře ukončeno prostřednictvím serveru: pokud se server sám odpojí, bude spojení také ukončeno a naše aplikace přejde do stavu „disconnected“. V takovém případě dostane klient událost OnDisconnect, v jejíž obsluze může na odpojení odpovídajícím způsobem zareagovat (přinejmenším je vhodné alespoň vypsat uživateli informační hlášení o tom, že spojení bylo ukončeno).

Na závěr

V dnešním dílu jsme rozebrali otázku klientských aplikací a jejich vytváření. Ukázali jsme si, jak komunikovat se serverem, zjišťovat informace o probíhajícím spojení i ukončovat spojení. Kromě toho jsme se naučili, jak přijímat od serveru výsledky operací, takže už nemusíme komunikovat pouze jednosměrně ve směru od klienta k serveru, ale také naopak, což je typickou ukázkou architektury klient-server.

Diskuze (24) Další článek: Červ Kibuv zřejmě nemá rád George Bushe

Témata článku: Software, Windows, Programování, Případné volání, Následující aplikace, Jednoduchá ukázka, Socket, Typická situace, Jediná změna, Delphi, Obdobný postup, SoC, Zajímavé použití, DEL, Spojení, Přijatý výsledek, Server, Tito, Díl, Klientská aplikace, Proud, Třetí metoda, Předchozí odstavec, Krok, Toto


Určitě si přečtěte

Wi-Fi 6 konečně začíná dostávat smysl. Poradíme, jak ji využít

Wi-Fi 6 konečně začíná dostávat smysl. Poradíme, jak ji využít

** Na trh míří první levné Wi-Fi 6 routery ** Nabídka zařízení, zejména notebooků, každý den roste ** Poradíme, jak nejlépe přejít s domácností na Wi-Fi 6

Tomáš Holčík | 31

Pozor, na Česko v těchto dnech útočí falešné Tesco, Penny Market a Lidl

Pozor, na Česko v těchto dnech útočí falešné Tesco, Penny Market a Lidl

** Máme tu další českou phishingovou vlnu ** Podle průzkumů máme stále problém s kybernetickou gramotností ** Nebezpečím jsou děti, které opouštějí rodiče

Jakub Čížek | 37

10 mýtů a polopravd o bateriích, kterým možná ještě věříte

10 mýtů a polopravd o bateriích, kterým možná ještě věříte

** Kolem baterií a akumulátorů koluje řada mýtů, nepravd a polopravd ** Dnes vám devět z nich zkusíme vyvrátit na základě faktů ** Většina z nich totiž neplatí pro moderní lithiové baterie

Karel Kilián, David Polesný | 102



Aktuální číslo časopisu Computer

Test 9 bezdrátových reproduktorů

Jak ovládnout Instagram

Test levných 27" herních monitorů

Jak se zbavit nepotřebných věcí na internetu