2008-09-12 13 views
15

Una función que a menudo se pasa por alto y que no requiere biblioteca externa, pero básicamente no tiene documentación alguna.Cómo usar getaddrinfo_a para hacer una resolución asíncrona con glibc

+0

No veo cómo esta función es muy útil. Simplemente llamando 'pthread_create' con una función que realiza' getaddrinfo' seguido por cualquier código que haya puesto en la devolución de llamada parece mucho más fácil que configurar todas las estructuras de control para 'getaddrinfo_a'. –

+2

Definitivamente tenemos diferentes puntos de vista sobre cuál es el método "más fácil" para lograr esta funcionalidad, y tratar con hilos no es más fácil para mí – sztanpet

+0

Tratar con hilos solo será unas pocas líneas de código aquí, y de hecho 'getaddrinfo_a' internamente usa hilos de todos modos ... –

Respuesta

16

ACTUALIZACIÓN (2010-10-11): Linux-hombre páginas tienen ahora la documentación de la getaddrinfo_a, se puede encontrar aquí: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

Como un descargo de responsabilidad que debo añadir que estoy bastante nuevo a C, pero no exactamente a un novato, por lo que puede haber errores, o malas prácticas de codificación, por favor corrígeme (y mi gramática también apesta).

Yo personalmente no sabía nada hasta que encontré this post por Adam Langley, voy a dar algunos fragmentos de código para ilustrar el uso y aclarar algunas cosas que podrían no ser tan claras en el primer uso. Los beneficios de usar esto es que puede recuperar datos fácilmente utilizables en socket(), listen() y otras funciones, y si lo hace bien, no tendrá que preocuparse por ipv4/v6 tampoco.
Así que para empezar con lo básico, tomada desde el enlace anterior (que tendrá que enlazar con libanl (-lanl)):
Aquí es el prototipo de la función:

int getaddrinfo_a(int mode, struct gaicb *list[], int ent, 
        struct sigevent *); 
  1. El modo es o bien GAI_WAIT (que probablemente no es lo que quiere) y GAI_NOWAIT para búsquedas asíncronas
  2. el argumentogaicb acepta una gran variedad de anfitriones para buscar con el ent argumento que especifica cómo muchos elementos de la matriz tiene
  3. El sigevent será responsable de decirle a la función de la forma en que deben ser notificados, más sobre esto en un momento

una estructura gaicb se ve así:

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

Si está familiarizado con getaddrinfo, entonces estos campos corresponden a ellos, así:

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

El nodo es el campo ar_name, el servicio es el puerto, el argumento indirectos corresponde al miembro ar_request y el resultado se almacena en el resto.
Ahora especifique cómo desea que se le notifique a través de la estructura de 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. Puede ignorar la notificación a través _sigev_notify_ establecer a SIGEV_NONE
  2. Puede desencadenar una señal a través de la configuración sigev_notify a SIGEV_SIGNAL y sigev_signo a la señal deseada. Tenga en cuenta que cuando se utiliza una señal en tiempo real (SIGRTMIN - SIGRTMAX lo utilizan siempre a través de las macros y además SIGRTMIN 2, etc.) se pueden pasar a lo largo de un puntero o valor en el sigev_value.sival_ptr o sigev_value.sival_int miembro de respectivley
  3. Usted puede solicitar una devolución de llamada en un nuevo hilo a través de la configuración sigev_notify a SIGEV_NONE

Así que, básicamente, si desea buscar un nombre de host se establece ar_name al host y establecer todo lo demás para NULL , si desea conectarse a un host, configure ar_name y ar_service, y si desea crear un servidor, especifique ar_service y el campo ar_result. Por supuesto, puede personalizar el miembro ar_request al contenido de su corazón, consulte man getaddrinfo para obtener más información.

Si tiene un bucle de evento con select/poll/epoll/kqueue, puede usar signalfd para mayor comodidad. Signalfd crea un descriptor de archivo en el que puede utilizar los mecanismos de sondeo de eventos usuall así:

#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; 
} 

Por supuesto, puede utilizar un simple controlador de señal para este trabajo también, mirar a man sigaction para obtener más información.

+0

Si respondiste a tu pregunta (parece que descubriste cómo usarla), entonces debes marcarla como la respuesta 'aceptada'. Además, puede combinar la oración que tiene en su otra respuesta en esta, para que la información esté en un solo lugar. –

+0

Ya lo hice cuando escribí la respuesta, ni idea de por qué se revirtió. – sztanpet

+0

El enlace está roto para "esta publicación de Adam Langle" – Joakim

Cuestiones relacionadas