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 [2022/10/17 22:15]
jkonczak [getaddrinfo]
sk2:sockets_netdbs [2024/10/18 09:12] (aktualna)
jkonczak [getaddrinfo]
Linia 31: Linia 31:
 </​code>​ </​code>​
  
-//Zadanie ​1.// Przetestuj powyższy kod. Zastanów się jak uniknąć problemu, który pokazuje powyższy kod.+~~Zadanie.#~~ Przetestuj powyższy kod. Zastanów się jak uniknąć problemu, który pokazuje powyższy kod.
  
 ===== Rozszerzenia GNU ===== ===== Rozszerzenia GNU =====
  
-<​html>​<small></​html>+<​small>​
 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]].\\
-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.+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>
Linia 49: Linia 49:
     error(1,​0,"​empty result"​);​     error(1,​0,"​empty result"​);​
 print(resptr);</​code>​ print(resptr);</​code>​
-<​html>​</small></​html>+</​small>​
  
 ===== 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''​. Poza ''​getaddrinfo''​ standard wprowadza też m. inn. funkcję do tłumaczenia ''​sockaddr*''​ na tekst – ''​getnameinfo''​. 
  
-<​html><​small></​html>​ +Standard POSIX wprowadza funkcję ''​getaddrinfo'',​ która pozwala na bezpieczne 
-Składnia: ​<code cpp>+tłumaczenie nazwy domenowej (lub adresu IP) z tekstu na odpowiedni format dla 
 +funkcji sieciowych (wskaźnik na strukturę ''​sockaddr''​). Funkcja działa zarówno 
 +IPv4 i IPv6.\\ 
 +''​getaddrinfo''​ przygotowuje wszystkie argumenty potrzebne funkcjom takim jak 
 +''​socket'',​ ''​connect''​ czy ''​bind''​. 
 + 
 +Funkcja ''​getaddrinfo''​ sama alokuje pamięć dla wyników, stąd wyniki muszą być 
 +zwolnione ręcznie funkcją ''​freeaddrinfo''​. 
 + 
 +Standard POSIX wprowadza też m. inn. wygodną funkcję do tłumaczenia ''​sockaddr*''​ 
 +na tekst – funkcję ''​getnameinfo''​. 
 + 
 +<​small>​ 
 +Składnia ''​getaddrinfo'':​ 
 +<​html><​div style=margin-top:​-1.2em></​div></​html>​ 
 +<code cpp>
 int getaddrinfo( ​                 // zwraca 0 (ok) lub niezerowy kod błędu (można go przerobić na tekst używając gai_strerror) 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 * ip_or_hostname, ​ // nazwa domenowa lub adres IP 
Linia 63: Linia 77:
 </​code>​ </​code>​
  
-Definicja struktury ''​addrinfo'':<​code cpp>+Definicja struktury ''​addrinfo'':​ 
 +<​html><​div style=margin-top:​-1.2em></​div></​html>​ 
 +<code cpp>
 struct addrinfo { struct addrinfo {
-    int ai_flags;+    int ai_flags; ​ // pole flag, używane jeśli struktura ma być podpowiedziami dla getaddrinfo – patrz manual 
 +    ​
     int ai_family; ​   // ai_family, ai_socktype i ai_protocol mają znaczenie identyczne jak     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_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)     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 +    ​socklen_t ai_addrlen; ​     //  ai_addr to adres o długości ai_addrlen który może być 
-    char *ai_canonname;​+    struct sockaddr *ai_addr; ​ //          użyty w funkcji connect lub bind 
 +    ​ 
 +    char *ai_canonname; ​ // wypełniane (tylko jeśli podano odpowiednią flagę) nazwą o którą zapytano  
 +    ​
     struct addrinfo *ai_next; ​ // struktura addrinfo tworzy jednokierunkową linked listę, ai_next wskazuje na jej następny element ​     struct addrinfo *ai_next; ​ // struktura addrinfo tworzy jednokierunkową linked listę, ai_next wskazuje na jej następny element ​
-};</​code><​html></​small></​html>​+};</​code>​ 
 + 
 +Składnia ''​getnameinfo'':​ 
 +<html><div style=margin-top:​-1.2em></​div></​html>​ 
 +<code cpp> 
 +int getnameinfo( ​                // zwraca 0 (ok) lub niezerowy kod błędu (można go przerobić na tekst używając gai_strerror) 
 +    const struct sockaddr *addr, // adres (IPv4 lub IPv6) do przerobienia na tekst 
 +    socklen_t addrlen, ​          // ilość bajtów ​ powyższego adresu  
 +                                 // 
 +    char * host,                 // wskaźnik na bufor do którego zostanie wpisany adres IP (lub nazwa domenowa) jako tekst 
 +    size_t hostLen, ​             // ilość miejsca w powyższym buforze 
 +                                 // ​  (dla zmiany IP na tekst powinien wystarczyć bufor o długości NI_MAXHOST) 
 +    char * port,                 // wskaźnik na bufor do którego zostanie wpisany numer (lub nazwa) portu jako tekst 
 +    size_t portLen, ​             // ilość miejsca w powyższym buforze 
 +                                 // ​  (dla zmiany portu na tekst powinien wystarczyć bufor o długości NI_MAXSERV) 
 +    int flags); ​                 // pole flag, pozwalające wybrać m. inn. czy IP i port mają być tłumaczone na numery czy nazwy 
 +</​code>​ 
 +</​small>​ 
 + 
 +Pełen opis funkcji i struktur znajdziesz na stronie manuala do getaddrinfo (''​man getaddrinfo''​) 
 +<small> 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]]])</​small>.
  
-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''​ i ''​getnameinfo''​ w razie sukcesu zwraca ''​0'',​ a w 
-Uwaga: ''​getaddrinfo''​ w razie sukcesu zwraca ''​0'',​ a w przypadku błędu zwraca jego kod.+przypadku błędu zwraca jego kod. Czytelny dla człowieka komunikat powiązany z 
 +kodem błędu można uzyskać funckją ''​gai_strerror''​. 
 +Zwróć uwagę, że to odbiega od typowej konwencji POSIX.
  
 Przykłady użycia: Przykłady użycia:
Linia 82: Linia 126:
 #include <​netdb.h>​ #include <​netdb.h>​
 #include <​cstdio>​ #include <​cstdio>​
-#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>​
  
Linia 94: Linia 137:
     addrinfo * resolved;     addrinfo * resolved;
     ​     ​
-    // Z sieci PP proszę zmienić "​pool.ntp.org"​ na "​onet.pl"​ - Dział Obsługi i Eksploatacji z niezrozumiałych przyczyn cenzuruje odpowiedzi z DNS +    // 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);​
     ​     ​
-    if(res) ​error(1,0,"​Getaddrinfo failed: %s\n", gai_strerror(res));​ +    if(res) ​{fprintf(stderr, "​Getaddrinfo failed: %s\n", gai_strerror(res)); ​return ​1;}
-    if(!resolved) error(1,​0,"​Empty result\n"​);+
     ​     ​
     for(addrinfo * it = resolved; it; it=it->​ai_next){     for(addrinfo * it = resolved; it; it=it->​ai_next){
Linia 109: Linia 153:
     return 0;     return 0;
 } }
-</​code><​code cpp gai2.cpp>#​include <​netdb.h>​+</​code>​ 
 +<code cpp gai2.cpp>​ 
 +#include <​netdb.h>​
 #include <​cstdio>​ #include <​cstdio>​
-#include <​error.h>​ 
  
 int main(){ int main(){
Linia 118: Linia 163:
     int res = getaddrinfo("​ietf.org",​ nullptr, nullptr, &​resolved);​     int res = getaddrinfo("​ietf.org",​ nullptr, nullptr, &​resolved);​
     ​     ​
-    if(res) ​error(1,0,"​Getaddrinfo failed: %s\n", gai_strerror(res));​ +    if(res) ​{fprintf(stderr, "​Getaddrinfo failed: %s\n", gai_strerror(res)); ​return ​1;}
-    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 ':'​     char ip[40]; // maks. długość IP(v6) jako tekst: 8 bloków po 4 znaki oddzielone ':'​
Linia 126: Linia 170:
         // funkcja getnameinfo tłumaczy dowolny sockaddr na tekst         // funkcja getnameinfo tłumaczy dowolny sockaddr na tekst
         res = getnameinfo(it->​ai_addr,​ it->​ai_addrlen,​ ip, 40, nullptr, 0, NI_NUMERICHOST);​         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));​+        if(res) ​{fprintf(stderr, "​Getnameinfo failed: %s\n", gai_strerror(res)); ​return 1;}
         else printf("​%40s (socktype: %d, proto: %d)\n",​ ip, it->​ai_socktype,​ it->​ai_protocol);​         else printf("​%40s (socktype: %d, proto: %d)\n",​ ip, it->​ai_socktype,​ it->​ai_protocol);​
     }     }
Linia 133: Linia 177:
     ​     ​
     return 0;     return 0;
-}</​code>​+} 
 +</​code>​
  
-//Zadanie ​2.// Stwórz klienta TCP, który:+~~Zadanie.#~~ 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 podpowiedzią żądającą protokołu TCP   * użyje funkcji ''​getaddrinfo''​ z podpowiedzią żądającą protokołu TCP
-  * utworzy ​socket ​korzystając z rodziny adresów zwróconej przez ''​getaddrinfo''​ +  * utworzy ​gniazdo ​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''​ 
 +<​html><​div style=margin-top:​-1.2em></​div></​html>​ 
 +W tym zadaniu wszystkie argumenty funkcji ''​socket''​ i wszystkie poza deskryptorem 
 +gniazda argumenty funkcji ''​connect''​ mają być brane z wyników funkcji ''​getaddrinfo''​ 
 + 
 +<​small>​ 
 +~~Zadanie.#​~~  
 +Zmień kod ostatniego zadania z poprzedniego tematu (serwer UDP wysyłający każdą 
 +wiadomość do wszystkich od których wcześniej coś dostał) tak, by serwer w treść 
 +każdej wiadomości wstawiał informację skąd przyszła. Tłumacz adres IP na nazwę 
 +domenową, ale numer portu pozostaw jako liczbę. 
 +</​small>​
sk2/sockets_netdbs.1666037704.txt.gz · ostatnio zmienione: 2022/10/17 22:15 przez jkonczak