Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pc:laboratoare:06 [2022/04/04 00:28]
vlad_andrei.badoiu [UDP]
pc:laboratoare:06 [2022/04/13 15:06] (current)
vlad_andrei.badoiu [Exercitii]
Line 59: Line 59:
 Nu se garanteaza ordinea primirii mesajelor si nici prevenirea pierderilor pachetelor. UDP-ul se utilizeaza Nu se garanteaza ordinea primirii mesajelor si nici prevenirea pierderilor pachetelor. UDP-ul se utilizeaza
 mai ales in retelele in care exista o pierdere foarte mica de pachete si in cadrul aplicatiilor mai ales in retelele in care exista o pierdere foarte mica de pachete si in cadrul aplicatiilor
-pentru care pierderea unui pachet nu este foarte grava (Un exemplu: aplicatiile ​peer-to-peer +pentru care pierderea unui pachet nu este foarte grava (Un exemplu: aplicatiile ​streaming video).
-din cadrul unei retele locale).+
  
 Are un overhead foarte mic, in comparatie cu celelalte protocoale de transport (Are un header de 8 bytes, Are un overhead foarte mic, in comparatie cu celelalte protocoale de transport (Are un header de 8 bytes,
Line 100: Line 99:
 ==== Sockets ==== ==== Sockets ====
  
-Un socket este un canal generalizat de comunicare intre procese, reprezentat in Unix print-un descriptor de fisiere*. El ofera posibilitatea de comunicare intre procese aflate pe masini diferite intr-o retea.+In cadrul laboratorului nu vom implementa protocolul UDP ci vom folosi implementarea existenta din kernel-ul de Linux. Acest lucru se realizeaza prin intermediul API-ului de sockets. ​Un socket este un canal generalizat de comunicare intre procese, reprezentat in Unix print-un descriptor de fisiere*. El ofera posibilitatea de comunicare intre procese aflate pe masini diferite intr-o retea.
  
 * Un file descriptor este un handle prin care un proces comunica cu resursele sistemului. * Un file descriptor este un handle prin care un proces comunica cu resursele sistemului.
Line 107: Line 106:
  
  
-In continuare, se vor prezenta principalele functii pentru manipularea socketilor, ​pe cele din figura de mai jos.  ​+In continuare, se vor prezenta principalele functii pentru manipularea socketilor,
  
-{{:​pc:​laboratoare:​sockets-udp.png?​400|}} 
  
 == Comunicare client-server UNIX == == Comunicare client-server UNIX ==
 Intr-o arhitectura client-server,​ clientul trimite request-uri (cere resurse) catre server, iar acesta din urma trimite inapoi un raspuns (cu resursa).  ​ Intr-o arhitectura client-server,​ clientul trimite request-uri (cere resurse) catre server, iar acesta din urma trimite inapoi un raspuns (cu resursa).  ​
  
-Pasi urmati pentru a schimba mesaje folosind UDP la nivelul Transport sunt urmatorii:  ​+Pasi urmati pentru a schimba mesaje folosind UDP la nivelul Transport ​folosind API-ul de sockets ​sunt urmatorii:  ​
  
   - //Deschide un socket// unix in scopul de a permite comunicarea intre procese/​statii diferite folosind descriptori de fisiere (file descriptors) cu apelul //​socket()//​.   - //Deschide un socket// unix in scopul de a permite comunicarea intre procese/​statii diferite folosind descriptori de fisiere (file descriptors) cu apelul //​socket()//​.
-  - //Asociaza o adresa// pentru //socketul deschis// cu apelul //bind()//. In general, folosim //bind()// atunci cand dorim sa asteptam conexiuni pe un anumit port.  +  - //Asociaza o adresa// pentru //socketul deschis// cu apelul //bind()//. In general, folosim //bind()// atunci cand dorim sa asteptam conexiuni pe un anumit port. Bind este chemat pe server pentru a specifica la ce port sa lege socket-ul.
   - //​Trimite/​Receptioneaza date// cu apelul //​recvfrom()/​sendto()//​.   - //​Trimite/​Receptioneaza date// cu apelul //​recvfrom()/​sendto()//​.
   - //Inchide socket// prin //​close()//​.   - //Inchide socket// prin //​close()//​.
  
-{{:​pc:​laboratoare:​apeluri.png?​300|}} +{{:​pc:​laboratoare:​apeluri.png?​300|}} ​{{ :​pc:​laboratoare:​sockets-udp.png?300|}}
- +
-Optional clientul poate face bind, daca vrea sa specifice o interfata pe care pleaca pachetele sau un port sursa, dar de cele mai multe ori acest aspect cade in sarcina sistemului de operare.+
  
 == socket() == == socket() ==
Line 154: Line 150:
  
 == bind() == == bind() ==
- +Utilizata in server pentru lega un socket de un port si eventual de un subnet. ​Pentru a afla mai multe informatii, putem accesa [[https://​beej.us/​guide/​bgnet/​html/#​bind|5.3 bind()—What port am I on?​]]. ​
-Pentru a afla mai multe informatii, putem accesa [[https://​beej.us/​guide/​bgnet/​html/#​bind|5.3 bind()—What port am I on?​]]. ​+
  
 <code C>  ​ <code C>  ​
Line 162: Line 157:
        
 /*int bind(int sockfd, struct sockaddr *my_addr, int addrlen)*/ /*int bind(int sockfd, struct sockaddr *my_addr, int addrlen)*/
-    + 
-int rs = bind(sockfd, ​my_addr, sizeof(my_addr);+struct sockaddr myaddr; 
 +memset(&​myaddr,​ 0, sizeof(servaddr));​ 
 +myaddr.sin_family = AF_INET; // IPv4 
 +/* INADDR_ANY = 0.0.0.0 as uint32 */ 
 +myaddr.sin_addr.s_addr = INADDR_ANY;​ 
 +myaddr.sin_port = htons(atoi(8888));​ 
 + 
 +int rs = bind(sockfd, ​myaddr, sizeof(servaddr);
 /* in urma apelului, sockfd va avea adresa my_addr */ /* in urma apelului, sockfd va avea adresa my_addr */
 if (rs == -1) { if (rs == -1) {
Line 171: Line 173:
 Explicatii: Explicatii:
  
-* sockfd - Descriptorul de fisier returnat de socket() ​  +    ​* sockfd - Descriptorul de fisier returnat de socket() ​  
-my_addr ​- Structura sockaddr ce contine informatii despre adresa IP si port   +    myaddr ​- Structura sockaddr ce contine informatii despre adresa IP si port   
-* addrlen - lungimea lui my_addr+    * addrlen - lungimea lui my_addr
  
-Observam ca apelul bind utilizeaza o variabila de tip **struct ​sockaddr**. +Observam ca apelul bind utilizeaza o variabila de tip **struct ​sockaddr_in**.
- +
-<code C> +
-#include <​sys/​socket.h>​ +
- +
-/* tip de date generic pentru a retine informatii despre adresa socketilor */ +
-struct sockaddr { +
-  unsigned char sa_family;​ +
-  char sa_data[14];​ +
-+
-</​code>​ +
- +
-Explicatii:​ +
-* sa_family - indica formatul particular de adresa: AF_INET (protocolul IPv4), AF_INET6 (protocol IPv6)   +
-* sa_data - adresa socketului+
  
 == struct sockaddr_in == == struct sockaddr_in ==
Line 208: Line 196:
 Explicatii:  ​ Explicatii:  ​
  
-* sin_family - indica formatul particular de adresa: AF_INET (protocolul IPv4), AF_INET6 (protocol IPv6)   +    ​* sin_family - indica formatul particular de adresa: AF_INET (protocolul IPv4), AF_INET6 (protocol IPv6)   
-* sin_port - portul utilizat in NETWORK BYTE ORDER   +    * sin_port - portul utilizat in NETWORK BYTE ORDER   
-* sin_addr - adresa IP in NETWORK BYTE ORDER, in format in_addr mentionat mai jos  ​+    * sin_addr - adresa IP in NETWORK BYTE ORDER, in format in_addr mentionat mai jos  ​
  
 == recvfrom()/ sendto() == == recvfrom()/ sendto() ==
 +
 +Functile sunt folosite pentru a primi/​trimite o datagrama peste un socket. Mai multe detalii gasiti [[https://​beej.us/​guide/​bgnet/​html/#​sendtorecv|aici]].
  
 <code C> <code C>
 #include <​sys/​types.h>​ #include <​sys/​types.h>​
 #include <​sys/​socket.h>​ #include <​sys/​socket.h>​
 +
 +struct sockaddr to;
 +// Filling server information
 +memset(&​to,​ 0, sizeof(servaddr));​
 +to.sin_family = AF_INET;
 +to.sin_port = htons(8888);​
 +int rc = inet_aton("​127.0.0.1",​ &​to.sin_addr);​
 +
        
 int byteswrite = sendto(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *to, int addrlen); int byteswrite = sendto(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *to, int addrlen);
Line 222: Line 220:
   /* trateaza eroare */   /* trateaza eroare */
 } }
-   + 
 +/* from va fi populata de apelul recvfrom si va contine informatii despre cine a trimis datagrama catre noi */ 
 +struct sockaddr from;
 int bytesread = recvfrom(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *from, int *addrlen); int bytesread = recvfrom(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *from, int *addrlen);
 if (bytesread == -1) { if (bytesread == -1) {
Line 260: Line 260:
 Explicatii: Explicatii:
  
-* sockfd - Descriptorul de fisier returnat de socket() ​  +    ​* sockfd - Descriptorul de fisier returnat de socket() ​  
- +    * how - Specifica modul de inchidere: 0 - Nu se mai citesc date. 1 - Nu se mai pot face transmiteri de date. 2 - Se intrerupe comunicatia in ambele directii.
-* how - Specifica modul de inchidere: 0 - Nu se mai citesc date. 1 - Nu se mai pot face transmiteri de date. 2 - Se intrerupe comunicatia in ambele directii.+
  
 <​note>​ Shutdown() nu inchide un descriptor de fisier, ci doar ii schimba modul de utilizare. Resursele trebuie eliberate folosind close() la final. </​note>​ <​note>​ Shutdown() nu inchide un descriptor de fisier, ci doar ii schimba modul de utilizare. Resursele trebuie eliberate folosind close() la final. </​note>​
Line 312: Line 311:
  
 **[Task-ul 1]:**  **[Task-ul 1]:** 
-Plecând de la scheletul de cod oferit, scrieți o pereche de programe (client și server) care să permită transferul unui fișier binar de la server ​la client.+Plecând de la scheletul de cod oferit, scrieți o pereche de programe (client și server) care să permită transferul unui fișier binar de la client ​la server.
  
 <note warning> <note warning>
Line 327: Line 326:
 Specificații:  ​ Specificații:  ​
   - Clientul trimite un fisier binar prin UDP, iar serverul il va receptiona datele si le va scrie in fisierul lui.  ​   - Clientul trimite un fisier binar prin UDP, iar serverul il va receptiona datele si le va scrie in fisierul lui.  ​
-  - Fișierul trimis de client se va numi **fisier.bin**,​ iar fișierul salvat de client ​se va numi **received_file.bin**.+  - Fișierul trimis de client se va numi **fisier.bin**,​ iar fișierul salvat de server ​se va numi **received_file.bin**.
   - Clientul va transmite fișierul în calupuri de câte 1024 octeți.   - Clientul va transmite fișierul în calupuri de câte 1024 octeți.
 +
 +La final, vom studia folosind **Wireshark** datagramele trimise de catre client. ​
 +
 +Mai mult, daca sunteti in aceasi retea (e.g. pe acelasi WiFi) va puteti **grupa cate doi** pentru a trimite un fisier de la unu la celalalt. Mai exact, unul dintre voi va porni un server cu bind pe INADDR_ANY (0.0.0.0), iar clientul va putea trimite catre ip-ul celuilalt un fisierul. Felicitari, ati facut o **aplicatie simpla de transfer de fisiere**.
  
 **[Task-ul 2]:** **[Task-ul 2]:**
  
 Dacă ați rulat task-ul 1 cu ambele componente pe aceeași mașină, probabil totul a mers bine, însă o soluție naivă (care trimite datagrame doar de la sender la receiver), ascunde 2 probleme: Dacă ați rulat task-ul 1 cu ambele componente pe aceeași mașină, probabil totul a mers bine, însă o soluție naivă (care trimite datagrame doar de la sender la receiver), ascunde 2 probleme:
-  - Dacă transmițătorul este mai rapid decât receptorul, acesta din urma poate fi inundat și să nu reușească să le proceseze la timp;+  - Dacă transmițătorul este mai rapid decât receptorul, acesta din urma poate fi inundat și să nu reușească să proceseze la timp datagramele;
   - Două situații foarte posibile în cazul comunicării către mașini diferite (**pierderea unei datagrame** sau **inversarea ordinii datagramelor**) sunt netratate.   - Două situații foarte posibile în cazul comunicării către mașini diferite (**pierderea unei datagrame** sau **inversarea ordinii datagramelor**) sunt netratate.
  
Line 340: Line 343:
  
 <note warning> <note warning>
-Acest exercitiu va fi rulat doar pe mininet. ​Lansati-l doar folosind comanda: <​code>​sudo python3 topology.py</​code>​+Acest exercitiu va fi rulat doar pe mininet. ​Lansați-l doar folosind comanda: <​code>​sudo python3 topology.py</​code>​
 </​note>​ </​note>​
  
Line 398: Line 401:
 <​note>​ <​note>​
 Observatie: Cateodata, cand se incearca sa se reporneasca un _server_, bind() nu reuseste sa asigneze, iar eroarea este "​Address already in use". Asta inseamna ca un socket care a fost conectat pe acel port inca mai este agatat si //​utilizeaza portul//. In aceasta situatie, fie se poate astepta deconectarea portului respectiv, fie se specifica programatic reutilizarea portului cu [[https://​linux.die.net/​man/​3/​setsockopt|setsockopt()]] Observatie: Cateodata, cand se incearca sa se reporneasca un _server_, bind() nu reuseste sa asigneze, iar eroarea este "​Address already in use". Asta inseamna ca un socket care a fost conectat pe acel port inca mai este agatat si //​utilizeaza portul//. In aceasta situatie, fie se poate astepta deconectarea portului respectiv, fie se specifica programatic reutilizarea portului cu [[https://​linux.die.net/​man/​3/​setsockopt|setsockopt()]]
 +
 +<code C>
 +int enable = 1;
 +if (setsockopt(sockfd,​ SOL_SOCKET, SO_REUSEADDR,​ &​enable,​ sizeof(int)) < 0)
 +    perror("​setsockopt(SO_REUSEADDR) failed"​);​
 +</​code>​
 +
 +</​note>​
 +
 +<​note>​
 +O posibila solutie a laboratorului se gaseste [[https://​ocw.cs.pub.ro/​courses/​_media/​pc/​laboratoare/​lab06-rezolvare.zip|aici]]
 </​note>​ </​note>​
pc/laboratoare/06.1649021328.txt.gz · Last modified: 2022/04/04 00:28 by vlad_andrei.badoiu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0