Narzędzia użytkownika

Narzędzia witryny


sk2:sockets_netdbs

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Both sides previous revision Poprzednia wersja
Nowa wersja
Poprzednia wersja
sk2:sockets_netdbs [2015/10/05 12:05]
jkonczak
sk2:sockets_netdbs [2023/10/17 14:03] (aktualna)
jkonczak
Linia 1: Linia 1:
-====== Zmiana adresu domenowego na IP i z powrotem ​======+====== Zmiana adresu domenowego na IP ======
  
-===== Tradycyjna funkcja ​''​gethostbyname'' ​=====+===== Tradycyjna funkcja gethostbyname =====
  
 Najstarsze funkcje do zmiany to ''​gethostbyname''​ i ''​gethostbyaddr''​. 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: Funkcje są przestarzałe i powszechnie używane. Problemy z ich działaniem obrazuje poniższy kod:
 <code cpp ghbn.cpp>​ <code cpp ghbn.cpp>​
-#include <​iostream>​ 
-#include <​netdb.h>​ 
-#include <​arpa/​inet.h>​ 
-#include <​thread>​ 
-#include <​atomic>​ 
 #include <​unistd.h>​ #include <​unistd.h>​
 +#include <​thread>​
 +#include <​arpa/​inet.h>​
 +#include <​netdb.h>​
 +#include <​cstdio>​
  
-using namespace ​std; +int main() { 
- +     
-void print(hostent* ret)+    ​std::thread t1([]{ 
- cout << ret->​h_name << endl+        sleep(1)
- for(auto it = ret->​h_addr_list*it; ++it){ +        ​gethostbyname("​spam.org"​);​ 
- cout << ​" ​ " ​<< ​inet_ntoa(*((in_addr*)*it)) << endl+    }); 
- }+     
 +    std::thread t2([]{ 
 +        ​hostent* ret = gethostbyname("​cat.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>​
  
-int main(int argcchar **argv) { +//Zadanie 1.// Przetestuj powyższy kod. Zastanów się jak uniknąć problemuktóry pokazuje powyższy kod. 
- atomic<boolwait1 {true}, wait2 {true}; + 
-  +===== Rozszerzenia GNU ===== 
- std::​thread ​ + 
- t1([&]+<html><​small></​html
- while(wait1.load()); +Kompilator GCC udostępnia wiele rozszerzeń "​łatających"​ braki standardów [[https://​gcc.gnu.org/​onlinedocs/​gcc/​C-Extensions.html]].\\ 
- gethostbyname("​spam.org"); +Towarzysząca mu biblioteka ​[[https://​www.gnu.org/​software/​libc/​|glibc]] wprowadza między innymi funkcję ''​[[https://​www.gnu.org/​software/​libc/​manual/​html_node/​Host-Names.html#​index-gethostbyname_005fr|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. 
- wait2.store(false); + 
- sleep(1)+<code cpp> 
- })+hostent he, *resptr
- t2([&]{ +int statusretval; 
- hostent* ret gethostbyname("fc.put.poznan.pl"​);​ +char buffer[4096]; 
- wait1.store(false); +retval ​gethostbyname_r("cat.put.poznan.pl"​, &he, buffer, sizeof(buffer),​ &​resptr,​ &status); 
- while(wait2.load()); +if(retval
- print(ret); +    error(1,​0,"​gethostbyname_r error: %s", hstrerror(status)); 
- }); +if(!resptr) 
-  +    error(1,​0,"​empty result"​); 
- t1.join(); +print(resptr);</​code>​ 
- t2.join(); +<​html></​small></​html>​ 
- return 0; + 
-}+===== 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>​ </​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''​) <​html><​small></​html>​ lub w standardzie POSIX ([[https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​freeaddrinfo.html|[1]]],​ [[https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​getnameinfo.html|[2]]],​ [[https://​pubs.opengroup.org/​onlinepubs/​9699919799/​functions/​gai_strerror.html|[3]]])<​html></​small></​html>​.
 +\\
 +Uwaga: ''​getaddrinfo''​ w razie sukcesu zwraca ''​0'',​ a w przypadku błędu zwraca jego kod.
 +
 +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;
 +    ​
 +    // Z sieci bezprzewodowej PP (lub innych używających serwerów nazw z podsieci 150.254.5.0/​24)
 +    // proszę zmienić "​pool.ntp.org"​ na "​onet.pl"​ - Dział Obsługi i Eksploatacji z niezrozumiałych
 +    // przyczyn cenzuruje odpowiedzi z DNS 
 +    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.1444039518.txt.gz · ostatnio zmienione: 2015/10/05 12:05 przez jkonczak