Rezervační systém
Meziprocesová komunikace

Obsah

Aplikace pro rezervaci místenek v autobusových spojích je ze své podstaty typu server-klient, tedy má centrální úložiště dat, ke kterému přistupují operátoři na jednotlivých terminálech. Z toho plyne, že implementace musí řešit otázku komunikace mezi procesy. Operační systémy k tomu poskytují různé prostředky s různými vlastnostmi. Zmíníme alespoň některé:

V rámci projektu byly implementovány třídy pro komunikaci pomocí protokolu TCP a pomocí výměny zpráv Windows mezi okny.

Od místní databáze ke vzdálené

Databáze je reprezentována instancí třídy CMemoryDb, požadavky se vyřizují přes rozhraní CQuery. Případné jiné implementace databáze v budoucnu musí používat stejné rozhraní, vnitřní struktura ale může být libovolná.

Terminálová část je vybavena vnitřní logikou, ale veškerou svou činnost vykonává pomocí elementárních příkazů, které poskytuje databázové rozhraní. V konfiguraci server-klient, kdy terminálová část je v jiném procesu (případně na zcela jiném počítači) je potřeba zajistit přenos požadavků z klienta na serverovou část a vrátit požadovaný výsledek dotazu.

Parametry dotazu stejně jako jeho výsledky jsou převedeny do binární podoby, která je nezávislá na způsobu přenosu informací mezi procesy. Převod do tohoto binárního formátu a zpět zajišťuje dvojice tříd - CClientQuery na klientské straně a CServerQuery na straně serveru. První z nich implementuje rozhraní CQuery, druhá pak zajišťuje volání odpovídající metody téhož rozhraní implementované databáze, což znázorňuje i následující obrázek.

RemoteDb.png
Přímé připojení databáze vs. databáze v jiném procesu.

Dvojice právě zmíněných sesterských tříd odstiňuje zbytek aplikací od detailů implementace meziprocesové komunikace (IPC) i odlišností vyplývajících z rozličných vlastností jednotlivých druhů transportu. Z hlediska klientského rozhraní je přenos dat mezi procesy naprosto transparentní. Výhodou je mj. skutečnost, že vlastní serverová aplikace může implementovat i přímo připojeného klienta, minimálně však je takové uspořádání výhodné při ladění a testování funkčnosti jak vlastní databáze, tak klientské části - v rámci jednoho procesu je vše přirozeně jednodušší.

Realizace komunikace

Již bylo zmíněno výše, že byly realizovány dva různé druhy transportu mezi procesy - zprávy Windows mezi okny a komunikace po TCP protokolu.

TCP

Propojení s výše zmíněnými třídami CClientQuery a CServerQuery (které abstahují od konkrétního typu transportu) je zajištěno pomocí rozhraní CClientConnection na klientské straně a CServerConnection na straně serveru. Pro TCP protokol jsou připraveny implementace těchto rozhraní CClientTcp a CServerTcp, obě třídy zároveň dědí funkcionalitu přenosu dat od CSocketEndpoint.

Ipc.png
Volání databázového dotazu pomocí TCP.

Situace se schematicky snaží postihnout obrázek. Informace od CClientQuery k CClientTcp se přenášejí pomocí třídy CCommand (což je vlastně CBlob s binárními daty dotazu a stavové slovo), od CServerTcp k CServerQuery ve formě CBlob, návratové hodnoty pak stejně opačným směrem. Třídy CClientQuery a CServerQuery pracují na aplikační vrstvě, třídy CClientTcp a CServerTcp na vrstvě relační (rozhraní CClientConnection resp. CServerConnection) a transportní (CSocketEndpoint).

Klientů může být přirozeně připojeno více najednou, celá serverová část pracuje ve vícevláknovém režimu (k tomu podrobněji u CServerTcp).

Zprávy Windows

V případě zpráv operačního systému Windows je situace jen mírně odlišná od právě popsaného případu. Třídy CClientMsg a CServerMsg přirozeně implementují tatáž rozhraní, funkcionalitu pro vytvoření okna, posílání a příjem zpráv dědí od CMsgEndpoint. K posílání (i většího rozsahu) binárních dat mezi procesy se využívá zprávy WM_COPYDATA, podrobněji se k problematice vyjadřuje popis třídy CServerMsg.

Z uvedeného plyne, že není třeba opakovat obrázek, který by se lišil pouze pojmenováním několika tříd.

Více druhů transportu na straně serveru

Serverová aplikace může přijímat požadavky klientů z více zdrojů současně. Může mít i více instancí CServerTcp (s nasloucháním na různých IP adresách a portech) a zároveň i instanci CServerMsg (zde více instancí pravděpodobně nemá opodstatnění, i když je lze realizovat). Potom požadavky z rozhraní CServerConnection lze všechny směřovat na jednu instanci třídy CServerQuery, respektive její statickou metodu CServerQuery::ServerCall.

MultiIpc.png
Příjem klentských spojení z více zdrojů.

Teoreticky je možné mít více instancí CServerQuery, ale nepřináší to žádnou přidanou hodnotu, neboť volání dotazů databázového rozhraní tak jako tak probíhá paralelně ve více vláknech.

Formát binárních dat

Parametry volání funkcí databázového rozhraní musí být pro přenos mezi procesy převedeny do binární podoby v rámci CCommand respektive CBlob. To samé platí pro přenos návratových hodnot metod a parametrů opačným směrem.

Při volání funkce (statické metody třídy) se jednotlivé parametry ukládají za sebou dle těchto pravidel:

Pro návratové hodnoty platí prakticky totéž s tím, že místo čísla metody má první položka typu unsigned význam návratové hodnoty metody (ať již je to číselná nebo logická hodnota).

Je přirozené, že jakýkoliv zásah do struktury rozhraní CQuery se okamžitě promítá do implementace CClientQuery a CServerQuery při kódování/dekódování parametrů.

Zpracování chyb

Při vzdáleném volání databázových dotazů může docházet k chybám, jejich příčiny mohou být značně různorodé:

Chyby výše zmíněných typů jsou detekovány ve třídě CClientConnection a pomocí stavového slova CCommand předány do CClientQuery. Tam je podle druhu chyby vyvolána výjimka typu std::runtime_error s odpovídajícím hlášením. Zachycení výjimky a notifikace uživatele je úkolem uživatelského rozhraní.