Exerciții
In cadrul laboratorului de astazi vom implementa o aplicatie de chat precum Whatsapp.
Descrierea scheletului
Pentru implementarea cerințelor, vom porni de la acest schelet de cod.
În scheletul de laborator găsiți implementată o aplicație client-server care implementează o funcție de chat, care funcționează astfel:
- După pornire, serverul așteaptă conectarea a doi clienți
- După ce ambi clienți s-au conectat, aceștia trimit mesaje către server, pentru a fi trimise către celălalt client.
- La primirea unui mesaj, clientul afișează ce a primit.
Aplicația din schelet are câteva neajunsuri pe care le vom corecta.
Cerinte
Task 1
Note
TCP tratează informația transmisă ca pe un flux de date (octeții in ordine) și are un proces complex (cu ACK-uri, timere, etc.) care trebuie ascuns de utilizator (pentru a fi ușor de folosit). Astfel, apelul send() nu determină trimiterea imediată a unui mesaj/segment, ci face append datelor oferite la un buffer local de transmitere, trimiterea și recepționarea segmentelor de date fiind administrată de către stiva TCP/IP (implementată în kernel) în mod asincron.
La rândul său, recv() se deblochează atunci când orice cantitate de informație devine disponibilă în buffer-ul de recepție, neexistând o încadrare implicită (în mesaje) a octeților din flux.
În consecință, dacă protocolul de nivel aplicație vede transmisia ca fiind alcătuită din mesaje, atunci trebuie să ne așteptăm că este posibil să apară oricând:
- trunchieri - din niște octeți trimiși de către o aplicație, în procesul de la distanță devine disponibilă doar o parte din ei, și totuși apelul
recv()se întoarce, punând la dispoziția apelantului doar o parte din mesaj - concatenări - mai mult de un mesaj se citește cu un singur apel de
recv().
Pentru a fi simplă identificarea mesajelor, implementarea din schelet trimite informația în calupuri de lungime fixată (sizeof(struct chat_meesage)), însă acest lucru nu este suficient deoare funcțiile recv() și send() pot recepționa / copia pentru trimitere mai puțini octeți decât am dat ca parametru.
Reimplementați funcțiile din common.h send_all() și recv_all() astfel încât să acoperiți acest neajuns.
Task 2
Alte neajunsuri ale aplicației sunt că:
- Aceasta funcționează doar cu exact 2 clienți;
- Clienții au o ordine fixată a comunicației (clientul
xnu poate da mesaje când este rândul clientuluiy); - Ambii clienți întâi citesc un mesaj de la tastatură, apoi așteaptă să vadă ce primesc de la server. Asta face ca interactivitatea să fie afectată. Este un comportament așteptat înainte să rezolvăm task-ul 2.
Folosind API-ul de multiplexare (de exemplu, select(), poll() sau epoll()), urmăriți TODO-urile pentru a realiza o comunicare de tip chat între mai mulți clienți.
Funcționalități pentru a valida soluția:
- serverul va accepta noi clienți în orice moment;
- clienții vor putea comunica în orice ordine;
- un mesaj primit de la un client va fi trimis către toți ceilalți, iar aceștia îl vor afișa imediat.
Task 3
La fiecare 4 secunde, server-ul va trimite un anunt despre abonamentele premium catre toti clientii: “Dragi clienti, pentru doar 12 lei o sa puteti trimite de 10 ori mai multe mesaje in jumatate din timp”. Vom folosi un timerfd pentru a implementa aceasta functionalitate.