Cieľom projektu bolo vytvoriť klienta pre chatovací server na komunikáciu pomocou protokolu UDP a TCP. Program umožňuje výmenu správ medzi klientom a serverom a podporuje rôzne typy správ, ako sú autentifikácia, potvrdenia, odpovede, chybové správy a ukončenie spojenia.
- IPK Projekt 1: Klient pre chatovací server
Aplikácia využíva protokoly TCP (Transmission Control Protocol) a UDP (User Datagram Protocol) na komunikáciu s serverom. TCP zabezpečuje spoľahlivé spojenie s garanciou doručenia a poradia prenášaných správ, zatiaľ čo UDP poskytuje jednoduchý prenos bez spojenia s nižšou režiou, ale bez záruky doručenia alebo poradia. Tieto protokoly sú využívané pre vytvorenie, prenos a ukončenie spojenia medzi klientom a serverom, čo umožňuje efektívnu a spoľahlivú komunikáciu medzi nimi. Vo funkcionalite programu sa správy, ktoré sú reprezentované triedami správ, prevádzajú na pole bytov, aby mohli byť odoslané pomocou príslušného transportného protokolu. V prípade UDP je tento proces vykonávaný priamo, zatiaľ čo v prípade TCP je najprv vytvorené spojenie s serverom prostredníctvom TcpClient, po ktorom sú správy odosielané a prijímané.
IPK_Project1/
├── Tcp/
│ ├── TcpMessage.cs
│ └── TcpTransport.cs
├── Udp/
│ ├── MessageType.cs
│ ├── UdpMessage.cs
│ └── UdpTransport.cs
├── ErrorType.cs
├── Program.cs
└── User.cs
Program.cs:
Predstavuje vstupný bod programu. Vo funkcii ParseArguments sa spracujú a skontrolujú argumenty a následne sa na základe zadaného transportu začne vykonávať tcp alebo udp vetva. Obidva transporty sú rozdelené na dve vlákna, ktoré pracujú paralelne - ProcessUserInputTcp, ProcessReceivedMessagesTcp resp. ProcessUserInputUdp, ProcessReceivedMessagesUdp. Prvé vlákno čaká na vstup od uživateľa a vo funkcii ExecuteCommand, ktorá je spoločná pre obidva transporty, vykonáva zadané príkazy. Druhé vlákno čaká na prichádzajúce správy od serveru. Ak sa v akomkoľvek stave obdrží správa typu error alebo neočkávaný typ správy, pošle sa bye a program sa ukončí. Program sa tiež ukončí ak na vstup príde EOF pri špecifikovaní súboru, z ktorého sa majú čítať príkazy alebo ak uživateľ stlačí ctrl-c.
User.cs:
Trieda obsahuje metódu ValidateAuth na overenie argumentov príkazu auth a metódu WriteHelpMessage na výpis nápovedy.
ErrorType.cs:
Definuje enum ErrorType, ktorý obsahuje typy chýb, ktoré môžu nastať v rámci implementácie projektu.
TcpMessage.cs:
Určuje triedu TcpMessage, ktorá predstavuje TCP správy. Obsahuje typ správy, ktorý má každá správa, a ďalej vlastnosti, ktoré sú pridelené podla typu správy - informácie o odpovedi, zobrazovacie meno osoby a obsah správy.
TcpTransport.cs:
Zavádza triedu TcpTransport, ktorá spravuje komunikáciu pomocou TCP, kde sa v TcpClientInit vytvára spojenie so serverom. Metóda ReceiveMessage prijíma správu zo servera a vytvára inštanciu triedy TcpMessage. V metóde CheckMessageSyntax sa skontroluje syntax prijatej správy a na základe jej typu potom priradí hodnoty do potrebných atributov.
UdpMessage.cs:
V subore sa nachádzajú triedy a rozhranie, ktoré predstavujú rôzne typy správ používaných pri komunikácii. Patrí sem rozhranie IMessage, ktoré určuje spoločné vlastnosti správ (typ a id) a konkrétne triedy pre špecifické typy správ, ako sú autentifikácia (AuthMessage), potvrdenia (ConfirmMessage), chybové správy (ErrorMessage), atď.
MessageType.cs:
Obsahuje enum MessageType, ktorý definuje všetky typy používaných správ, kde každý typ je reprezentovaný jedným bytom.
UdpTransport.cs:
Zavádza triedu UdpTransport, ktorá spravuje komunikáciu pomocou UDP a riadi výmenu správ medzi klientom a serverom.
V triede sa nachádza metóda MessageToByteArray, ktorá prevádza správu na odoslanie na bytové pole a metóda ByteArrayToMessage, ktorá na základe prijatých bytov vráti inštanciu konkrétnej správy.
Spojenie so serverom sa nadväzuje v metóde UdpClientInit po zadaní príkazu auth. Odosielanie a asynchrónne prijímanie správ a overenie, že správa bola serverom prijatá zaisťujú metódy SendMessage a ReceiveMessage.
Udp komunikácia vyžaduje aby bola každá prijatá správa potvrdená odoslaním správy typu confirm druhou stranou. Po odoslaní správy v SendMessage sa teda čaká (v základe maximálne trikrát 250ms) na potvrdzujúcu správu od serveru a ak nepríde, odošle sa správa znova.
Uživateľ môže pri spúštaní programu zadať, ako dlho sa má na confirm čakať a kolkokrát má program správu skúsiť odoslať znova (premenná MaxRetries) ak confirm neobdrží.
Po uplynutí času na prijatie sa program ukončí s chybou. Na každú prijatú správu, ktorá nie je typu confirm sa v metóde SendConfirmMessage odošle potvrdzujúca správa.
Ak bola v akomkoľvek stave prijatá správa typu error, spojenie so serverom sa v ReceivedErrorMessage preruší a program sa ukončí.
V tejto časti sa zameriam na podrobnú analýzu a vysvetlenie niektorých kľúčových častí programu.
public async Task SendMessage(IMessage message) {
WaitingForConfirm = true;
IncrementId(message);
MessageToSend = message;
byte[] messageToSend = MessageToByteArray(message);
for (int i = 0; i < MaxRetries; i++) {
if (UdpClient.Client.Connected)
await UdpClient.SendAsync(messageToSend, messageToSend.Length);
else
await UdpClient.SendAsync(messageToSend, messageToSend.Length, EndPoint);
await Task.Delay(Delay);
if (!WaitingForConfirm) {
return; //received confirm message
}
}
await Console.Error.WriteLineAsync("ERR: No response from server.");
Environment.Exit((int)ErrorType.NoResponse);
}Metóda SendMessage v udp protokole je hlavnou funkciou na odosielanie správ a kontrolu či bola prijatá confirm správa. Po nastavení flagu WaitingForConfirm a prevedení správy na bytové pole sa spúšťa cyklus, ktorý odosiela správu serveru. Ak ide o prvú správu a klient ešte nie je pripojený uvádza sa aj EndPoint kam má správu zaslať. Následne sa čaká 250ms (ak uživateľ nešpecifikoval inak). Ak sa počas tohoto čakania v druhom vlákne prijíme confirm správa, nastaví sa WaitingForConfirm flag na false a metóda sa ukončí. V prípade, že program potvrdzujúcu správu neprijíme, sa správa odošle znova. Ak sa nepríjme ani po MaxRetries, program sa ukončí s chybou.
Queue<string?> commandQueue = new Queue<string?>();
udpTransport.StatusChanged += async (_, args) =>
{
if (!args.WaitingForReply && !args.WaitingForConfirm)
{
while (commandQueue.Count != 0)
{
if(udpTransport.WaitingForReply || udpTransport.WaitingForConfirm)
break;
string? input = commandQueue.Dequeue();
await ExecuteCommand(input, udpTransport, null, true);
}
}
};if (commandQueue.Count > 0 || udpTransport.WaitingForReply || udpTransport.WaitingForConfirm && command != "/rename" && command != "/help")
{
commandQueue.Enqueue(input);
continue;
}Ak uživateľ zadá príkaz, na ktorý sa očakáva od serveru správa typu reply (v udp aj confirm) a pred tým ako túto správu obdrží, zadá ďalšie príkazy, sa tieto príkazy vkladajú do rady commandQueue. Event handler StatusChanged sa potom zavolá vždy, keď sa očakávaná správa prijme (premenná WaitingForReply a WaitingForConfirm sa nastaví na false) a vykoná všetky príkazy, ktoré boli vložené do rady.
Testovanie prebiehalo primárne pomocou nástroja Wireshark a referenčného servera. Okrem toho som využil súbory obsahujúce simulované vstupné príkazy, ktoré som presmeroval na vstup programu. Dodatočné testy boli vykonané pomocou vlastného Python skriptu. Tento prístup mi umožnil overiť správne fungovanie rôznych častí kódu a ich interakciu s klientmi v rôznych scenároch. Analyzovanie sieťovej komunikácie pomocou Wiresharku mi poskytlo podrobnejší pohľad na prenášané správy a ich formát, čo bolo kľúčové pre ladenie a zlepšenie spoľahlivosti serverovej aplikácie. Presmerovanie súboru na vstup mi umožnilo otestovať aj to, že program správne čaká na príchod odpovede od servera, ako je napríklad správa reply alebo confirm, a v tom čase nevykonáva ďalšie príkazy.
Účel: Test overuje správne fungovanie autentifikácie na serveri a následné zaslanie správy po úspešnom prihlásení.
Akcie:
- Odoslanie autentifikačného požiadavku na server s použitím uvedeného používateľského mena a tajného kľúča.
- Odoslanie správy "hi" na server po úspešnom prihlásení.
Očakávané výsledky:
- Potvrdenie úspešnej autentifikácie serverom zaslaním reply
OKa pripojenie uživateľa do predvoleného kánalu. - Správa "hi" odoslaná na server a potvrdenie prijatia tejto správy serverom.
Vstup
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
hi
Účel: Overenie reakcie servera na autentifikačný požiadavok s nesprávnym tajným kľúčom a overenie inkrementácie id.
Akcie:
- Odoslanie autentifikačného požiadavku na server s použitím uvedeného používateľského mena a nesprávneho tajného kľúča.
- Opatovné odoslanie autentifikačného požiadavku s použitím uvedeného používateľského mena a správneho tajného kľúča.
Očakávané výsledky:
- Odpoveď servera reply
NOK, keďže autentifikačné údaje sú nesprávne. - Druhá autentifikačná správa potvrdená, zaslanie reply
OKzo servera a pripojenie uživateľa do predvoleného kánalu.
Vstup
/auth xpasti00 badSecret AP
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
Účel: Overenie správnosti fungovania funkcie premenovania užívateľa na serveri.
Akcie:
- Odoslanie prvej správy s menom "AP"
- Zadanie príkazu na premenovanie
- Odoslanie druhej správy s novým menom "Adam"
Očakávané výsledky:
- Správa "hi" odoslaná s menom "AP" na server a potvrdenie servera o prijatí tejto správy.
- Druhá správa odoslaná s menom "Adam" na server a potvrdenie servera o prijatí tejto správy.
Vstup
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
hi
/rename Adam
i was renamed
Účel: Overenie správnosti fungovania funkcie zmeny kanálu.
Akcie:
- Zmena kanálu na "discord.verified-1"
- Odoslanie správy "hello from verified-1"
Očakávané výsledky:
- Potvrdenie požiadavku o pripojenie, odpoveď servera reply
OKna zmenu kanálu a pripojenie uživateľa do nového kanálu. - Správa "hello from verified-1" odoslaná do nového kanálu a potvrdená serverom.
Vstup
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
/join discord.verified-1
hello from verified-1
Účel: Tento test overuje správne fungovanie delayu na prijatie správy zo servera, keď je nastavený veľmi krátky časový limit.
Akcie:
- Spustenie programu s argumentom
-d 1 - Pokus o zaslanie správy
Očakávané výsledky:
- Server nestihne do 1ms odoslať potvrdzujúcu správu a teda snaha o opätovné zaslanie správy.
- Po tom ako program trikrát nepríjme potvrdzujúcu spravú sa ukončí s chybou
Argumenty
-t udp -s anton5.fit.vutbr.cz -d 1
Vstup
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
hi
Tento test bol vykanný použitím vlastného Python skriptu, ktorý simuloval chybovú správu od servera
Účel: Test overuje správne fungovanie programu pri prijatí chybovej správy od servera.
Akcie:
- Simulácia odoslania chybovej správy od servera
Očakávané výsledky:
- Potvrdenie prijatia chybovej správy a odoslanie správy typu
byena server
Vstup
/auth xpasti00 123 AP
Výstup Wiresharku
Tento test bol vykanný použitím vlastného Python skriptu, ktorý simuloval neznámu správu od servera
Účel: Test overuje správne fungovanie programu pri prijatí neznámej správy od servera.
Akcie:
- Simulácia odoslania neznámej správy od servera
Očakávané výsledky:
- Potvrdenie prijatia neznámej správy
- Odoslanie správy typu
errna server a ukončenie programu
Vstup
/auth xpasti00 123 AP
Výstup Wiresharku
Účel: Test overuje správne fungovanie autentifikácie na serveri a následné zaslanie správy po úspešnom prihlásení.
Akcie:
- Odoslanie autentifikačného požiadavku na server s použitím uvedeného používateľského mena a tajného kľúča.
- Odoslanie správy "hi" na server po úspešnom prihlásení.
Očakávané výsledky:
- Potvrdenie úspešnej autentifikácie zaslaním
REPLY OKzo servera a pripojenie uživateľa do predvoleného kánalu. - Správa "hi" odoslaná na server.
Vstup
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
hi
Výstup Wiresharku
Účel: Overenie reakcie servera na autentifikačný požiadavok s nesprávnym tajným kľúčom.
Akcie:
- Odoslanie autentifikačného požiadavku na server s použitím nesprávneho tajného kľúča.
- Opatovné odoslanie autentifikačného požiadavku s použitím správneho tajného kľúča.
Očakávané výsledky:
- Odoslanie
REPLY NOKzo servera, keďže autentifikačné údaje sú nesprávne. - Na druhú autentifikačnú správu odpoveď
REPLY OKa pripojenie uživateľa do predvoleného kánalu.
Vstup
/auth xpasti00 badSecret AP
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
Výstup Wiresharku
Účel: Overenie správnosti fungovania funkcie premenovania užívateľa na serveri.
Akcie:
- Odoslanie prvej správy s menom "AP"
- Zadanie príkazu na premenovanie
- Odoslanie druhej správy s novým menom "Adam"
Očakávané výsledky:
- Správa "hi" odoslaná s menom "AP".
- Druhá správa odoslaná s novým menom "Adam".
Vstup
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
hi
/rename Adam
i was renamed
Výstup Wiresharku
Účel: Overenie správnosti fungovania funkcie zmeny kanálu.
Akcie:
- Zmena kanálu na "discord.verified-1"
- Odoslanie správy "hello from verified-1"
Očakávané výsledky:
- Odpoveď servera
REPLY OKna zmenu kanálu a pripojenie uživateľa do nového kanálu. - Správa "hello from verified-1" odoslaná do nového kanálu.
Vstup
/auth xpasti00 e8b8b8c4-a22f-40dd-b4c4-59c0781ac9b7 AP
/join discord.verified-1
hello from verified-1
Výstup Wiresharku
- RFC 792 - Internet Control Message Protocol a RFC 4443 - ICMPv6
- RFC 826 - ARP
- RFC 5952 - A Recommendation for IPv6 Address Text Representation
- RFC 3339 - Date and Time on the Internet: Timestamps
- Wikipedia, the free encyclopedia: http://en.wikipedia.org
- ChatGPT
- Gemini
- StackOverflow
- Regexr
- https://www.geeksforgeeks.org/tcp-and-udp-in-transport-layer/
- https://stackoverflow.com/questions/46882815/c-sharp-sockets-tcp-udp
