A4B33OSS: Semestrální práce 2

Síťová komunikace, sockety

UPOZORNĚNÍ! zadání semestrální práce bude upřesněno v průběhu semestru.

Cílem této práce je zkusit si některé způsoby implementace algoritmů z oblasti sítí. Při vytváření programů, které komunikují pomocí externího spojení je vždy velký důraz kladen na schopnost detekce chyby a řešení nastalé situace.

Úlohy jsou zaměřené na implementaci algoritmů protokolového zásobníku, zajištění doručení zprávy v síti a směrování.
Použití protokolového zásobníku má velmi užitečnou vlastnost: každá vrstva má na starosti jednoduchou dobře definovanou úlohu a může se spolehnout na korektní řešení problémů ostatními vrstvami. Tuto architekturu je vhodné použít i tam, kde to zadání úlohy jednoznačně nevyžaduje (např. Úloha č. 1).

Dále je při implementaci aplikací síťové komunikace nutné brát ohled na správnou synchronizaci vláken, pokud se jedná o vícevláknovou aplikaci.

Obecné požadavky:

Poznámka k zadání úlohy:
Berte prosím na vědomí, že zadání varianty úlohy nemusí být vyčerpávající a nemusí popisovat všechny situace, které mohou nastat (podobně jako ve skutečném světě, kdy je zákazník málokdy schopen přesně specifikovat co vlastně chce). Je tedy na vašem odborném úsudku dodefinovat si chování aplikace v těchto situacích s ohledem na podstatu zadání úlohy. Pokud tedy budete mít pochybnosti, nebo nejasnosti ve smyslu zadání, neváhejte se obrátit na svého cvičícího a požadujte dodatečná vysvětlení. Pokud zákazníkovi předáte produkt, který nefunguje tak jak on chce, zpravidla vám za něj nezaplatí a nepomůže vám vymlouvat se na to že zadání bylo nejasné.

Poznámka k implementaci úloh:
Záměrem většiny úloh je

  1. naučit se vytvořit aplikaci komunikující po IP socketu a
  2. vyzkoušet si implementaci některých základních metod z oblasti síťové komunikace (které jsou často implementovány uvnitř protokolových zásobníků, v ovladačích, v jádře OS apod.).
Vytvořený socket tak v mnoha úlohách slouží pouze jako komunikační kanál (roura do které z jedné strany posílám data a z druhé strany je čtu), přičemž u tohoto kanálu nepředpokládám žádné zabezpečení apod. Je proto potřeba se povznést nad to, že se např. snažíme implementovat zabezpečený protokol nad TCP kanálem, který už zabezpečení sám o sobě poskytuje - tzn. pokud není řečeno jinak, chovejte se k otevřenému socketu jako ke kanálu který je potenciálně nespolehlivý (může dojít ke ztrátě nebo poškození dat).

Je důrazně doporučeno striktně oddělit soketovou komunikační vrstvu (obsahující vlastní socket, případně generátor chyb v přenášených datech) a vrstvu realizující vlastní úlohu (nejlépe implementací obou částí v samostatných souborech). Inspirujte se např. architekturou ISO/OSI modelu.

Dodatečné instrukce k implementaci:

Hodnocení úloh
Funkční úloha, která je perfektně vypracována (především s ohledem na výše uvedené body a samozdřejmě s ohledem na všechny body zadání dané varianty) je cvičícím ohodnocena maximálním počtem uvedeným na hlavní stránce cvičení. Za prohřešky proti výše uvedeným bodům (či jiné které cvičící uzná za vážné) se snižují udělené body. Pokud aplikace funguje špatně, nebo nefunguje jak je zadáno, nemusí být přijata vůbec, nebo může být vrácena k přepracování.

Testování
Implemetace správné funkce úlohy podle jejího zadání není triviální, zvláště s ohledem na nečekané a málo se vyskytující situace. Proto je vhodné navrhnout pomocné aplikace, skripty, případně postupy a testovací data, která umožní otestovat správnou funkci vašeho systému / aplikace. Testujte tak, abyste postihli situace, které se typicky nevyskytnou při naivním ladění zadáváním vstupu interaktivně přes terminál (špatná implementace se někdy projeví např. při velké frekvenci vstupních data).

Výběr varianty a odevzdávání
Úlohy si vybírejte přes odevzdávací systém. Úlohy můžete řešit samostatně nebo ve dvojicích. Odevzdávání úlohy je možné pouze osobním předvedením cvičícímu za účasti obou členů skupiny.

Po kontrole úlohy cvičícím nahrajte zdrojové soubory na odevzdávací systém, pokud úlohu nenahrajete nedostanete zápočet.
Nahrávejte pouze zdrojové kódy (*.c, *.h) a Makefile zabalené do zip archivu. Pokud tam najdeme i binární soubory, strhneme vám body za zbytečné zaplňování serveru! :)


Varianty úlohy:

1. Navazované spojení

typ socketuTCP (stream)
směr komunikaceobousměrná
počet uzlů1 server, 1 klient

Implementujte potvrzované spojení splňující následující protokolový zásobník (stack) vycházející z referenčního ISO/OSI modelu. Jedná se o příklad synchronizované komunikace, kdy si klient a server vyměňují data a je nutné detekovat identitu účastníků a výpadky dat. Relace se skládá z fáze navazování spojení, kdy dochází k ověřování identity komunikujících stran a synchronizaci vnitřních sekvenčních značek. Navazování spojení se vždy děje tak, že server vyčkává na spojení a klient navazuje spojení.

Pro spojení (z vašeho pohledu "fyzická vrstva") se využije soket typu stream (TCP).

Popis vrstev protokolového zásobníku:

Datová vrstva Datová vrstva použije pro spojenení TCP sokety. Po soketech se mohou vysílat pouze znaky '0'-'9' a 'a'-'f' a znak ','. Každý bajt, který má být přenesen musí být proto převeden do dvojice znaků reprezentujících jeho hexadecimální hodnotu, bajty jsou odděleny znakem ','. Začátek a konec telegramu určují dva znaky ',' vyslané bezprostředně za sebou.

Příklad vyslání telegramu o 6-ti bajtech a hodnotách 0x04, 0x0d, 0x55, 0x32, 0x32, 0x58:
,,04,0d,55,32,32,58,,
Transportní vrstva Transportní vrstva zajišťuje ochranu proti chybám. Na začátek vysílaného telegramu přidává dva bajty:
  • délku dat bez hlavičky a
  • kontrolní součet.
lengthchecksumbyty původního telegramu ...

Kontrolní součet se vypočte jako bitový XOR všech hodnot zasílaného telegramu (tedy ze všeho od vyšší vrstvy). Při příjmu telegramu se tento kontrolní součet otestuje a relační vrstvě se předají pouze telegramy se správným kontrolním součtem, ostatní se zahodí. Uvědomte si, že checksum nechrání hodnotu length.
Relační vrstva Relační vrstva navazuje a udržuje relaci, v rámci relace kontroluje výpadek zpráv. Server i klient mají jméno kterým se identifikují druhé straně. Při výměně datových paketů si obě strany vyměňují číslo sequence, které server inkrementuje při poslání každé zprávy, což umožňuje detekci ztracených telegramů. Sekvenční číslo je 16-bitová hosnota, která se posílá v binární formě, pořadí bytů je patrné z formátu příslušných telegramů.

Relační vrstva používá tři druhy telegramů:
  • T0 - telegram pro navázání spojení a
  • T1 - telegram pro datovou komunikaci
  • T2 - telegram pro ukončení spojení
Formát telegramu T0:
0x10sequence_highsequence_lowjméno [1. byte]jméno [2. byte]jméno [...]

Klient navazuje spojení zasláním telegramu T0 s hodnotou sequence=0 a svým jménem. Server odpovídá telegramem T0 s hodnotou sequence kterou si náhodně vygeneruje pro každé příchozí spojení. Při ztrátě telegramu, tedy když klient neobrdří do 1s odpověď od serveru, tak telegram T0 vyšle znovu.

Formát telegramu T1:
0x20sequence_highsequence_lowdata [1. byte]data (2. byte)data [...]

Server při vysílání datového telegramu sekvenční značku inkrementuje, klient vysílá sekvenční značku kterou přijal od serveru naposled. V relaci se vždy musí střídat telegram od klienta na server s telegramem ze serveru na klienta (tzn. žádná strana nemůže vyslat dva telegramy za sebou bez toho, aby mezi nimi přijala telegram z druhé strany. Při chybě telegramu, čeká klient 1s na odpověď od serveru, a pokud ji nedostane zruší relaci a snaží se ji obnovit vysláním telegramu T0.

Při příjmu chybné sekvenční značky, nebo při příjmu telegramu T0 je relace zrušena - to znamená, že je potřeba, aby došlo k výměně 2 telegramů T0 s novými sekvenčními značkami (v tomto případě se socket fyzicky neuzavírá, ten je součástí datové vrstvy).

Formát telegramu T2:
0x30sequence_highsequence_low

Server i klient se může rozhodnout relaci zrušit. Klient posílá telegram T2 a server na něj odpovídá také telegramem T2. Pokud klient neobdrží odpověď do 1s, pak vyšle telegram T2 ještě jednou, počká 1s, zda dojde odpověď a socket uzavře i v případě, že by odpověď nedošla.
Pokud chce ukončit relaci server, pak vyčká na datový telegram od klienta a jako odpověď vyšle telegram T2. Klient při příjmu telegramu T2 vygeneruje jako odpověď také telegram T2. Pokud server nedostane do 1s odpověď od klienta, vyšle telegram T2 ještě jednou, počká 1s, zda přijde odpověď a socket uzavře i v případě, že by odpověď nedošla.

Příklad navázání spojení, datové komunikace a ukončení spojení:
KlientServer
0x100x000x00KLIENT1
0x100x550x99SERVER5
0x200x550x99ZPRAVA1
0x200x550x9AZPRAVA5
0x200x550x9AZPRAVA2
0x300x550x9B
0x300x550x9B
Aplikační vrstva Aplikační vrstva čte ze standartního vstupu (stdin) po řádcích a každý řádek předává jako datový telegram relační vrstvě. Data přijatatá z relační vrstvy vypisuje na standartní výstup (stdout). Vstupní řádky mohou být z implementačních důvodů oříznuty na zvolenou maximální délku (min. 32 znaků).

Klient posílá telegramy pouze pokud má k dispozici vstupní data, v opačném případě čeká.
Server odpovídá na příchozí telegram bezprostředně. Pokud má k dispozici vstupní data ze stdin, pošle tato data, v opačném případě odpovídá telegramem s nulovou délkou dat.

Jednotlivé vrsty důsledně oddělte! Důsledně = minimálně do samostatných funkcí, lépe však do samostatných souborů. Porušení tohoto požadavku může být důvodem pro nepřijetí úlohy.
Dbejte na to, aby každá vstva dělala jen to co má - nic víc, nic míň!

Klient se spouští s 3 povinnými parametry: IP_serveru, port_serveru, jmeno.
Server se spouští se 2 povinnými parametry: port_serveru, jmeno.

Server přijímá pouze jedno příchozí spojení - po připojení klienta všechny ostatní klienty odmítá až do uzavření spojení, potom čeká na dalšího klienta. Klient se připojuje ke spuštěnému serveru, pokud server neběží klient se ukončí s chybovým hlášením.

Jak server, tak klient čtou data ze standardního vstupu oddělená znakem nový_řádek (EOL = 0x0A) a pokud je to možné data odesílá druhé straně, jinak čeká. Je zřejmé, že aplikace (jak server, tak klient) čeká na 3 typy událostí (standartní vstup, socket, časové omezení), proto může implementace potřebovat dvě až tři vlákna, nebo použít funkci select.
V případě že je vstup na klientu ukončen (konec souboru - EOF), aplikace klienta vyšle telegram T2 a po potvrzení uzavírá socket (v případě chybného doručení telegramů se po 1s telegram T2 jednou opakuje a pak se socket uzavře i bez potvrzení, viz výše), server však běží dál a čeká na další spojení. V případě ukončení vstupu na straně serveru pokud je navázána relace s klientem vyšle server telegram T2 jako odpověď na následující datový telegram od klienta. Způsob uzavření a ošetření nedoručení telegramu T2 je pak již stejný, jako u uzavírání socketu klientem.

Klient i server musí být odolní na chybu ve spojení, tzn. musí se správně zachovat při chybě kontrolního součtu, při chybě datové vrstvy a při chybě relační vrstvy. Telegram s chybou je vždy zahozen. V tomto případě se nejedná o potvrzované spojení, takže zahozená data se již opakovaně nevysílají, s vypadlým paketem se musí vypořádat vyšší vrstva.
Protože je komunikace realizována TCP socketem, je nutné chyby komunikačního kanálu simulovat. Simulaci chyb doporučujeme implementovat pod datovou vrstvou (tzn. ve "fyzické vrstvě" - bezprostředně před vysláním dat do socketu, nebo bezprostředně po jejich přijetí) s vhodně zvolenou pravděpodobností. Pro účely ladění je vhodné implementovat přepínač, kterým se bude simulace chyb aktivovat, případně nastavovat pravděpodobnost chyb.

2. Potvrzované spojení

typ socketuUDP (datagram)
směr komunikacejednosměrná
počet uzlů1 server, 1 klient

Implementujte potvrzované spojení využívající následující algoritmus Selective Repeat ARQ pro N=8.

Klient čte data ze stdin a posílá datové pakety serveru přes nespolehlivý kanál. Server potvrzuje přijetí jednotlivých paketů. Data která nejsou potvrzena v daném časovém intervalu (nebo jsou "potvrzena" jako chybná) posílá klient znovu tak, aby na výstupu serveru byla stejná data jako na vstupu klienta.

Aplikace klienta na začátku otevře soketové připojení k serveru podle zadaných argumentů (IP a číslo portu) a zároveň vysílá zprávy, které čte ze standardního vstupu (tedy 1. řádka vstupu = 1 zpráva = 1 datový paket). Zprávy jsou odděleny znakem Nová_řádka (EOL = 0x0A) a oříznuty na maximální délku 255 znaků (zjednodušení implementace). Při uzavření vstupu (EOF) se ukončí soketové spojení a aplikace klienta, server zůstává běžet a čeká na připojení dalšího klienta. Server je možné ukončovat signálem SIGINT (CTRL+C).
Klient před každou zprávu přidá hlavičku, která bude obsahovat dva bajty 16-bitového sekvenčního čísla následované jedním bajtem obsahujícím délku zprávy a na konec zprávy přidá 1-bytový kontrolní součet získaný jako XOR všech bytů původní zprávy.

sequence_high sequence_low length data (length bytes) checksum

Pro posílání zpráv využijte soketu typu Datagram (UDP), který bude realizovat vaší fyzickou a datovou vrstvu. V rámci této vrstvy (bezprostředně před odesláním dat do UDP socketu) implementujte mechanismus simulace chyb, který se zadanou pravděpodobností změní některý byte v datagramu, nebo zahodí (nepošle) celý datagram.

Aplikace serveru přijímá datagramy a vysílá zpět potvrzení o jejich přijetí. Přijaté zprávy zobrazuje (ve správném pořadí!) na standartní výstup.
Obdobně jako klient zahazuje posílané zpraávy, zahazuje server se zadanou pravděpodobností potvrzení.

Klient se spouští s 2 parametry: IP_serveru, port_serveru.
Server se spouští s 1 parametrem: port_serveru.

Detaily algoritmu Selective Repeat ARQ

Algoritmus pracuje s pohyblivým oknem nad posloupností odesílaných datagramů:

... D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 D14 D15 ...
potvrzená dataodeslaná data čekající na potvrzení (okno)data čekající k odeslání
Data která se dostanou do odesílacího okna se mohou odeslat. V případě, že je přijato potvrzení o doručení prvního datagramu v okně, okno se posouvá o jeden datagram dál (což umožní odeslání dalšího datagramu).
Tento mechanismus umožňuje, narozdíl od čekání na potvrzení každého datagramu, vyšší datovou propustnost spojení.
Klient si musí pamatovat čas odeslání každého datagramu. V případě, že potvrzení o doručení není obdrženo v definovaném časovém intervalu, je nutné odeslat daný datagram znovu.

Server musí mít obdobnou strukturu jako klient, která mu umožní správně seřadit příchozí datagramy tak, aby byly správně vypsány na výstup.

Java applet s animací funkce algoritmu

3. Poštovní server

typ socketuTCP (stream)
směr komunikaceobousměrná
počet uzlů1 server, více klientů

V této úloze máte za úkol naprogramovat dva samostatné programy klient a server. Po spuštění se klient podle zadaných údajů připojí na server a přihlásí se svým jménem. Připojený klient může zasílat zprávy jiným klientům podle jména. Zprávy jsou čteny ze standardního vstupu klienta ve tvaru jmeno:zpráva (ukončené znakem Nová_řádka (EOL=0x0A)). Načtené zprávy jsou ihned odesílány na server.

Server zprávy přeposílá přihlášeným klientům bez zbytečného prodlení. Pokud je zpráva určena pro klienta, který ještě nikdy nebyl přihlášen, server zprávu zahodí a informuje o tom odesílatele. Pokud není adresát v daném okamžiku dostupný, server zprávu uloží a informuje o tom odesílatele. Server si pro každého klienta může pamatovat pouze omezený počet zpráv (např. 16), další zprávy tedy může zahodit (musí o tom opět informovat klienta). Maximální počet různých klientů, kteří se mohou přihlásit, může být omezen (zjednodušení implementace). Server musí umožnit současné přihlášení alespoň 32 klientů.
Pokud se klient přihlásí, jsou mu serverem zaslány všechny uložené zprávy. Při ukončení klienta je nutné poslat serveru zprávu oznamující odpojení klienta.

Uvažujte, že spojení mezi klientem a serverem je nespolehlivé, tzn. jednotlivé zprávy se mohou ztrácet (jedná se o umělé ztížení úlohy, tzn. neberte v úvahu že TCP protokol vám garantuje že jsou data doručena nebo se dozvíte o chybě). Uvažujte pouze chybu, kdy se ztratí celá zpráva. Poruchovost komunikačního kanálu simulujte ve vhodné vrstvě, která se zadanou pravděpodobností zprávu zahodí (nepošle). Z ladicích důvodů je vhodné pokud je možné chybovost zapnout zvláštním přepínačem zadaným jako argument aplikace. V případě, že dojde ke ztracení a opětovnému přeposlání zprávy, je přípustné že klient zprávu obdrží se zpožděním.
Uživatelé by měli mít garanci doručení zprávy, tzn. že je nutné implementovat mechanismus potvrzování a opětovného posílání nepotvrzených zpráv.

Pro komunikaci mezi klientem a serverem použijte soket typu stream (TCP). Komunikační protokol si navrhněte sami tak, aby umožňoval všechny požadované funkce. Nezapomeňte, že na přijímací straně musíte být schopni rozpoznat začátek nové zprávy.

Klient se spouští se 3 povinnými parametry: IP_serveru, port_serveru, jmeno.
Server se spouští s 1 povinným parametrem: port_serveru.

4. Dynamické směrování

UPOZORNĚNÍ: řešení úlohy může být implementačně náročnější oproti ostatním úlohám. Zadání je proto doporučeno zkušenějším programátorům. Konzultujte detaily zadání s vaším cvičícím s ohledem na možná zjednodušení.

typ socketuUDP (datagram)
směr komunikaceobousměrná
počet uzlůvíce uzlů bez rozlišení server/klient

V této úloze máte za úkol vytvořit aplikaci, která se chová jako klient i server současně. Každá běžící instance aplikace (uzel) přijímá připojení od ostatních uzlů, kteří se snaží připojit tak, aby vytvořily komunikační síť umožňující přenášet zprávy mezi libovolnými dvěma uzly. V této síti není žádný centrální prvek, všechny uzly jsou rovnocenné.


Každý uzel v síti je identifikován svým unikátním číslem ID, což je 1-bytové číslo v rozsahu 1-255.
Struktura síťě je dána konfiguračním souborem, což je textový soubor, který obsahuje 2 typy řádků:

Struktura řádku node je node <ID> <port> <ip_address> kde ID je číslo uzlu, port> je číslo portu na kterém uzel běží a ip_address je nepovinná adresa která je nutná pouze pro případ kdy uzly běží na různých strojích. Struktura řádku link je link <ID-client> <ID-server> kde ID-client je ID uzlu který navazuje spojení a ID-server je ID uzlu ke kterému se připojuje.

Každá instance aplikace se spouští s jedním povinným parametrem, což je ID uzlu, podle kterého uzel pozná které řádky konfiguračního souboru se uzlu týkají.
Každý uzel musí umožnit alespoň 4 připojení jako server a 4 připojení jako klient. Všechna navázaná spojení jsou obousměrná a již se nerozlišuje kdo je server nebo klient.

Program využívá některý ze známých algoritmů směrování, nebo váš vlastní přístup. Program čte ze standardního vstupu zprávy ve formátu ID:zpráva, kde ID je ID cílového uzlu. Zpráva je textový řetězec, který je z důvodu implementace možné oříznout na definovanou maximální délku (např. 128 znaků). Vaším úkolem je navrhnout komunikační protokol mezi uzly, který umožní předávání vlastních zpráv a pomocných informací pro účely směrování.

Aby se projevily schopnosti dynamického routování je třeba aby síť fungovala i když se jednotlivé uzly za běhu libovolně spouští nebo ukončují. To v praxi znamená, že pokud je cílový uzel spojení nedostupný, je třeba připojení zkoušet v pravidelných intervalech (např. 1 sekunda) znovu pro případ, že se cílový uzel spustí.

Pro komunikaci mezi uzly použijte soket typu Datagram (UDP). To vás mimo jiné oprostí od potřeby hldedat začátky a konce datagramů posílaných mezi uzly a navíc vám může stačit jeden soket pro komunikaci se všemi připojenými uzly.


Systém by měl zaručit doručení zprávy, pokud existuje cesta mezi odesílatelem a příjemcem. Jedna zpráva by neměla být za žádných okolností doručena vícekrát (pokud bude např. poslána více různými cestami). Zprávy které nelze doručit se zahodí. Rovněž je povoleno zahodit zprávy, které se nevejdou do buferu programu (v případě přetížení sítě).
Pokud zprávu není možné doručit (neexistuje cesta), systém by měl být schopen upozornit odesílatele na nedoručitelnost zprávy (pokud to vaše implementace neumožní, úloha je odezdatelná, ovšem se srážkou bodů).

Pro načítání konfiguračního souboru jsme pro vás připravili funkci parseRouteConfiguration: int parseRouteConfiguration(const char * fileName, int localId, int * connectionCount, TConnection * connections) kterou najdete v souboru route_cfg_parser.zip, kde je kromě popisu parametrů v .h souboru i ukázka programu která funkci používá a ukázkový cfg soubor.
Funkce ze souboru načte seznam spojení, která je potřeba otevřít z tohoto uzlu s ID uvedeným v parametru localId. Ke každému spojení dostanete ID, port a případně IP adresu ve struktuře typedef struct {
  int id;
  int port;
  char ip_address[IP_ADDRESS_MAX_LENGTH];
} TConnection;
Kód je možné přeložit jak v C tak v C++.

Pro testování vaší aplikace si připravte dostatečně rozsáhlý a zajímavý soubor s konfigurací sítě (lépe více konfigurací s různými vlastnostmi a topologiemi).

5. Záložní server

typ socketuTCP (stream)
směr komunikaceobousměrná
počet uzlů2 servery, více klientů

Navrhněte systém, ve kterém budou dva servery totožné servery, umožňující ukládání a zpětné čtení dat. Oba servery jsou vzájemně synchronizované tak, aby poskytovaly stejná data bez ohledu na to ke kterému z nich se klient připojí. V případě nekonzistence dat (různá data na každém ze serverů) by měla být platná ta která byla zapsána nejpozději. Klientské aplikace jsou napsány tak, že se pokouší připojit k jednomu ze serverů a v případě jeho nedostupnosti zkusí připojení k serveru druhému. K libovolnému ze serverů se může zároveň připojit více klientů.

Klient čte ze standartního vstupu dva typy příkazů: set pro ukládání dat a get pro čtení dat ze serveru. Formát příkazů je set <ID> <DATA>
get <ID>
kde <ID> je číslo v rozsahu 1-32 a <DATA> je libovolný textový řetězec (první dvě mezery v příkazu set tedy oddělují příkaz a argument ID od zbytku dat, ostatní mezery až do konce jsou součástí dat). Jednotlivé příkazy jsou odděleny znakem konce řádku (EOL = 0x0A).
Příkaz set odešle data na server, který je pod příslušným ID uloží, příkaz get přečte data ze serveru a vypíše je na standartní výstup. Data můžete ukládat v maximální délce 255 znaků, tzn. pokud uživatel zadá delší řetězec, můžete nadbytečné znaky zahodit.
Uživatel by měl být upozorněn ve všech případech, že jeho data nelze uložit, tj. např. v případě že server ke kterému je připojen je nečekaně nedostupný.
Klientská aplikace čte příkazy dokud přicházejí na standartní vstup, v případě ukončení vstupu (konec souboru), klient končí. V případě, že dojde k ukončení serveru, ke kterému je klient připojen (včetně nečekaných ukončení či ztráty spojení bez korektního uzavření komunikace), klient by se měl pokusit připojit ke druhému serveru (a dokončit operaci, která se z důvodu přerušeného spojení nezdařila). Pokud není dostupný žádný ze serverů, klient se může ukončit s chybou.

Servery si musí data ukládat na disk pro případ, že se ukončí oba servery - uložená data se v tomto případě nesmí ztratit. Dále byste měli zajistit, že klient vždy (pokud je to možné) dostane poslední uložená data (bez ohledu na to který server přijal poslední aktualizaci), tj. že poslední uložená data nebudou za žádných okolností přepsána jejich starší verzí.

Komunikace mezi klientem i mezi servery je postavena na TCP socketech. Příslušné komunikační protokoly si navrhněte podle potřeby sami.
Pokud to budete potřebovat, můžete předpokládat, že oba servery mají synchronizované systémové hodiny.

Server se spouští se třemi parametry: server <port> <second-server-port> <second-server-ip-address> (oba servery musí vědět adresu a port druhého serveru)
Klient se spouští se čtyřmi parametry: client <server-port> <server-ip-address> <backup-server-port> <backup-server-ip-address>

6. varianta není vypsána

7. FTP server/klient

typ socketuTCP (stream)
směr komunikaceobousměrná
počet uzlů1 server, více klientů

Implementujte FTP server a klient podle platné specifikace, pracujíci v aktivním i pasivním módu.

Požadavky: