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 [2017/10/29 22:00]
jkonczak
sk2:sockets_netdbs [2023/10/17 14:03] (aktualna)
jkonczak
Linia 6: Linia 6:
 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"​);​ 
-int main(int argc, char **argv) { +        sleep(2); 
- atomic<​bool>​ wait1 {true}, wait2 {true}; +        ​printf("%s: %s\n", ret->​h_name,​ inet_ntoa(**(in_addr**)ret->​h_addr_list)); 
-  +    }); 
- std::​thread ​ +     
- t1([&]{ +    t1.join();​ 
- while(wait1.load()); +    t2.join(); ​    
- // cout << "About to invoke ​gethostbyname(\"​spam.org\")" << endl+    return 0;
- gethostbyname("​spam.org"​); +
- // cout << "​gethostbyname(\"​spam.org\"​) finished"​ << endl << endl; +
- wait2.store(false);​ +
- sleep(1);​ +
- }), +
- t2([&]{ +
- // cout << "About to invoke gethostbyname(\"​fc.put.poznan.pl\"​)"​ << endl; +
- hostent* ret = gethostbyname("​fc.put.poznan.pl"​);​ +
- // cout << "​gethostbyname(\"​fc.put.poznan.pl\"​finished"​ << endl << endl+
- wait1.store(false); +
- while(wait2.load());​ +
- // cout << ​"Printing the result of gethostbyname(\"​fc.put.poznan.pl\")" << endl; +
- print(ret); +
- }); +
-  +
- t1.join();​ +
- t2.join();​ +
- return 0;+
 } }
 </​code>​ </​code>​
Linia 56: Linia 37:
 <​html><​small></​html>​ <​html><​small></​html>​
 Kompilator GCC udostępnia wiele rozszerzeń "​łatających"​ braki standardów [[https://​gcc.gnu.org/​onlinedocs/​gcc/​C-Extensions.html]].\\ 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]]) ​+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.
  
 <code cpp> <code cpp>
 hostent he, *resptr; hostent he, *resptr;
- int status, retval; +int status, retval; 
- char buffer[4096];​ +char buffer[4096];​ 
- retval = gethostbyname_r("​fc.put.poznan.pl",​ &he, buffer, sizeof(buffer),​ &​resptr,​ &​status);​ +retval = gethostbyname_r("​cat.put.poznan.pl",​ &he, buffer, sizeof(buffer),​ &​resptr,​ &​status);​ 
- if(retval) +if(retval) 
- error(1,​0,"​gethostbyname_r error: %s", hstrerror(status));​ +    error(1,​0,"​gethostbyname_r error: %s", hstrerror(status));​ 
- if(!resptr) +if(!resptr) 
- error(1,​0,"​empty result"​);​ +    error(1,​0,"​empty result"​);​ 
- print(resptr);​ +print(resptr);</​code>​
-</​code>​+
 <​html></​small></​html>​ <​html></​small></​html>​
  
 ===== getaddrinfo ===== ===== 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''​.+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>​ <​html><​small></​html>​
 Składnia: <code cpp> Składnia: <code cpp>
-int getaddrinfo( ​                 // zwraca 0 (ok) lub -1 (błąd+int getaddrinfo( ​                 // zwraca 0 (ok) lub niezerowy kod ędu (można go przerobić na tekst używając gai_strerror)
     const char * ip_or_hostname, ​ // nazwa domenowa lub adres IP      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 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.     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)     struct addrinfo **res); ​      // pole na wynik (jeśli puste - nic nie znaleziono)
-</​code><​html></​small></​html>​Opis struktury ''​addrinfo''​ znajdziesz na stronie manuala do getaddringo (''​man getaddrinfo''​).+</​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: Przykłady użycia:
Linia 88: Linia 83:
 #include <​netdb.h>​ #include <​netdb.h>​
 #include <​cstdio>​ #include <​cstdio>​
-#include <​error.h>​+#include <​error.h> ​ // niestandardowa funkcja ułatwiająca obsługę błędów (dostępna w glibc)
 #include <​arpa/​inet.h>​ #include <​arpa/​inet.h>​
  
 int main(){ int main(){
     // Ustawienie "​podpowiedzi"​ - sterowanie jakie wyniki chcemy otrzymać:     // Ustawienie "​podpowiedzi"​ - sterowanie jakie wyniki chcemy otrzymać:
-    addrinfo hints {};+    addrinfo hints {};    // uwaga: puste 'list initialization'​ zeruje całą strukturę
     hints.ai_family ​  = AF_INET; ​        // tylko IPv4 (AF_INET)     hints.ai_family ​  = AF_INET; ​        // tylko IPv4 (AF_INET)
-    hints.ai_protocol = IPPROTO_TCP;     // protokół ​TCP+    hints.ai_protocol = IPPROTO_UDP;     // protokół ​UDP
     ​     ​
     // Zmienna w której będzie umieszczona lokalizacja wyniku w pamięci     // Zmienna w której będzie umieszczona lokalizacja wyniku w pamięci
     addrinfo * resolved;     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);​     int res = getaddrinfo("​pool.ntp.org",​ "​ntp",​ &hints, &​resolved);​
     ​     ​
Linia 127: Linia 125:
     ​     ​
     char ip[40]; // maks. długość IP(v6) jako tekst: 8 bloków po 4 znaki oddzielone ':'​     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){     for(addrinfo * it = resolved; it; it=it->​ai_next){
         // funkcja getnameinfo tłumaczy dowolny sockaddr na tekst         // funkcja getnameinfo tłumaczy dowolny sockaddr na tekst
Linia 141: Linia 140:
 //Zadanie 2.// Stwórz klienta TCP, który: //Zadanie 2.// Stwórz klienta TCP, który:
   * odczyta z listy argumentów adres i numer portu   * odczyta z listy argumentów adres i numer portu
-  * użyje funkcji ''​getaddrinfo''​ z pustymi podpowiedziami+  * użyje funkcji ''​getaddrinfo''​ z podpowiedzią żądającą protokołu TCP
   * utworzy socket korzystając z rodziny adresów zwróconej przez ''​getaddrinfo''​   * 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'' ​   ​   * połączy się funkcją connect korzystając ze struktury sockaddr i informacji o jej długości zwróconej przez ''​getaddrinfo'' ​   ​
sk2/sockets_netdbs.1509310854.txt.gz · ostatnio zmienione: 2017/10/29 22:00 przez jkonczak