przejście do zawartości
Jan Kończak
Narzędzia użytkownika
Zaloguj
Narzędzia witryny
Narzędzia
Pokaż stronę
Poprzednie wersje
Odnośniki
Ostatnie zmiany
Menadżer multimediów
Indeks
Zaloguj
Ostatnie zmiany
Menadżer multimediów
Indeks
Jesteś tutaj:
start
»
sk2
»
sockets_netdbs
sk2:sockets_netdbs
Ta strona jest tylko do odczytu. Możesz wyświetlić źródła tej strony ale nie możesz ich zmienić.
====== Zmiana adresu domenowego na IP ====== ===== Tradycyjna funkcja gethostbyname ===== 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: <code cpp ghbn.cpp> #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; } </code> //Zadanie 1.// Przetestuj powyższy kod. Zastanów się jak uniknąć problemu, który pokazuje powyższy kod. ===== Rozszerzenia GNU ===== <html><small></html> Kompilator GCC udostępnia wiele rozszerzeń "łatających" braki standardów [[https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html]].\\ Towarzysząca mu biblioteka [[https://www.gnu.org/software/libc/|glibc]] wprowadza między innymi funkcję ''gethostbyname_r'' (funkcje ''*_r'' są reentrant, tj. można je łatwiej stosować w aplikacjach wielowątkowych [[https://en.wikipedia.org/wiki/Reentrancy_%28computing%29]]). ''gethostbyname_r'' może być używana współbieżnie. <code cpp> 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);</code> <html></small></html> ===== getaddrinfo ===== 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''. Poza ''getaddrinfo'' standard wprowadza też m. inn. funkcję do tłumaczenia ''sockaddr*'' na tekst – ''getnameinfo''. <html><small></html> Składnia: <code cpp> int getaddrinfo( // zwraca 0 (ok) lub niezerowy kod błędu (można go przerobić na tekst używając gai_strerror) 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) </code> Definicja struktury ''addrinfo'':<code cpp> struct addrinfo { int ai_flags; int ai_family; // ai_family, ai_socktype i ai_protocol mają znaczenie identyczne jak int ai_socktype; // w funkcji socket(). Mogą być ustawione w podpowiedziach na wybraną int ai_protocol; // wartość lub na 0 (wtedy oznaczają wszystkie pasujące wartości) socklen_t ai_addrlen; // ai_addr to adres o długości ai_addrlen który może być struct sockaddr *ai_addr; // użyty w funkcji connect lub bind char *ai_canonname; struct addrinfo *ai_next; // struktura addrinfo tworzy jednokierunkową linked listę, ai_next wskazuje na jej następny element };</code><html></small></html> Pełen opis funkcji ''getaddrinfo'' i struktury ''addrinfo'' znajdziesz na stronie manuala do getaddrinfo (''man getaddrinfo''). Przykłady użycia: <code cpp gai1.cpp> #include <netdb.h> #include <cstdio> #include <error.h> // niestandardowa funkcja ułatwiająca obsługę błędów (dostępna w glibc) #include <arpa/inet.h> int main(){ // Ustawienie "podpowiedzi" - sterowanie jakie wyniki chcemy otrzymać: addrinfo hints {}; // uwaga: puste 'list initialization' zeruje całą strukturę 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; } </code><code cpp gai2.cpp>#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 ':' // można użyć stałą NI_MAXHOST określającą maksymalną długość nazwy domenowej 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; }</code> //Zadanie 2.// Stwórz klienta TCP, który: * odczyta z listy argumentów adres i numer portu * użyje funkcji ''getaddrinfo'' z podpowiedzią żądającą protokołu TCP * utworzy socket korzystając z rodziny adresów zwróconej przez ''getaddrinfo'' * połączy się funkcją connect korzystając ze struktury sockaddr i informacji o jej długości zwróconej przez ''getaddrinfo''
sk2/sockets_netdbs.1603145952.txt.gz
· ostatnio zmienione: 2020/10/20 00:19 (edycja zewnętrzna)
Narzędzia strony
Pokaż stronę
Poprzednie wersje
Odnośniki
Złóż / rozłóż wszystko
Do góry