2015-05-20 26 views
5

Piszę aplikację w języku C, która jest uruchamiana jako systemd service przy starcie (dystrybucja: Arch Linux) i która ma połączyć się z serwerem. Ponieważ aplikacja jest uruchamiana podczas rozruchu, w końcu zdarza się, że połączenie sieciowe nie zostało jeszcze ustanowione. To naturalnie prowadzi do niepowodzenia pierwszej funkcji, która wymaga jednej, która w moim przypadku to getaddrinfo.Jeśli polecenie getaddrinfo zakończy się niepowodzeniem, gdy zakończy się niepowodzeniem na zawsze (nawet po uruchomieniu sieci)

Tak więc pomyślałem, że po prostu napiszę pętlę, która wielokrotnie wywołuje getaddrinfo, aż do momentu, gdy sieć będzie gotowa. Niestety, stwierdziłem, że getaddrinfo nadal nie działa z name or service not known nawet po nawiązaniu połączenia.

Mogę wysłać polecenie ping do serwera za pomocą jego nazwy hosta, ale nadal nie będzie można tego zrobić za pomocą usługi getaddrinfo. Jeśli zatrzymam aplikację i uruchomię ją ponownie, wszystko będzie działać poprawnie. Jeśli połączenie sieciowe jest już ustanowione przed pierwszym połączeniem, działa również dobrze.

Wygląda na to, że jeśli getaddrinfo raz się nie powiodło, ponieważ sieć nie była gotowa, zakończy się niepowodzeniem na zawsze. Wydaje się, że nie zdajemy sobie sprawy z istniejącego połączenia. Podczas korzystania z wycofanego gethostbyname zachowanie jest takie samo.

Jaki jest powód takiego zachowania? Czy istnieje sposób na wymuszenie odświeżenia zmiennych wewnętrznych (jeśli takie istnieją) lub podobnych, które mogłyby wyjaśnić, dlaczego funkcja nadal uważa, że ​​nie ma połączenia? Czy istnieje inna funkcja, którą powinienem wcześniej wywołać, aby sprawdzić, czy sieć jest gotowa?

Chciałbym uniknąć opóźnienia, które czeka przez pewien czas, oczekując, że sieć zostanie później podłączona. Wolałbym też sprawdzić połączenie z poziomu mojej aplikacji i nie mieć najpierw skryptu bash, a następnie uruchomić aplikację.

Odpowiedz

4

Można zrozumieć odpowiedź kompilując następujący program testowy i postępując zgodnie z instrukcjami poniżej:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netdb.h> 
#include <stdio.h> 
#include <unistd.h> 

int main(int argc, char *argv[]) 
{ 
    while (1) 
    { 
     struct addrinfo *res; 
     int rc=getaddrinfo(argv[1], "http", NULL, &res); 

     printf("getaddrinfo returned %d\n", rc); 

     if (rc == 0) 
      freeaddrinfo(res); 

     sleep(1); 
    } 
} 

Przed uruchomieniem tego programu testowego:

  1. połączyć się z siecią.
  2. Zmień nazwę, tymczasowo /etc/resolv.conf na /etc/resolv.conf.save.
  3. Uruchom ten program testowy, używając dobrej nazwy hosta.
  4. Wkrótce po uruchomieniu programu testowego i rozpoczęciu drukowania kodów błędów zmień nazwę na /etc/resolv.conf.save na /etc/resolv.conf.
  5. Należy zauważyć, że program testowy nadal zgłasza awarie rozpoznawania nazw DNS.
  6. Jeśli jednak użyjesz CTRL-C i uruchomi się ponownie, program testowy będzie teraz wyświetlał poprawną rozdzielczość DNS.

Po odłączeniu i ponownym połączeniu z siecią stos sieci zmienia odpowiednio i aktualizuje /etc/resolv.conf. Ten plik konfiguracyjny jest wymagany przez funkcję rozpoznawania nazw DNS w bibliotece C. Biblioteka C odczytuje konfigurację DNS z /etc/resolv.conf po raz pierwszy i zapisuje ją w pamięci podręcznej. Nie sprawdza, przy każdym wyszukiwaniu, czy zawartość /etc/resolv.conf uległa zmianie.

Wreszcie:

  1. odrabianie zadania jest dodanie wywołanie res_init(), zdefiniowanego w resolv.h, do tego programu testowego, przeczytaj odpowiednią stronę człowieka i zobaczyć, co się dzieje. To jest twoja odpowiedź.
+0

Przez to miałem na myśli res_init() w pętli. –

+0

To faktycznie działa czasami (ale raczej rzadko). Zakładam, że działa, jeśli połączenie sieciowe jest już ustanowione, ale 'resolv.conf' nie został jeszcze zaktualizowany podczas uruchamiania aplikacji. Po aktualizacji 'resolv.conf', wywołanie' res_init' w pętli ładuje nową konfigurację, a następnie 'getaddrinfo'. Ale jeśli połączenie sieciowe nie jest gotowe do uruchomienia aplikacji, nadal nie działa, – kassiopeia

+0

Moje demo krok po kroku polega na uruchomieniu aplikacji, gdy w ogóle nie skonfigurowano rozdzielczości DNS, co miałoby miejsce, gdyby nie istniały połączenia sieciowe. Czy próbowałeś uruchomić kod demo za pomocą wywołania rs_init() bez połączenia sieciowego, a następnie łączenia się z siecią? Ten krótki mały kod demonstracyjny jest dość łatwy do przetestowania. Nie musisz niczego zakładać. –