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 <iostream> #include <netdb.h> #include <arpa/inet.h> #include <thread> #include <atomic> #include <unistd.h> using namespace std; void print(hostent* ret){ cout << ret->h_name << endl; for(auto it = ret->h_addr_list; *it; ++it){ cout << " " << inet_ntoa(*((in_addr*)*it)) << endl; } } int main(int argc, char **argv) { atomic<bool> wait1 {true}, wait2 {true}; std::thread t1([&]{ while(wait1.load()); // cout << "About to invoke gethostbyname(\"spam.org\")" << endl; 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; }
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)
Przykład użycia:
#include <netdb.h> (...) // // znajdzie dowolne adresy, w tym IPv6, nie ustawi portu // addrinfo * aio; // int res = getaddrinfo("fc.put.poznan.pl", 0, 0, &aio); // znajdzie tylko adresy IPv4, ustawi port 13 addrinfo * aio, aih {.ai_flags=0, .ai_family=AF_INET, .ai_socktype=SOCK_STREAM}; int res = getaddrinfo("fc.put.poznan.pl", "13", &aih, &aio); if(res) error(1, errno, "getaddrinfo failed"); if(!aio) error(1, 0, "empty result"); cout << "fc.put.poznan.pl" << endl; for(addrinfo * it = aio; ; it=it->ai_next){ cout << " " << inet_ntoa(((sockaddr_in*)it->ai_addr)->sin_addr) << endl; if(!it->ai_next) break; } freeaddrinfo(aio);