This is an old revision of the document!
Tema 5 Server web asincron
Temele trimise până la
19 mai 2019, ora 23:55 vor intra în competiția pentru premiile acordate de către
Bitdefender
Obiectivele temei
Enunț
Să se implementeze un server web care să folosească operații avansate de intrare/ieșire:
operații asincrone pe fișiere;
operații non-blocante pe sockeți;
zero-copying;
multiplexarea operațiilor I/O.
Pentru implementare se va folosi API-ul de operații I/O avansate specific sistemelor de operare Linux și Windows:
Serverul web va utiliza API-ul modern de multiplexare pentru a aștepta conexiuni din partea clienților: epoll (Linux) și Completion Ports pe Windows. Pe conexiunile realizate se vor recepționa cereri din partea clienților și apoi se vor distribui răspunsurile către aceștia.
Clienții și serverul vor comunica folosind protocolul HTTP. Pentru parsarea cererilor HTTP din partea clienților recomandăm folosirea acestui parser HTTP, disponibil și în cadrul directorului de resurse ale temei. Va trebui să folosiți un callback pentru obținerea căii către resursa locală solicitată de client. Tot în directorul de resurse, găsiți un exemplu simplificat de folosire a parser-ului.
Serverul implementează o funcționalitate limitată a protocolului HTTP, aceea de a transmite fișiere clienților. Serverul va furniza fișiere din directorul AWS_DOCUMENT_ROOT
, definit în cadrul antetului temei. Fișiere se găsesc doar în subdirectoarele AWS_DOCUMENT_ROOT/static/
, respectiv AWS_DOCUMENT_ROOT/dynamic/
, iar request path-uri corespunzătoare vor fi, de exemplu, AWS_DOCUMENT_ROOT/static/test.dat
, respectiv AWS_DOCUMENT_ROOT/dynamic/test.dat
. Prelucrarea fișierelor va fi următoarea:
Fișierele din directorul
AWS_DOCUMENT_ROOT/static/
sunt fișiere statice care vor fi transmise clienților folosind
API de zero-copying (
sendfile/
TransmitFile).
Fișierele din directorul
AWS_DOCUMENT_ROOT/dynamic/
sunt fișiere pentru care se presupune că este necesară o fază de post-procesare din partea serverului. Aceste fișiere vor fi citite de pe disc folosind
API asincron și apoi vor fi transmise către clienți. Transmiterea va folosi sockeți non-blocanți (Linux) și Overlapped I/O pe sockeți (Windows).
Pentru
request path-uri nevalide se va transmite un mesaj
HTTP 404.
După transmiterea unui fișier, conform protocolului HTTP, conexiunea este închisă.
Precizări/recomandări pentru implementare
Implementarea temei presupune existența unei mașini de stări pentru fiecare conexiune, pe care să o interogați și actualizați periodic pe măsura desfășurării transferului.
Definițiile macro-urilor și structurilor utile se găsesc în
header-ul temei.
Răspunsurile HTTP vor avea codul 200 pentru fișiere existente și 404 pentru fișiere inexistente.
Un răspuns valid este format din antetul HTTP, conținând directivele aferente, două newline-uri (\r\n\r\n
), urmat de conținutul efectiv (fișierul).
-
Puteți folosi directive de request predefinite, precum Date
, Last-Modified
etc.
Portul pe care serverul web ascultă pentru conexiuni este definit în cadrul
header-ului temei ca macro:
AWS_LISTEN_PORT
.
Directorul rădăcină raportat la care se caută resursele/fișierele este definit în cadrul
header-ului temei ca macro:
AWS_DOCUMENT_ROOT
.
Resurse utile
Depanare/troubleshooting
Pentru depanarea serverului, recomandăm folosirea
wget și a
netcat.
Pentru netcat, dacă doriți să comunicați folosind HTTP, va trebui să transmiteți o cerere specifică (spre exemplu
GET /path/to/resource HTTP/1.0
). Exemplu de folosire:
$ nc localhost 8888
GET / HTTP/1.0
Sau:
echo -ne "GET / HTTP/1.0\r\n\r\n" | nc -q 2 localhost 8888
Exemple de utilizare găsiți în
teste.
Precizări pentru Windows
-
Așteptarea încheierii operațiilor asincrone (atât pe fișiere cât și pe sockeți) se va realiza unificat, folosind
I/O Completion Ports.
-
API-ul I/O Completion Ports este apelat prin intermediul wrapper-elor din cadrul header-ului
w_iocp.h.
-
Pentru operații asincrone de comunicare pe sockeți folosiți funcțiile
WSASend. respectiv
WSARecv.
Structura
WSAOVERLAPPED conține informațiile necesare pentru operațiile asincrone pe sockeți. Este echivalentă structurii
OVERLAPPED.
-
AcceptEx permite notificarea unei acțiuni în momentul în care aceasta are loc, împreună cu binding-ul socket-ului.
Pentru operații asincrone cu sistemul de fișiere, folosiți funcțiile
ReadFile și
WriteFile cu argumentele de Overlapped I/O activate.
-
Transmiterea de fișiere statice se realizează folosind
TransmitFile.
Tema se va rezolva folosind doar funcții Win32. Se pot folosi de asemenea și funcțiile de formatare printf
, scanf
, funcțiile de alocare de memorie malloc
, free
și funcțiile de manipulare a șirurilor de caractere (strcat
, strcmp
etc.).
Pentru partea de I/O și procese se vor folosi doar funcții Win32. De exemplu, funcțiile open
, read
, write
, close
nu trebuie folosite; în locul acestora folosiți CreateFile
, ReadFile
, WriteFile
, CloseHandle
.
Precizări pentru Linux
Atât citirea cât și scrierea peste sockeți se realizează doar la notificarea dată de
API-ul specific sistemului de operare, folosind
epoll.
Tot folosind
epoll se va aștepta notificarea încheierii operațiilor asincrone pe fișiere.
-
API-ul epoll este apelat prin intermediul wrapper-elor din cadrul header-ului
w_epoll.h.
Scrierea pe sockeți, atât în cazul fișierelor statice, cât și în cazul fișierelor dinamice, se realizează non-blocant: sockeții sunt marcați ca non-blocanți, iar la un apel de scriere se scrie cât permite buffer-ul socketului. La următoarea notificare din partea
API-ului specific se va realiza o nouă scriere și așa mai departe.
Pentru configurarea unui socket ca non-blocant puteți folosi
fcntl (flag-ul
O_NONBLOCK
).
Pentru implementarea operațiilor I/O, folosiți funcțiile din familia
io_setup.
Pentru utilizarea funcțiilor va trebui să realizați link-area cu
biblioteca libaio și includerea header-ului
<libaio.h>
.
Recomandăm folosirea unei variabile de tipul
io_context_t
și a unui descriptor
eventfd pentru fiecare conexiune.
Pentru încheierea operațiilor asincrone, folosiți
io_getevents.
-
Transmiterea de fișiere statice se realizează folosind
sendfile.
Tema se va rezolva folosind doar funcții POSIX. Se pot folosi de asemenea și funcțiile de formatare din familia printf
, funcțiile de alocare de memorie malloc
, free
și funcțiile de lucru cu șiruri de caractere (strcat
, strdup
etc.)
Pentru partea de I/O se vor folosi doar funcții POSIX și funcții pentru operații asincrone. De exemplu, funcțiile fopen
, fread
, fwrite
, fclose
nu trebuie folosite; în locul acestora folosiți open
, io_setup
, io_submit
, close
.
Scrierea și citirea pe sockeți se vor face doar prin funcțiile send()
și recv()
. Nu este permisă folosirea funcțiilor read()
și write()
pentru lucrul cu sockeți.
Testare
Corectarea temelor se va realiza automat cu ajutorul unor suite de teste publice:
Indicații despre utilizarea suitei de teste se găsesc în fișierul README
din cadrul arhivei.
Pentru testare, pe Windows, se folosește o versiune a utilitarului
netcat
; puteți descărca arhiva completă (inclusiv executabilul necesar)
de aici (parola este
nc
).
-
În urma compilării temei trebuie să rezulte un executabil denumit aws
(Linux), respectiv aws.exe
(Windows).
Pe Windows, va trebui ca, în urma compilării, să rezulte și fișierul obiect aws.obj
(pentru verificările de simboluri folosind nm
).
Suita de teste conține un set de teste. Trecerea unui test conduce la obținerea punctajului aferent acestuia.
În urma rulării testelor, se va acorda, în mod automat, un punctaj total. Punctajul total maxim este de 90 de puncte, pentru o temă care trece toate testele. La acest punctaj se adaugă 10 puncte care reprezintă aprecierea temei de către asistentul care o corectează.
Cele 100 de puncte corespund la 10 puncte din cadrul notei finale.
Pot exista penalizări în caz de întârzieri sau pentru neajunsuri de implementare sau de stil.
Pe lângă penalizările precizate în cadrul
listei de depunctări, se vor avea în vedere următoarele elemente:
-2p Linux și Windows – fișierele statice nu sunt transmise prin mecanismul de zero-copy
-0.5p Linux și Windows – alocare statică a unui vector de conexiuni
-4p Linux – nu se folosesc doar send
și recv
pentru operațiile cu sockeți
-2p Linux – folosirea de operații blocante pe sockeți în locul operațiilor non-blocante
-1p Linux – recv/send/sendfile sunt apelate într-o buclă și nu pe baza evenimentelor semnalizate de epoll
-2p Linux – operațiile I/O asincrone pe fișiere nu sunt așteptate “integrat” folosind eventfd
și epoll
-2p Windows – așteptarea încheierii operațiilor asincrone (atât pe fișiere cât și pe sockeți) nu este realizată unificat, folosind I/O Completion Ports
-1p Linux și Windows – alte implementări nevalide la nivelul sistemului asincron de comunicație
O temă care nu folosește operații I/O asincrone pe fișiere: io_*
(din libaio
) pe Linux, respectiv Overlapped I/O pe Windows va pierde punctajul pentru testele cu fișiere dinamice (testele 26-35)
Testul 0 din cadrul checker-ului temei verifică automat coding style-ul surselor voastre. Ca referință este folosit
stilul de coding din kernelul Linux. Acest test nu oferă sau scade puncte, dar poate fi folosit orientativ de către echipa de corectare pentru a penaliza problemele grave de coding style. Important este să vă definiți un stil și să-l folosiți consecvent. Pentru mai multe informații despre un cod de calitate citiți
pagina de recomandări.
Resurse de suport
Resursele temei se găsesc și în repo-ul
so-assignments de pe GitHub. Repo-ul conține și un script Bash care vă ajută să vă creați un repository privat pe instanța de
GitLab a facultății. Urmăriți indicațiile din README și de pe
pagina de Wiki dedicată pentru git.
În plus, responsabilii de teme se pot uita mai rapid pe GitLab la temele voastre în cazul în care aveți probleme/bug-uri. Este mai ușor să primiți suport în rezolvarea problemelor implementării voastre dacă le oferiți responsabililor de teme acces la codul sursă pe GitLab.
Dacă ați folosit GitLab pentru realizarea temei, indicați în README link-ul către repository. Asigurați-vă că responsabilii de teme au drepturi de citire asupra repo-ului vostru.
FAQ
Suport, întrebări și clarificări