Komunikacja bezpołączeniowa
UDP (z ang. User Datagram Protocol – protokół datagramów użytkownika) to bardzo lekki protokół umożliwiający przesyłanie komunikatów między klientem i serwerem, o następujących cecach:
- brak utrzymania informacji o stanie połączenia,
- brak potwierdzenia dotarcia komunikatu (w efekcie brak retransmisji w przypadku utraty komunikatu)
- mały narzut komunikacyjny (wielkość nagłówka to 64 bity, podczas gdy w TCP to 160 bitów),
- brak kontroli nad kolejnością docierania komunikatów (w szczególnych przypadkach jeden datagram może nawet zostać dostarczony dwa razy),
- minimalne informacje przekazywane, 4 pola po 16 bitów:
- port źródłowy
- port docelowy (oba porty określają strony komunikacji),
- suma kontrolna
- długość datagramu UDP.
Podobnie jak w przypadku TCP porty o wartości poniżej 1024 są zarezerwowane dla znanych usług i wymagają uprawnień administratora w celu komunikacji.
Wykorzystywane funkcje:
- sockfd = socket(int socket_family, int socket_type, int protocol); (dla komunikacji UDP należy podać odpowiednio AF_INET, SOCK_DGRAM, 0)
- ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
- ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
- ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
- ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
- struktura msghdr:
123456789struct msghdr {void *msg_name; /* optional address */socklen_t msg_namelen; /* size of address */struct iovec *msg_iov; /* scatter/gather array */size_t msg_iovlen; /* # elements in msg_iov */void *msg_control; /* ancillary data, see below */size_t msg_controllen; /* ancillary data buffer len */int msg_flags; /* flags on received message */}; - struktura iovec:
1234struct iovec { /* Scatter/gather array items */void *iov_base; /* Starting address */size_t iov_len; /* Number of bytes to transfer */};
Funkcje sendto i recvfrom są prostsze i będziemy je wykorzystywać na zajęciach.
UDP server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <stdio.h> #include <string.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> char buf[512]; int main(int argc, char **argv) { struct sockaddr_in myaddr, endpoint; int sdsocket, r; socklen_t addrlen; unsigned int port; printf("Na ktorym porcie mam sluchac? : "); scanf("%u", &port); if ((sdsocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { printf("socket() nie powiodl sie\n"); return 1; } myaddr.sin_family = AF_INET; myaddr.sin_port = htons(port); myaddr.sin_addr.s_addr = INADDR_ANY; if ( bind(sdsocket, (struct sockaddr*) &myaddr, sizeof(struct sockaddr_in)) < 0) { printf("bind() nie powiodl sie\n"); return 1; } addrlen = sizeof(struct sockaddr_in); while (1) { /* nieskonczona petla */ memset(buf, 0, 512); r = recvfrom(sdsocket, buf, 512, 0, (struct sockaddr*) &endpoint, &addrlen); printf("Wiadomosc od %s: %s\n", inet_ntoa(endpoint.sin_addr), buf); } return 0; } |
UDP client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <stdio.h> #include <string.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> char buf[512]; int main(int argc, char **argv) { struct sockaddr_in adr; int gniazdo, r; unsigned int port; char abcd[512]; printf("Podaj adres IP odbiorcy: "); scanf("%s", abcd); printf("Podaj numer portu odbiorcy: "); scanf("%u", &port); gniazdo = socket(AF_INET, SOCK_DGRAM, 0); adr.sin_family = AF_INET; adr.sin_port = htons(port); adr.sin_addr.s_addr = inet_addr(abcd); printf("Podaj wiadomosc: "); fflush(stdout); fgetc(stdin); fgets(buf, 512, stdin); r = sendto(gniazdo, buf, 512, 0, (struct sockaddr*) &adr, sizeof(adr)); if (r != 512) printf("sendto() nie powiodl sie\n"); else printf("Wiadomosc wyslana.\n"); close(gniazdo); return 0; } |
Zadania:
W oparciu o kod z Przykładu 2 zaimplementować następujące przypadki:
- Przesyłanie bloków 10000-bajtowych za pomocą protokołu UDP z pomiarem czasu. Program powinien działać wg schematu pomiar czasu, socket, 100 razy sendto i recvfrom, close, pomiar czasu, podanie czasu podzielonego przez 100.
- Przesyłanie bloków 10000-bajtowych za pomocą protokołu TCP w jednej sesji z pomiarem czasu. Program powinien działać wg schematu pomiar czasu, socket, connect, 100 razy send i recv, close, pomiar czasu, podanie czasu podzielonego przez 100.
Przeimplementować należy zarówno stronę klienta, jak i stronę serwera.
Program proszę uruchamiać na dwóch komputerach w laboratoriach wydziału aby uzyskane wyniki faktycznie uwzględniały przesyłanie danych przez sieć.
Uwagi:
- przykłady klienta i serwera UDP oraz zadanie wzięte ze strony Tomasza Tyrakowskiego http://www.staff.amu.edu.pl/~ttomek/sik/cwiczenia2.html),
- opisy struktur i dokumentacje protokołu wzięte z manuala (podręcznika systemowego linux).