V minulém dílu tohoto seriálu jsme si vysvětlili základní, obecné informace o počítačových sítích a o tom, co potřebujeme znát a mít pro programování síťových aplikací (tedy aplikací schopných komunikovat po síti).
Protože minulý článek byl zaměřen ryze teoreticky, a nerad bych vás znudil příliš dlouhými řečmi, podíváme se dnes na zcela praktickou otázku. Naprogramujeme společně první síťovou aplikaci – a využijeme k tomu koncepci tzv. socketů.
Protože jsme si dosud nevysvětlili žádné podrobnosti o socketech ani o protokolech TCP/IP, je možné, že vám nebude princip vytváření aplikace úplně zřejmý. To však v tomto okamžiku vůbec nevadí: aplikaci napíšeme krok za krokem s tím, že naším cílem bude vytvoření funkční dvojice programů, které spolu budou umět komunikovat.
V následujících článcích si pak budeme postupně vysvětlovat jednotlivé aspekty tvorby těchto aplikací: pak se dozvíte odpovědi na všechny otázky, které vám třeba po dnešním článku vytanou na mysli.
Protože jsme si řekli, že dnes nebudeme zabíhat do žádných teoretických a ostatně ani technických podrobností, proneseme jen skutečně nezbytný úvod k samotnému vytvoření aplikace.
Dnes společně vytvoříme první z dvojice aplikací, přičemž tyto aplikace spolu budou umět „komunikovat“. Při vytváření komunikujících aplikací existuje v zásadě několik možných architektur a několik možných uspořádání. My se dnes budeme držet klasického modelu klient-server.
Vytvoříme tedy postupně dvě aplikace; jedna bude vystupovat v roli serveru a druhá v roli klienta:
Uspořádání klient-server je zřejmě nejtradičnější architekturou aplikací, v níž existuje řada klientů, kteří se připojují k jednomu serveru a posílají mu své požadavky. Server se typicky chová tak, že provede operaci požadovanou požadavkem (například získá nebo zjistí požadovaná data) a výsledek pošle zpět klientům, kteří s ním naloží dle svého vlastního uvážení.
Stejným způsobem funguje vlastně principiálně i celý Internet: chcete-li se podívat na nějakou internetovou stránku, spustíte si internetový prohlížeč (tj. klienta) a zapíšete adresu serveru, k němuž se chcete připojit. Následně proběhne poměrně několik poměrně sofistikovaných operací nutných pro lokalizaci serveru a pro doručení požadavku – tyto operace jsou však již provedeny síťovou infrastrukturou a nikterak se o ně nestaráme. Server pak přijme požadavek, provede jej (tj. připraví požadovaná data – požadovanou internetovou stránku) a pošle tato data zpět klientovi (prohlížeči). Prohlížeč data zobrazí dle svého nejlepšího vědomí a svědomí (zde tedy hledejte důvod, proč tatáž stránka vypadá na různých počítačích – prohlížečích – různě).
Tolik tedy k tomu, co vlastně budeme programovat. Ještě je nutné pronést jednu důležitou poznámku: následující postup si nemohou vyzkoušet (a následující dvě aplikace naprogramovat) majitelé Delphi verze 7. Použijeme totiž dvě komponenty (ServerSocket a ClientSocket ze záložky Internet), které se vyskytují v Delphi až po verzi 6, ale v Delphi 7 byly odebrány a nahrazeny jinými. Nové komponenty v Delphi 7 jsou sofistikovanější, ale také trochu obtížnější na používání, a proto v této ukázce použijeme jednodušší cestu realizovatelnou v Delphi 5.
Pojďme se bez otálení vrhnout do díla. Dnes vytvoříme serverovou aplikaci, tedy aplikaci přijímající požadavky klientů. Naprogramování klientů přijde na řadu za týden.
Aplikace, kterou vytvoříme, bude sloužit jako jednoduchý TCP/IP server. Po spuštění bude aplikace čekat na připojení klientů. Sice jsme si ještě neprozradili, co to je port v TCP/IP komunikaci, ale představte si jej pouze jako podrobnější rozlišovací označení na straně serveru pro případ, že by server měl umět komunikovat s více druhy klientů. V našem případě tedy říkáme, že server poslouchá klienty na svém portu číslo 5050, což pro nás znamená jediné – aby náš klient mohl přistoupit k tomuto serveru, musí kromě adresy serveru uvést také číslo portu 5050.
Poté, co se připojí nový klient, vypíše o tom server zprávu do seznamu ListBox, stejně se zachová po odpojení kteréhokoliv klienta. Do druhého seznamu ListBox aplikace vypisuje data, která jí zasílají připojení klienti.
Tolik k popisu aplikace. Nyní se krok za krokem pustíme do jejího vytváření. Ještě ednou připomenu, že následující postup není možný v Delphi verze 7.
1. Vytvořte v Delphi 5 novou aplikaci. Na formulář umístěte následující komponenty:
2. Nejprve ošetříme událost OnCreate hlavního formuláře. V obsluze události nastavíme některé důležité vlastnosti serveru:
procedure TForm1.FormCreate(Sender: TObject);
begin
//
nastaveni serveru
ServerSocket1.Port := 5050;
// cislo portu - zvolime 5050
ServerSocket1.ServerType := stNonBlocking; // server bude
neblokujici
ServerSocket1.Active := True;
// server "zapneme"
Caption := `Serverová
aplikace přes sockety`;
end;
3. Nyní zaměříme svou pozornost na komponentu ServerSocket, která tvoří samotné jádro našeho serveru. Nejprve ošetříme její událost OnClientConnect, která je vyvolána po připojení každého nového klienta k serveru. Jedinou činností, kterou v této jednoduché ukázce provedeme po připojení klienta k serveru, bude vypsání informační hlášky oznamující připojení klienta a sdělující jeho IP adresu:
// udalost OnClientConnect nastane po pripojeni klienta
procedure
TForm1.ServerSocket1ClientConnect(Sender: TObject;
Socket:
TCustomWinSocket);
begin
ListBox1.Items.Add(`Připojeno: ` +
Socket.RemoteHost + `(` + Socket.RemoteAddress + `)`);
end;
4. Další událost, která nás bude zajímat, se bude opět týkat komponenty ServerSocket. Bude se jednat o událost OnClientDisconnect, která nastává po odpojení klienta od serveru. V našem případě na odpojení klienta zareagujeme pouze vypsáním informační hlášky:
// udalost OnClientDisconnect nastane po odpojeni klienta
procedure
TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
Socket:
TCustomWinSocket);
begin
ListBox1.Items.Add(`Odpojeno: ` +
Socket.RemoteHost + ` (` + Socket.RemoteAddress + `)`);
end;
5. Další událostí komponenty ServerSocket, kterou ošetříme, bude OnClientRead. Ta nastane tehdy, když má server přečíst data zaslaná klientem. Jinak řečeno – zašle-li klient našemu serveru nějaká data, přečteme je v obsluze události OnClientRead:
// udalost OnClientRead nastane, ma-li server precist data zaslana
klientem
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
ListBox2.Items.Add(Socket.RemoteHost + `: ` +
Socket.ReceiveText);
end;
6. A jsme prakticky hotoví! Poslední událost, kterou ošetříme, bude OnClose formuláře, v jejíž obsluze „vypneme“ server:
procedure TForm1.FormClose(Sender: TObject; var Action:
TCloseAction);
begin
ServerSocket1.Active := False;
// server "vypneme"
end;
Aplikace je hotova. Ač se to zdá být nemožné, právě jsme dokončili svého prvního TCP/IP klienta. Jedná se samozřejmě o zcela primitivní server, který prakticky nic neumí, nicméně samotná skutečnost, že takto jednoduchá aplikace je schopna komunikovat po síti s klienty z celého světa, je pozoruhodná.
Pokud chcete, můžete si zkusit aplikaci spustit. Prozatím nebude nic umět a nic dělat. K tomu, abychom se mohli pokochat její funkčností, musíme ještě naprogramovat klienty, kteří se k našemu serveru budou připojovat.
Ukázku aplikace si můžete prohlédnout na následujících obrázcích. Nejprve se podívejte na aplikaci v návrhové fázi (je patrné, že jediná komponenta realizující funkčnost TCP/IP serveru, je ServerSocket):
Na následujícím obrázku je běžící server (bez jakýchkoliv připojených klientů):
Závěrem dnešního dílu si uvedeme kompletní zdrojový kód. Je až překvapivě krátký na to, že jeho výsledkem je fungující (i když jsme se o tom dnes ještě nepřesvědčili :-)) TCP server.
unit Unit1;
interface
uses
Windows, Messages,
SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp,
StdCtrls;
type
TForm1 = class(TForm)
ListBox1:
TListBox;
ListBox2: TListBox;
Label1:
TLabel;
Label2: TLabel;
ServerSocket1:
TServerSocket;
procedure FormCreate(Sender: TObject);
procedure ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure
ServerSocket1ClientDisconnect(Sender: TObject;
Socket:
TCustomWinSocket);
procedure ServerSocket1ClientRead(Sender:
TObject;
Socket: TCustomWinSocket);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1:
TForm1;
implementation
{$R *.DFM}
procedure
TForm1.FormCreate(Sender: TObject);
begin
// nastaveni
serveru
ServerSocket1.Port := 5050;
// cislo portu - zvolime 5050
ServerSocket1.ServerType := stNonBlocking; // server bude
neblokujici
ServerSocket1.Active := True;
// server "zapneme"
Caption := `Serverová
aplikace pøes sockety`;
Label1.Caption := `Spojeni:`;
Label2.Caption := `Prijata data:`;
end;
// udalost OnClientConnect
nastane po pripojeni klienta
procedure
TForm1.ServerSocket1ClientConnect(Sender: TObject;
Socket:
TCustomWinSocket);
begin
ListBox1.Items.Add(`Pøipojeno: ` +
Socket.RemoteHost + `(` + Socket.RemoteAddress + `)`);
end;
//
udalost OnClientDisconnect nastane po odpojeni klienta
procedure
TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
Socket:
TCustomWinSocket);
begin
ListBox1.Items.Add(`Odpojeno: ` +
Socket.RemoteHost + ` (` + Socket.RemoteAddress + `)`);
end;
//
udalost OnClientRead nastane, ma-li server precist data zaslana
klientem
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
ListBox2.Items.Add(Socket.RemoteHost + `: ` +
Socket.ReceiveText);
end;
procedure TForm1.FormClose(Sender:
TObject; var Action: TCloseAction);
begin
ServerSocket1.Active :=
False; // server
"vypneme"
end;
end.
Na závěr V dnešním článku jsme společně naprogramovali jednoduchý TCP server, který využívá socketů a k jehož realizaci je použita komponenta ServerSocket. Předchozí aplikaci můžeme naprogramovat pouze v Delphi verze 5, protože komponenta ServerSocket byla v Delphi 6 odebrána.
Abychom si mohli vyzkoušet, zda (a jak) aplikace funguje, musíme napsat ještě klienty, kteří by se k ní mohli připojit. A přesně to bude naším úkolem za týden.