//Pytanie: "Przedstaw i omów algorytm budowy podstawowej aplikacji klient-serwer dla połączeń UDP/IP"//


{{:programowanie_uslug_sieciowych:14.6.pdf|:wersja pdf}}
====== Protokół UDP ======
Protokół UDP (ang. //User Datagram Protocol//) to bezpołączeniowy i zawodny protokół 4. warstwy modelu OSI (czyli warstwy transportowej). Jest to protokół bezpołączeniowy, gdyż przed wysłaniem danych nie trzeba podejmować żadnych dodatkowych kroków. Wystarczy po prostu wysłać dane. Jest to również protokół zawodny, gdyż nie daje nam gwarancji, że wysłane dane dotrą do celu. Gwarantuje jedynie, że jeśli dane jednak dotrą do celu, to nie będą przekłamane.

{{ :programowanie_uslug_sieciowych:udpapp.png|:programowanie_uslug_sieciowych:udpapp.png}}
===== Serwer =====
Algorytm serwera UDP przedstawia się następująco:
  * Stwórz gniazdo bezpołączeniowe (''SOCK_DGRAM'')
  * Przypisz gniazdo do portu
  * Rozpocznij pętle:
    * Odbierz przychodzące dane
    * Przetwórz dane
    * Odeślij odpowiedź

Serwer UDP implementuje się zwykle jako jeden proces, który obsługuje klientów sekwencyjnie, jeden po drugim. Z tego względu krok przetwarzania danych powinien być krótki.

===== Klient =====
Algorytm klienta UDP przedstawia się następująco:
  * Stwórz gniazdo bezpołączeniowe (''SOCK_DGRAM'')
  * Rozpocznij pętlę:
    * Wyślij zapytanie
    * Odbierz odpowiedź
    * Sprawdź czy odpowiedź nadeszła z maszyny do której zostało wysłane zapytanie
    * Przetwórz odpowiedź

===== Funkcje =====
Drugi parametr funkcji ''socket()'' określa typ tworzonego gniazda. Do utworzenia gniazda bezpołączeniowego wykorzystuje się następujące wywołanie:
<code c>
    int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
</code>

Aby przypisać gniazdo do portu, należy przygotować strukturę ''struct sockaddr'' i wywołać funkcję ''bind()'':
<code c>
    bind(sockfd, (struct sockaddr *) &my_addr, sizeof(my_addr));
</code>

Dane odbiera się za pomocą funkcji ''recvfrom()''. Jej argumenty to: socket z którego dane będą czytane, wskaźnik do bufora (i jego długość) do którego trafią dane, flagi (zwykle 0), wskaźnik do struktury ''sockaddr_in'' (i jej rozmiar) do której zostaną zapisane informacje o nadawcy:
<code c>
    recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
            (struct sockaddr *) &sender_addr, &sender_addr_len);
</code>

Dane wysyła się za pomocą funkcji ''sendto()''. Jej argumenty to: socket do którego będą wysyłane dane, wskaźnik do bufora (i jego długość) z którego będą pobrane dane do wysłania, flagi (zwykle 0), wskaźnik do struktury ''sockaddr_in'' (i jej rozmiar) w której znajdują się informacje o odbiorcy:
<code c>
    sendto(sockfd, buf, strlen(buf), 0,
          (struct sockaddr *) &receiver_addr, sizeof(receiver_addr));
</code>

Jak widać za pomocą funkcji ''sendto()'' można wykorzystać jeden socket do wysyłania danych do wielu serwerów. Jedyne co trzeba zrobić, to zmienić dane w strukturze ''sockaddr'', której wskaźnik przekazujemy do funkcji. Ale uwaga, można to robić tylko jeśli na danym sockecie nie została wywołana funkcja ''connect()''. Jeśli ją wywołamy, to dany socket może służyć tylko do komunikacji z tym jednym serwerem, z którym się "połączyliśmy" (zwracam uwagę na cudzysłowy -- to nadal jest UDP, czyli protokół bezpołączeniowy). Zaletą wywołania funkcji ''connect()'' na sockecie bezpołączeniowym jest to, że od tego momentu do wysyłania i odbierania danych można używać funkcji ''send()'' i ''recv()''.

Powyższe funkcje w przypadku wystąpienia błędu zwracają -1 i ustawiają zmienną ''errno''.

===== Dodatkowe informacje =====
  * http://beej.us/guide/bgnet/output/htmlsingle/bgnet.html -- programowanie sieciowe
  * http://en.wikipedia.org/wiki/Berkeley_sockets -- programowanie sieciowe
