resolucion - Cómo usar getaddrinfo_a para hacer una resolución asíncrona con glibc
modelo de resolucion en word (1)
ACTUALIZACIÓN (10-10-2010) : Las páginas man de Linux ahora tienen documentación de getaddrinfo_a, puedes encontrarla aquí: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a .3.html
Como una exención de responsabilidad, debo agregar que soy bastante nuevo para C, pero no exactamente como un novato, por lo que podría haber errores, o malas prácticas de codificación, por favor corrígeme (y mi gramática también apesta).
Personalmente no lo supe hasta que encontré esta publicación de Adam Langley. Daré unos pocos 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 comenzar con lo básico, como se toma del enlace de arriba (necesitarás vincular con libanl (-lanl)):
Aquí está el prototipo de la función:
int getaddrinfo_a(int mode, struct gaicb *list[], int ent,
struct sigevent *);
- El modo es GAI_WAIT (que probablemente no es lo que desea) y GAI_NOWAIT para búsquedas asincrónicas
- El argumento gaicb acepta una matriz de hosts para buscar con el argumento ent especificando cuántos elementos tiene la matriz
- El sigevent será responsable de decirle a la función cómo seremos 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, estos campos les corresponden de la siguiente manera:
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 especifica cómo desea recibir notificaciones a través de la estructura 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;
};
- Puede ignorar la notificación configurando _sigev_notify_ en SIGEV_NONE
- Puede disparar una señal mediante la configuración de sigev_notify a SIGEV_SIGNAL y sigev_signo a la señal deseada. Tenga en cuenta que cuando utilice una señal en tiempo real ( SIGRTMIN - SIGRTMAX , utilícela siempre a través de las macros y agregue SIGRTMIN +2, etc.) puede pasar un puntero o valor en el miembro sigev_value.sival_ptr o sigev_value.sival_int respectivley
- Puede solicitar una devolución de llamada en un nuevo hilo mediante la configuración de sigev_notify a SIGEV_NONE
Así que, básicamente, si desea buscar un nombre de host, establezca ar_name en el host y configure todo lo demás en NULL , si desea conectarse a un host, configure ar_name y ar_service, y si desea crear un servidor, especifique ar_service y el ar_result campo. 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 usar los mecanismos de sondeo de eventos habituales de esta manera:
#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, también puedes utilizar un manejador de señal simple para este trabajo, mira hombre sigaction para más información.
Una función que a menudo se pasa por alto y que no requiere una biblioteca externa, pero básicamente no tiene documentación alguna.