Dydaktyka:
FeedbackTo jest stara wersja strony!
Najstarsze funkcje do zmiany to gethostbyname
i gethostbyaddr
.
Funkcje są przestarzałe i powszechnie używane. Problemy z ich działaniem obrazuje poniższy kod:
#include <unistd.h> #include <thread> #include <arpa/inet.h> #include <netdb.h> int main() { std::thread t1([&]{ sleep(1); gethostbyname("spam.org"); }); std::thread t2([&]{ hostent* ret = gethostbyname("fc.put.poznan.pl"); sleep(2); printf("%s: %s\n", ret->h_name, inet_ntoa(**(in_addr**)ret->h_addr_list)); }); t1.join(); t2.join(); return 0; }
Zadanie 1. Przetestuj powyższy kod. Zastanów się jak uniknąć problemu, który pokazuje powyższy kod.
Kompilator GCC udostępnia wiele rozszerzeń "łatających" braki standardów https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html.
Między innymi wprowadza funkcje gethostbyname_r
(funkcje *_r
są reentrant, tj. można je bezpiecznie stosować w aplikacjach wielowątkowych https://en.wikipedia.org/wiki/Reentrancy_%28computing%29)
hostent he, *resptr; int status, retval; char buffer[4096]; retval = gethostbyname_r("fc.put.poznan.pl", &he, buffer, sizeof(buffer), &resptr, &status); if(retval) error(1,0,"gethostbyname_r error: %s", hstrerror(status)); if(!resptr) error(1,0,"empty result"); print(resptr);
Standard POSIX wprowadza funkcje getaddrinfo
, która pozwala na bezpieczne tłumaczenie nazwy domenowej na adresy IP (zarówno IPv4 i IPv6). Funkcja od razu tworzy gotową strukturę sockaddr
. Wynik musi być zwolniony przez programistę funkcją freeaddrinfo
.
Składnia:
int getaddrinfo( // zwraca 0 (ok) lub -1 (błąd) const char * ip_or_hostname, // nazwa domenowa lub adres IP const char * port_or_service, // numer portu (np. "80" lub nazwa usługi (np. "http", ustawi port na 80) const struct addrinfo *hints, // pozwala wybrać typ adresu, wyłączyć używanie DNS, etc. struct addrinfo **res); // pole na wynik (jeśli puste - nic nie znaleziono)
Opis struktury addrinfo
znajdziesz na stronie manuala do getaddringo (man getaddrinfo
).
Przykłady użycia:
#include <netdb.h> #include <cstdio> #include <error.h> #include <arpa/inet.h> int main(){ // Ustawienie "podpowiedzi" - sterowanie jakie wyniki chcemy otrzymać: addrinfo hints {}; hints.ai_family = AF_INET; // tylko IPv4 (AF_INET) hints.ai_protocol = IPPROTO_UDP; // protokół UDP // Zmienna w której będzie umieszczona lokalizacja wyniku w pamięci addrinfo * resolved; int res = getaddrinfo("pool.ntp.org", "ntp", &hints, &resolved); if(res) error(1,0,"Getaddrinfo failed: %s\n", gai_strerror(res)); if(!resolved) error(1,0,"Empty result\n"); for(addrinfo * it = resolved; it; it=it->ai_next){ sockaddr_in* addr = (sockaddr_in*) it->ai_addr; // <- rzutowanie bezpieczne, printf(" %s\n",inet_ntoa(addr->sin_addr)); // bo w podpowiedziach } // zażądaliśmy adresów IPv4 freeaddrinfo(resolved); return 0; }
#include <netdb.h> #include <cstdio> #include <error.h> int main(){ addrinfo * resolved; // Podpowiedzi, port i nazwa hosta są opcjonalne. int res = getaddrinfo("ietf.org", nullptr, nullptr, &resolved); if(res) error(1,0,"Getaddrinfo failed: %s\n", gai_strerror(res)); if(!resolved) error(1,0,"Empty result\n"); char ip[40]; // maks. długość IP(v6) jako tekst: 8 bloków po 4 znaki oddzielone ':' for(addrinfo * it = resolved; it; it=it->ai_next){ // funkcja getnameinfo tłumaczy dowolny sockaddr na tekst res = getnameinfo(it->ai_addr, it->ai_addrlen, ip, 40, nullptr, 0, NI_NUMERICHOST); if(res) error(1,0,"Getnameinfo failed: %s\n", gai_strerror(res)); else printf("%40s (socktype: %d, proto: %d)\n", ip, it->ai_socktype, it->ai_protocol); } freeaddrinfo(resolved); return 0; }
Zadanie 2. Stwórz klienta TCP, który:
getaddrinfo
z podpowiedzią żądającą protokołu TCPgetaddrinfo
getaddrinfo