10. Práce se sokety

V této kapitole se začneme zabývat vytvářením Internetovských a Intranetovských aplikací a distribuovaných aplikací. Nejdříve se budeme zabývat sokety.
Komponenty soketů umožňují vytvářet aplikace, které mohou komunikovat s ostatními systémy pomocí TCP/IP a podobných protokolů. Pomocí soketů můžeme číst a zapisovat prostřednictvím připojení na další počítač a to bez nutnosti znalostí aktuálního síťového software. Sokety poskytují připojení založené na protokolu TCP/IP, ale jsou také určeny pro práci s podobnými protokoly, jako je Xerox Network System (XNS), DECnet firmy Digital nebo IPX/SPX firmy Novell.
C++ Builder umožňuje zapisovat síťové servery nebo klientské aplikace, které čtou z nebo zapisují na ostatní systémy. Aplikace serveru nebo klienta je obvykle vyhrazena k jedné službě jako je Hypertext Transfer Protocol (HTTP) nebo File Transfer Protocol (FTP). Pomocí soketů serveru, aplikace poskytující jednu z těchto služeb, se může spojit s klientskou aplikací, která chce používat tuto službu. Klientské sokety umožňují aplikaci používající jednu z těchto služeb, spojení se serverovou aplikací, která službu poskytuje.
K zápisu aplikací používajících sokety, musíme pochopit:

Implementování služeb

Sokety poskytují jádro potřebné k zápisu síťových serverů a klientských aplikací. Mnoho služeb, jako je HTTP nebo FTP je dobře dostupných. Někdy jsou služby zabudované do operačního systému a tedy není nutné zapisovat své vlastní. Nicméně, když požadujeme lepší řízení než poskytuje implementovaná služba, těsnější integraci mezi naší aplikací a síťovou komunikací nebo když požadovaná služba není dostupná, pak můžeme chtít vytvořit svoji vlastní serverovou nebo klientskou aplikaci. Např. když pracujeme s distribuovanou datovou množinou, můžeme chtít zapsat komunikační vrstvu s databázemi na ostatních systémech.
K implementaci a používání služeb realizovaných sokety se musíme seznámit s:

Protokoly služeb

Dříve než můžeme zapisovat aplikaci síťového serveru nebo klienta, musíme pochopit služby, které naše aplikace bude poskytovat nebo používat. Mnoho služeb má standardní protokoly, které naše síťová aplikace může využívat. Pokud zapisujeme síťovou aplikaci pro standardní službu, jako je HTTP nebo FTP, musíme nejprve pochopit protokoly použité pro komunikaci s ostatními systémy. Musíme si prohlédnout dokumentaci služby, kterou chceme používat.
Pokud poskytujeme novou službu pro aplikaci, která komunikuje s ostatními systémy, pak prvním krokem je navržení komunikačního protokolu pro server i klienta této služby. Jaké zprávy jsou zasílány? Jak jsou tyto zprávy koordinovány? Jak jsou informace zakódovány?
Často naše aplikace síťového serveru nebo klienta poskytuje vrstvu mezi síťovým softwarem a aplikací, která používá služby. Např. HTTP server je mezi aplikací Web serveru a Internetem, který poskytuje připojení a reaguje na zprávy HTTP.
Sokety poskytují rozhraní mezi našim síťovým serverem nebo klientskou aplikací a síťovým software. Musí poskytnout rozhraní mezi naší aplikací a aplikací, která ji používá. Můžeme použít standardní službu API (jako je ISAPI) nebo můžeme navrhnout a zveřejnit své vlastní API.
Služby a porty
Většina standardních služeb je spojená (konvencí) se specifickým číslem portu. Když implementujeme službu, pak můžeme považovat číslo portu jako číselný kód služby.
Jestliže implementujeme standardní službu, objekty soketů Windows poskytují metody pro nalezení čísla portu pro službu. Pokud poskytujeme novou službu, pak můžeme specifikovat přiřazené číslo portu v souboru SERVICES na počítači s Windows 95 nebo NT. Více informací o nastavování souboru SERVICES nalezneme v dokumentaci Soketů Windows.

Typy soketových připojení

Soketová připojení můžeme rozdělit na tři základní typy, které určují, jak připojení bude inicializováno a k čemu se připojujeme. Jsou to: Pouze připojení na klientský soket je kompletováno, jinak serverové připojení je nerozeznatelné od klientského připojení. Oba koncové body mají stejné možnosti a získávají stejné typy událostí. Naslouchací připojení se zásadně liší, má pouze jeden koncový bod.

Klientské připojení

Klientské připojení připojuje klientský soket na lokálním systému k serverovému soketu na vzdáleném systému. Klientská připojení jsou inicializována klientským soketem. Klientský soket musí popisovat serverový soket, ke kterému se chceme připojit. Klientský soket pak hledá serverový soket a když server nalezne vyžaduje připojení. Serverový soket udržuje frontu klientských požadavků a kompletuje připojení. Když serverový soket akceptuje klientské připojení, pak serverový soket, ke kterému se připojujeme zasílá svůj plný popis klientskému soketu a připojení je klientem kompletováno.

Naslouchací připojení

Serverové sokety nelokalizují klienty. Jsou pasivní formou "poloviny připojení" která naslouchá klientským požadavkům. Serverové sokety přiřazují frontu ke svým naslouchacím připojením; fronta zaznamenává požadavky klientů na připojení jak přicházejí. Když serverový soket akceptuje požadavek na klientské připojení, provedeto to formou nového soketu pro připojení klienta a naslouchací připojení zůstává otevřeno pro akceptování ostatních klientských požadavků.

Severové připojení

Serverové připojení je formováno serverovými sokety, když naslouchací soket akceptuje klientský požadavek. Popis serverového soketu, který kompletuje připojení ke klientu je zaslán klientu, když server připojení akceptuje. Připojení je zřízeno, když klientský soket přijme tento popis a kompletuje připojení.

Popisování soketů

Sokety umožňují naši síťové aplikaci komunikovat s ostatními systémy v síti. Na každý soket se můžeme dívat jako na koncový bod síťového propojení. Má adresu, která specifikuje: Plný popis soketového propojení, musí poskytnout adresy soketů na obou koncích připojení. Můžeme popsat adresu každého soketového koncového bodu předáním IP adresy nebo jména hostitele a čísla portu.
Dříve než můžeme zřídit propojení soketů, musíme plně popsat sokety tvořící jeho koncové body. Některé informace jsou dostupné ze systému, na kterém je naše aplikace spuštěna. Např. nemusíme zadávat lokální IP adresu klientského soketu neboť tato informace je zjistitelná z operačního systému.
Informace, které musíme poskytnout závisí na typu soketu se kterým pracujeme. Klientský soket musí popsat server, ke kterému se chceme připojit. Naslouchací soket serveru musí popsat port, který reprezentuje poskytovanou službu.

Popisování hostitele

Hostitel je systém, spouštějící aplikaci, která obsahuje soket. Hostitele pro soket můžeme popsat jeho IP adresou, což je řetězec čtyř číselných hodnot (slabik) v tečkovém zápisu Internetu, jako je
123.197.1.2
Jeden systém může být identifikován více než jednou IP adresou.
IP adresy jsou obtížně zapamatovatelné a jsou zdrojem chyb. Alternativou je použití jména hostitele. Jméno hostitele je přezdívka pro IP adresu, kterou často vidíme v URL (Uniform Resource Locators). Je to řetězec obsahující jméno domény a služby, např.
http://www.wSite.Com
V Internetu poskytujeme jméno hostitele pro IP adresu systému na Internetu. Na počítačích s operačním systémem Windows 95/98/NT, pokud jméno hostitele není dostupné, pak jej můžeme vytvořit pro naši lokální adresu zadáním jména do souboru HOSTS. Více informací o souboru HOSTS nalezneme v dokumentaci Soketů Windows.
Serverové sokety nepotřebují specifikovat hostitele. Lokální IP adresa může být přečtena ze systému. Pokud systém má více než jednu IP adresu, pak serverové sokety naslouchající klientským požadavkům naslouchají na všech IP adresách současně. Když serverový soket akceptuje připojení, pak klientský soket poskytne vzdálenou IP adresu. Klientský soket musí specifikovat vzdáleného hostitele poskytnutím jména hostitele nebo IP adresy.
Většina aplikací používá jméno hostitele pro specifikaci systému. Jméno hostitele je snadněji zapamatovatelné a snadněji se testuje na typografické chyby. Dále server může změnit systém nebo IP adresu, která je přiřazena k jistému jménu hostitele. Použitím jména hostitele může klientský soket nalézt abstraktní místo reprezentované jménem hostitele i když jeho IP adresa se změní.
Pokud jméno hostitele je neznámé, pak klientský soket musí specifikovat systém serveru pomocí jeho IP adresy. Specifikace serverového systému pomocí IP adresy je rychlejší. Když poskytneme jméno hostitele, pak soket musí hledat IP adresu přiřazenou k tomuto jménu hostitele, dříve než může lokalizovat systém serveru.

Používání portů

I když IP adresa poskytuje dostatek informací k nalezení systému na druhém konci soketového připojení, musíme také zadat číslo portu na tomto systému. Bez čísel portů by systém mohl pracovat pouze s jedním připojením. Čísla portů jsou jednoznačné identifikátory, které umožňují jednomu systému hostit současně více připojení, přiřazením každému připojení samostatného čísla portu.
Z jednoho pohledu, čísla portů jsou číselné kódy pro služby implementované síťovou aplikací. To je konvence, která umožňuje naslouchacím serverovým připojením být dostupnými na pevných číslech portů a tedy mohou být hledány klientskými sokety. Serverové sokety naslouchají na portu čísla přiřazeného ke službě, kterou poskytuje. Když akceptují připojení na klientský soket, pak vytváří samostatné soketové připojení, které používá jiné číslo portu. Tímto způsobem naslouchací připojení může stále poslouchat na portu čísla přiřazeného ke službě.
Klientské sokety používají volná lokální čísla portů, která nejsou používány ostatními sokety. Specifikují číslo portu serverového soketu, ke kterému se chtějí připojit a tak nalézt serverovou aplikaci. Často toto číslo portu je specifikováno nepřímo, jménem požadované služby.

Používání komponent soketů

C++ Builder poskytuje (na stránce Internet Palety komponent) dvě komponenty soketů, klientský soket a serverový soket, které umožňují našim síťovým aplikacím připojení na jiný počítač a které umožňují číst a zapisovat informace pomocí tohoto propojení. Ke každé z těchto komponent je přiřazen objekt soketu Windows, který reprezentuje koncový bod aktuálního propojení soketů. Komponenta soketu používá objekty soketů Windows k zaobalení volání API soketu Windows a tak naše aplikace se nemusí zabývat detaily zřizování připojení nebo správou zpráv soketu.
Pokud chceme pracovat s voláním API soketu Windows nebo přizpůsobit detaily propojení, pak můžeme použít vlastnosti, události a metody objektu soketu Windows.

Používání klientských soketů

Přidáním komponenty klientského soketu (TClientSocket) na náš formulář nebo datový modul zapojíme naši aplikaci do klienta TCP/IP. Klientské sokety umožňují specifikovat serverový soket, ke kterému se chceme připojit a službu, kterou požadujeme od serveru. Když již máme popsané požadované připojení, pak můžeme použít komponentu soketu klienta ke kompletování připojení na server.
Každá komponenta klientského soketu používá jeden objekt soketu Windows (TClientWinSocket) k reprezentaci klientského koncového bodu v připojení.
Klientské sokety používáme ke:
Specifikace žádaného serveru
Komponenta klientského soketu má řadu vlastností, které umožňují specifikovat systém serveru a port, ke kterému se chceme připojit. Systém serveru můžeme specifikovat jeho jménem hostitele pomocí vlastnosti Host. Pokud neznáme jméno hostitele, nebo pokud chceme urychlit lokalizaci serveru, pak můžeme specifikovat tečkovou IP adresu systému serveru pomocí vlastnosti Address. Musíme specifikovat jméno hostitele nebo IP adresu. Pokud specifikujeme obojí, pak komponenta použije jméno hostitele.
Mimo systému serveru, musíme specifikovat port na systému serveru, ke kterému se náš klientský soket chce připojit. Můžeme chtít specifikovat číslo portu serveru přímo pomocí vlastnosti Port nebo nepřímo pojmenováním požadované služby pomocí vlastnosti Service. Pokud specifikujeme obojí, pak je použito jméno služby.
Formování připojení
Pokud již máme nastaveny vlastnosti komponenty klientského soketu na popis serveru ke kterému se chceme připojit, pak se můžeme za běhu připojit voláním metody Open. Pokud chceme aby naše aplikace se připojovala automaticky při spuštění, pak nastavíme vlastnost Active při návrhu pomocí Inspektora objektů na true.
Získávání informací o připojení
Po zkompletování připojení na soket serveru, můžeme použít objekt klientského soketu Windows přiřazený k naší komponentě klientského soketu k získání informací o připojení. Pomocí vlastnosti Socket získáme přístup k objektu klientského soketu Windows. Tento objekt soketu Windows má vlastnosti, které umožňují určit adresu a číslo portu použité klientským a serverovým soketem na koncích připojení. Vlastnost SocketHandle můžeme použít k získání madla soketu připojení pro použití ve voláních API soketu Windows. Vlastnost Handle můžeme použít k přístupu k oknu získávajícímu zprávy od soketu připojení. Vlastnost AsyncStyles určuje jaký typ zpráv toto madlo okna přijímá.
Uzavření připojení
Po dokončení komunikace se serverovou aplikací pomocí soketového propojení, můžeme zrušit připojení voláním metody Close. Připojení může být také ukončeno serverem. V tomto případě získáme oznámení v události OnDisconnect.

Používání serverových soketů

Přidáním komponenty soketu serveru (TServerSocket) na náš formulář nebo datový modul zapojíme naši aplikaci do serveru TCP/IP. Soket serveru umožňuje specifikovat službu, kterou poskytujeme nebo port, který chceme použít k poslouchání klientských požadavků. Soket serveru můžeme použít k naslouchání a akceptování požadavků klientů na připojení.
Každá komponenta soketu serveru používá jeden objekt soketu serveru Windows (TServerWinSocket) k reprezentaci serverového koncového bodu v naslouchacím připojení. Také používá objekt soketu serverového klienta Windows (TServerClientWinSocket) pro serverový konec každého aktivního připojení na klientský soket, který server akceptuje.
Serverové sokety použijeme ke:
Specifikování požadovaného portu
Dříve než náš serverový soket může naslouchat klientským požadavkům, musíme specifikovat port na kterém náš server bude naslouchat. Tento port můžeme specifikovat pomocí vlastnosti Port. Pokud naše serverová aplikace poskytuje standardní službu, která je přiřazena konvencí ke specifickému číslu portu, pak můžeme port specifikovat nepřímo pomocí vlastnosti Service. Při nastavování čísla portu je vhodné používat vlastnost Service. Pokud specifikujeme vlastnost Port i Service, pak soket serveru použije jméno služby.
Naslouchání klientským požadavkům
Když již máme nastaveno číslo portu, pak můžeme začít naslouchat připojením, voláním metody Open. Pokud chceme, aby naše aplikace automaticky začala naslouchat při spuštění, pak nastavíme vlastnost Active na true při návrhu pomocí Inspektora objektů.
Připojování na klienty
Naslouchání komponentou soketu serveru automaticky akceptuje požadavek klienta na připojení, když je přijat. Výskyt této události je oznámen událostí OnClientConnect.
Získávání informací o připojeních
Když již máme otevřeno naslouchací připojení našim serverovým soketem, pak můžeme použít objekt serverového soketu Windows přiřazený k naší komponentě serverového soketu k získání informací o připojení. Použijeme vlastnost Socket k získání přístupu k objektu serverového soketu Windows. Tento objekt soketu Windows má vlastnosti, které umožňují nalézt všechny aktivní připojení klientských soketů, které byly akceptovány komponentou serverového soketu. Vlastnost SocketHandle použijeme k získání madla soketu připojení pro použití ve volání API soketu Windows. Vlastnost Handle použijeme pro přístup k oknu, které přijímá zprávy od soketu připojení.
Každé aktivní propojení s klientskou aplikací je zaobaleno objektem soketu serverového klienta Windows (TServerClientWinSocket). Můžeme k němu přistupovat prostřednictvím vlastnosti Connections objektu soketu Windows. Tyto objekty soketů serverového klienta Windows mají vlastnosti, které umožňují určit adresu a číslo portu použitou sokety serveru a klienta tvořících koncové body propojení. Vlastnost SocketHandle použijeme k získání madla soketu připojení pro použití ve volání API soketu Windows. Vlastnost Handle použijeme pro přístup k oknu, které přijímá zprávy od soketu připojení. Vlastnost AsyncStyles určuje jaké typy zpráv okno může přijímat.
Uzavírání serverových připojení
Když chceme ukončit naslouchací připojení, pak voláme metodu Close. Tím ukončíme všechna otevřená propojení klientských aplikací, zrušíme nevyřízené připojení, které nejsou akceptovány a ukončíme naslouchací připojení a tak již naše komponenta serverového soketu nemůže akceptovat žádná nová připojení.
Když klienti ukončí jednotlivá připojení, pak náš serverový soket je informován událostí OnClientDisconnect.

Reagování na události soketu

Když zapisujeme aplikaci používající sokety, pak většinu práce obvykle umisťujeme do obsluh událostí komponent soketů. Události OnRead a OnWrite v neblokujících klientských soketech nastanou, když proběhlo čtení nebo zápis pomocí propojení soketů. Podobně serverové sokety (blokující nebo neblokující) získávají událost OnClientRead a OnClientWrite.
Klientské sokety získávají událost OnDisconnect když server ukončí připojení a serverové sokety získávají událost OnClientDisconnect, když klient ukončí připojení.
Dále klientské i serverové sokety generují událost chyby, když přijmou chybovou zprávu od připojení. Komponenty soketů také přijímají několik událostí v případě otevření a kompletování připojení. Pokud naše aplikace chce ovlivňovat způsob zpracování otevíraní soketů nebo zahajování čtení nebo zápisu, pak můžeme požadovat zapsat obsluhy událostí reagující na tyto klientské nebo serverové události.

Události chyb

Klientské sokety generují událost OnError, když přijmou chybovou zprávu od připojení. Serverové sokety generují OnClientError. Můžeme zapsat obsluhy těchto událostí k reagování na tyto chybové zprávy. Obsluze je předána informace o tom: V obsluze můžeme chybu zpracovat a změnit chybový kód na 0 pro zabránění soketu v generování výjimky.

Události klienta

Když klientský soket otevírá připojení, pak nastanou následující události:
  1. Událost OnLookup nastane před pokusem lokalizovat serverový soket. Od tohoto okamžiku nemůžeme měnit vlastnosti Host, Address, Port nebo Service pro změnu lokalizovaného serverového soketu. Můžeme použít vlastnost Socket pro přístup k objektu klientského soketu Windows a vlastnost SocketHandle k provedení volání API Windows, které ovlivní vlastnosti soketu. Např. pokud chceme, pak můžeme nastavit číslo portu klientské aplikace (musíme to udělat nyní).
  2. Soket Windows je nastaven a inicializován pro oznamování událostí.
  3. Událost OnConnecting nastane po lokalizaci serverového soketu. Objekt soketu Windows je dostupný pomocí vlastnosti Socket a může poskytnout informace o serverovém soketu, který je na druhém konci propojení. Toto je první možnost získání aktuálního portu a IP adresy použité pro propojení, které se mohou lišit od portu a IP adresy naslouchacího soketu, který akceptuje připojení.
  4. Požadavek na připojení je akceptován serverem a kompletován klientským soketem.
  5. Po zřízení připojení vzniká událost OnConnect. Pokud náš soket má bezprostředně začít číst nebo zapisovat data pomocí propojení, pak to provádíme v obsluze události OnConnect.

Události serveru

Komponenty serverového soketu mají dva typy připojení: naslouchací připojení a připojení ke klientské aplikaci. Serverové sokety přijímají události v průběhu formování obou těchto připojení.
Těsně před zformováním naslouchacího připojení vzniká událost OnListen. V tomto okamžiku můžeme získat objekt serverového soketu Windows prostřednictvím vlastnosti Socket. Můžeme použít vlastnost SocketHandle k provedení změn na soketu dříve než je otevřen pro naslouchání. Např. můžeme chtít omezit IP adresy serveru použité pro naslouchání a provedeme to v obsluze události OnListen.
Když serverový soket akceptuje požadavek klientského připojení, pak vzniknou následující události:
  1. Serverový soket generuje událost OnGetSocket, předáním madla soketu Windows pro soket, který tvoří koncový bod serverového připojení. Pokud chceme poskytnout svého vlastního přizpůsobeného potomka TServerClientWinSocket, pak jej můžeme vytvořit v obsluze události OnGetSocket a tak bude použit místo TServerClientWinSocket.
  2. Nastává událost OnAccept, předávající nový objekt TServerClientWinSocket obsluze události. To je první okamžik, kdy můžeme použít vlastnost TServerClienWinSocket k získávání informací o koncovém bodu serveru připojení na klienta.
  3. Pokud při vzniku události OnGetThread ServerType je stThreadBlocking, pak chceme poskytnout svého vlastního přizpůsobeného potomka TServerClientThread, tak jej můžeme vytvořit v obsluze události OnGetThread a bude používán namísto TServerClientThread.
  4. Pokud ServerType je stThreadBlocking, pak nastane událost OnThreadStart, když vlákno zahájí provádění. Jestliže chceme provést nějakou inicializaci vlákna nebo provést nějaké volání API soketu Windows před zahájením čtení nebo zápisu vlákna v propojením, pak použijeme obsluhu události OnThreadStart.
  5. Klient kompletuje připojení a vzniká událost OnClientConnect. S neblokujícím serverem můžeme v tento okamžik zahájit čtení nebo zápis v soketovém připojení.

Čtení a zápis v soketovém propojení

Důvod formování soketového propojení na jiný počítač je to, že můžeme číst nebo zapisovat informace v tomto propojení. Jaké informace čteme nebo zapisujeme nebo když jsou čteny nebo zapisovány, závisí na službách přiřazených k soketovému propojení.
Čtení a zápis prostřednictvím soketů může probíhat asynchronně, a tak neblokujeme provádění jiného kódu v naší síťové aplikaci. Toto propojení se nazývá neblokující propojení. Můžeme také formovat blokující propojení, kde naše aplikace čeká na dokončení čtení nebo zápisu před provedením následujícího řádku kódu.

Neblokující propojení

Neblokující propojení čte a zapisuje asynchronně, a tak přenášená data neblokují provádění jiného kódu naší síťové aplikace. K vytvoření neblokujícího propojení: Když propojení je neblokující, pak čtecí a zápisové události informují náš soket, když soket na druhém konci propojení čte nebo zapíše informaci.
Čtecí a zápisové události
Neblokující sokety generují čtecí a zápisové události, které informují náš soket, když je zapotřebí číst nebo zapisovat v propojení. V klientských soketech, můžeme reagovat na tyto oznámení v obsluhách událostí OnRead nebo OnWrite. V serverových soketech, můžeme reagovat na tyto události v obsluhách událostí OnClientRead nebo OnClienWrite.
Objekt soketu Windows přiřazený k soketovému propojení je poskytnut jako parametr obsluhám čtecích nebo zápisových událostí. Tento objekt soketu Windows poskytuje řadu metod umožňujících čtení nebo zápis v propojení. Ke čtení ze soketového propojení, používáme metody ReceiveBuf nebo ReceiveText. Před použitím metody ReceiveBuf, použijeme metodu TReceiveLength k získání počtu slabik, které soket na druhém konci propojení odeslal.
K zápisu na soketové propojení, používáme metody SendBuf, SendStream nebo SendText. Pokud po zápisu informací na soket již dále soketové propojení nepotřebujeme, pak můžeme použít metodu SendStreamThenDrop. Tato metoda uzavírá soketové připojení po zápisu všech informací, které mohou být přečteny z proudu. Pokud použijeme metodu SendStream nebo SendStreamThenDrop, pak objekt proudu není uvolněn. Soket uvolní proud automaticky, když propojení je uzavřeno.
Poznámka: SendStreamThenDrop uzavírá serverové připojení na jistého klienta a ne naslouchací připojení.

Blokující propojení

Když propojení je blokující, pak náš soket musí inicializovat čtení nebo zápis v propojení namísto pasivního čekání na oznámení od soketového propojení. Blokující sokety použijeme když náš konec propojení má na starosti provádění čtení a zápisu.
Pro klientské sokety, nastavíme vlastnost ClientType na ctBlocking k formování blokujícího propojení. V závislosti na tom co naše aplikace provádí, můžeme chtít vytvořit nové běžící vlákno pro čtení nebo zápis, aby naše aplikace mohla pokračovat v provádění kódu v jiném vláknu, zatímco čekáme na dokončení čtení nebo zápisu.
Pro serverové sokety, nastavíme vlastnost ServerType na stThreadBlocking pro formování blokujícího propojení. Protože blokující propojení pozdržuje provádění všeho ostatního kódu během čekání soketu na čtení nebo zápis, komponenty serverových soketů vždy plodí nové vlákno pro každé klientské propojení, když ServerType je stThreadBlocking.
Většina aplikací, které používají blokující připojení jsou zapsány na použití vláken.
Pokud nepoužíváme vlákna, pak můžeme číst nebo zapisovat pomocí TWinSocketStream.
Používání vláken s blokujícími připojeními
Klientské sokety automaticky neplodí nové vlákno, když čteme nebo zapisujeme pomocí blokujícího připojení. Pokud naše klientská aplikace nic nedělá dokud informace není přečtena nebo zapsána, pak je to vyhovující. Jestliže naše aplikace obsahuje uživatelské rozhraní, které musí stále reagovat na uživatele, pak pro čtení a zápis musíme požadovat samostatné vlákno.
Když serverový soket formuje blokující připojení, pak vždy plodí samostatné vlákno pro každé klientské připojení a tak klient nemusí čekat až jiný klient dokončí čtení nebo zápis ve svém připojení. Implicitně serverový soket používá objekt TServerClientThread k implementaci běžícího vlákna pro každé připojení.
Objekt TServerClientThread simuluje události OnClientRead a OnClientWrite, které vynikají při neblokujícím připojení. Nicméně, tyto události vznikají i na naslouchacím soketu, který není vláknově lokální. Pokud klientské požadavky jsou časté, pak můžeme chtít vytvořit svého vlastního potomka TServerClientThread k poskytnutí vláknově bezpečného čtení a zápisu.
Když zapisujeme klientská vlákna nebo zapisujeme serverová vlákna, pak musíme použít TWinSocketStream k provádění aktuálního čtení a zápisu.
Používání TWinSocketStream
Když implementujeme vlákno pro blokující připojení, pak musíme určit, zda soket na druhém konci propojení je určen pro čtení nebo pro zápis. Blokující připojení neoznamují soketu čas čtení nebo zápisu. Abychom je viděli, když připojení je reálné, použijeme objekt TWinSocketStream. TWinSocketStream poskytuje metody pomáhající koordinovat časy čtení a zápisu. Voláním metody WaitForData čekáme, dokud soket na druhém konci připojení neprovede zápis.
Když čtení nebo zápis používá TWinSocketStream, pak čas proudu je překročen, pokud čtení nebo zápis není dokončeno ve specifikovaném časovém intervalu. Výsledkem tohoto překročení času je ukončení čtení nebo zápisu a zrušení připojení.
Poznámka: TWinSocketStream nemůžeme použít pro neblokující připojení.
Zápis klientských vláken
K zápisu vlákna pro klientské připojení, definujeme nový objekt vlákna pomocí dialogového okna New Thread Object. Metoda Execute našeho nového objektu vlákna zpracovává detaily čtení a zápisu vláknového připojení. Je vytvořen objekt TWinSocketStream a je používán ke čtení nebo zápisu. Např.
void __fastcall TMyClientThread::Execute()
{
  // vytvoření TWinSocketStream pro čtení a zápis
  TWinSocketStream *pStream=new TWinSocketStream(ClientSocket1->Socket, 60000);
  try
  {
    //získání a zpracovávání příkazu dokud připojení nebo vlákno není ukončeno
    while (!Terminated && ClientSocket1->Active)
    {
      try
      {
        char buffer[10];
        GetNextRequest(buffer); // GetNextRequest musí být vláknově bezpečná metoda
        // zápis požadavku na server
        pStream->Write(buffer, strlen(buffer) + 1);
        // pokračování v komunikaci (čtení reakce ze serveru)
        ...
      }
      catch (Exception &E)
      {
        if (!E.ClassNameIs("EAbort"))
          Synchronize(HandleThreadException()); // musíme zapsat HandleThreadException
      }
    }
  }
  __finally
  {
    delete pStream;
  }
}
K použití našeho vlákna, vlákno vytvoříme v obsluze události OnConnect.
Zápis serverových vláken
Vlákna pro serverová připojení jsou potomci TServerClientThread. Nemůžeme tedy použít dialogové okno New Thread Object. Musíme je deklarovat ručně takto:
class PACKAGE TMyServerThread : public ScktComp::TServerClientThread
{
  public
    void __fastcall ClientExecute(void);
}
K implementaci tohoto vlákna, přepisujeme metodu ClientExecute (místo metody Execute).
Implementování metody ClientExecute se podobá zápisu metody Execute vlákna pro klientské připojení. Místo použití komponenty klientského soketu kterou umístíme do naší aplikace z Palety komponent, serverově klientské vlákno musí použít objekt TServerClientWinSocket, který je vytvořen když naslouchací soket serveru akceptuje klientské připojení. Je dostupné jako veřejná vlastnost ClientSocket. Dále můžeme použít chráněnou metodu HandleException namísto zápisu své vlastní vláknově bezpečné obsluhy výjimky. Např.
void __fastcall TMyServerThread::ClientExecute()
{
  while (!Terminated && ClientSocket->Connected) // zjištění že připojení je aktivní
  {
    try
    {
      TWinSocketStream *pStream = new TWinSocketStream(ClientSocket, 60000);
      try
      {
        char buffer[10];
        memset(buffer, 0, sizeof(buffer));
        if (pStream->WaitForData(60000)) // Dáme klientu 60 sekund k zahájení zápisu
        {
          if (pStream->Read(buffer, sizeof(buffer) == 0)
            ClientSocket->Close(); // není-li přečteno v 60 sekundách, pak uzavřeme připojení
          // zpracování požadavku
          ...
        }
        else
          ClientSocket->Close();
      }
      __finally
      {
        delete pStream;
      }
    }
    catch (...)
    {
     HandleException();
    }
  }
}
Varování: Serverový soket odkládá používané vlákno. Musíme si být jisti, že metoda ClientExecute provede nezbytné inicializace jako výsledek změny z posledního provedeného vlákna.
K použití našeho vlákna, vytvoříme obsluhu události OnGetThread. Když zapisujeme vlákno, pak nastavíme parametr CreateSuspended na false.

  1. Podívejme se na aplikaci Chat, která umožňuje "rozmlouvat" dvěma uživatelům na různých počítačích. Jedná se opět o již hotovou aplikaci. Můžete si ji stáhnout. Prohlédněte si, jak využívá sokety a vyzkoušejte ji.
10. Práce se sokety