Comment utiliser getaddrinfo_a pour effectuer une résolution asynchrone avec la glibc

StackOverflow https://stackoverflow.com/questions/58069

  •  09-06-2019
  •  | 
  •  

Question

Une fonction souvent négligée qui ne nécessite aucune bibliothèque externe, mais qui n'a fondamentalement aucune documentation.

Était-ce utile?

La solution

MISE À JOUR (2010-10-11):Les pages de manuel Linux contiennent désormais une documentation sur getaddrinfo_a, vous pouvez la trouver ici : http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

En guise de clause de non-responsabilité, je dois ajouter que je suis assez nouveau en C mais pas vraiment un débutant, donc il peut y avoir des bugs ou de mauvaises pratiques de codage, veuillez me corriger (et ma grammaire est aussi nulle).

Personnellement, je n'en savais rien jusqu'à ce que je découvre ce post par Adam Langley, je vais donner quelques extraits de code pour illustrer son utilisation et clarifier certaines choses qui pourraient ne pas être si claires lors de la première utilisation.L'avantage de cette utilisation est que vous récupérez des données facilement utilisables dans prise(), écouter() et d'autres fonctions, et si c'est bien fait, vous n'aurez pas non plus à vous soucier d'ipv4/v6.
Donc, pour commencer avec les bases, tirées du lien ci-dessus (vous devrez créer un lien contre libanl (-lanl)) :
Voici le prototype de la fonction :

int getaddrinfo_a(int mode, struct gaicb *list[], int ent, 
                  struct sigevent *);
  1. Le mode est soit GAI_WAIT (ce qui n'est probablement pas ce que vous voulez) et GAI_NOWAIT pour les recherches asynchrones
  2. Le gaicb L'argument accepte un tableau d'hôtes à rechercher avec le ent argument spécifiant le nombre d'éléments du tableau
  3. Le événement important sera chargé d'indiquer à la fonction comment nous devons être avertis, nous en reparlerons dans un instant

Une structure gaicb ressemble à ceci :

struct gaicb {
    const char *ar_name;
    const char *ar_service;
    const struct addrinfo *ar_request;
    struct addrinfo *ar_result;
};

Si vous connaissez getaddrinfo, alors ces champs leur correspondent comme suit :

int getaddrinfo(const char *node, const char *service,
                const struct addrinfo *hints,
                struct addrinfo **res);

Le nœud est le champ ar_name, le service est le port, l'argument astuces correspond au membre ar_request et le résultat est stocké dans le reste.
Vous spécifiez maintenant comment vous souhaitez être averti via la structure sigevent :

struct sigevent {
    sigval_t sigev_value;
    int sigev_signo;
    int sigev_notify;
    void (*sigev_notify_function) (sigval_t);
    pthread_addr_t *sigev_notify_attributes;
};
  1. Vous pouvez ignorer la notification en définissant _sigev_notify_ sur SIGEV_NONE
  2. Vous pouvez déclencher un signal en définissant sigev_notify sur SIGEV_SIGNAL et sigev_signo sur le signal souhaité.Notez que lorsque vous utilisez un signal en temps réel (SIGRTMIN-SIGRTMAX, utilisez-le toujours via les macros et l'ajout SIGRTMIN+2 etc.), vous pouvez transmettre un pointeur ou une valeur dans le membre sigev_value.sival_ptr ou sigev_value.sival_int respectivement
  3. Vous pouvez demander un rappel dans un nouveau fil de discussion en définissant sigev_notify sur SIGEV_NONE

Donc, fondamentalement, si vous souhaitez rechercher un nom d'hôte, vous définissez ar_name sur l'hôte et définissez tout le reste sur NUL, si vous souhaitez vous connecter à un hôte, vous définissez ar_name et ar_service , et si vous souhaitez créer un serveur, vous spécifiez ar_service et le champ ar_result.Vous pouvez bien sûr personnaliser le membre ar_request à votre guise, regardez homme, obtenir une adresse pour plus d'informations.

Si vous avez une boucle d'événements avec select/poll/epoll/kqueue, vous souhaiterez peut-être utiliser signalfd pour plus de commodité.Signalfd crée un descripteur de fichier sur lequel vous pouvez utiliser les mécanismes habituels d'interrogation d'événements comme ceci :

#define _GNU_SOURCE //yes this will not be so standardish
#include <netdb.h>
#include <signal.h>
#include <sys/signalfd.h>

void signalfd_setup(void) {
    int sfd;
    sigset_t mask;

    sigemptyset(&mask);
    sigaddset(&mask, SIGRTMIN);
    sigprocmask(SIG_BLOCK, &mask, NULL); //we block the signal
    sfd = signalfd(-1, &mask, 0);
    //add it to the event queue
}
void signalfd_read(int fd) {
    ssize_t s;
    struct signalfd_siginfo fdsi;
    struct gaicb *host;

    while((s = read(fd, &fdsi, sizeof(struct signalfd_siginfo))) > 0){
    if (s != sizeof(struct signalfd_siginfo)) return; //thats bad
    host = fdsi.ssi_ptr; //the pointer passed to the sigevent structure
            //the result is in the host->ar_result member
            create_server(host);
    }
}
void create_server(struct gaicb *host) {
    struct addrinfo *rp, *result;
    int fd;

    result = host->ar_result;
    for(rp = result; rp != NULL; rp = rp->ai_next) {
        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        bind(fd, rp->ai_addr, rp->ai_addrlen);
        listen(fd, SOMAXCONN);
        //error checks are missing!

        freeaddrinfo(host->ar_request);
        freeaddrinfo(result);
        //you should free everything you put into the gaicb
    }
}
int main(int argc, char *argv[]) {
    struct gaicb *host;
    struct addrinfo *hints;
    struct sigevent sig;

    host = calloc(1, sizeof(struct gaicb));
    hints = calloc(1, sizeof(struct addrinfo));

    hints->ai_family = AF_UNSPEC; //we dont care if its v4 or v6
    hints->ai_socktype = SOCK_STREAM;
    hints->ai_flags = AI_PASSIVE;
    //every other field is NULL-d by calloc

    host->ar_service = "8888"; //the port we will listen on
    host->ar_request = hints;

    sig.sigev_notify = SIGEV_SIGNAL;
    sig.sigev_value.sival_ptr = host;
    sig.sigev_signo = SIGRTMIN;

    getaddrinfo_a(GAI_NOWAIT, &host, 1, &sig);

    signalfd_setup();

    //start your event loop
    return 0;
}

Vous pouvez bien sûr également utiliser un simple gestionnaire de signal pour ce travail, regardez homme sigaction pour plus d'informations.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top