Pergunta

Uma função frequentemente esquecida que não requer biblioteca externa, mas basicamente não possui qualquer documentação.

Foi útil?

Solução

ATUALIZAÇÃO (11/10/2010):As páginas de manual do Linux agora possuem documentação do getaddrinfo_a, você pode encontrá-la aqui: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

Como isenção de responsabilidade, devo acrescentar que sou muito novo em C, mas não exatamente um novato, então pode haver bugs ou práticas de codificação inadequadas. Corrija-me (e minha gramática também é uma droga).

Eu pessoalmente não sabia disso até que descobri esta postagem por Adam Langley, darei alguns trechos de código para ilustrar seu uso e esclarecer algumas coisas que podem não ficar tão claras no primeiro uso.Os benefícios de usar isso é que você recupera dados prontamente utilizáveis ​​em soquete(), ouvir() e outras funções e, se feito corretamente, você também não precisará se preocupar com ipv4/v6.
Então, para começar com o básico, conforme retirado do link acima (você precisará vincular ao libanl (-lanl)):
Aqui está o protótipo da função:

int getaddrinfo_a(int mode, struct gaicb *list[], int ent, 
                  struct sigevent *);
  1. O modo é GAI_WAIT (que provavelmente não é o que você deseja) e GAI_NOWAIT para pesquisas assíncronas
  2. O gaicb argumento aceita uma matriz de hosts para pesquisar com o ent argumento especificando quantos elementos o array possui
  3. O evento sige será responsável por informar à função como seremos notificados, mais sobre isso em um momento

Uma estrutura gaicb se parece com isto:

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

Se você estiver familiarizado com getaddrinfo, esses campos correspondem a eles da seguinte forma:

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

O nó é o campo ar_name, o serviço é a porta, o argumento de dicas corresponde ao membro ar_request e o resultado é armazenado no restante.
Agora você especifica como deseja ser notificado através da estrutura 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. Você pode ignorar a notificação definindo _sigev_notify_ como SIGEV_NONE
  2. Você pode acionar um sinal configurando sigev_notify para SIGEV_SIGNAL e sigev_signo para o sinal desejado.Observe que ao usar um sinal em tempo real (SIGRTMIN-SIGRTMAX, sempre use-o através das macros e adição SIGRTMIN+2 etc.) você pode passar um ponteiro ou valor no membro sigev_value.sival_ptr ou sigev_value.sival_int, respectivamente
  3. Você pode solicitar um retorno de chamada em um novo tópico definindo sigev_notify como SIGEV_NONE

Então, basicamente, se você quiser procurar um nome de host, defina ar_name como host e defina todo o resto como NULO, se você deseja se conectar a um host, defina ar_name e ar_service e, se desejar criar um servidor, especifique ar_service e o campo ar_result.É claro que você pode personalizar o membro ar_request como desejar, veja homem getaddrinfo para mais informações.

Se você tiver um loop de eventos com select/poll/epoll/kqueue você pode querer usar sinalfd Por conveniência.Signalfd cria um descritor de arquivo no qual você pode usar os mecanismos usuais de pesquisa de eventos como este:

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

É claro que você também pode usar um manipulador de sinal simples para esse trabalho, veja homem sigaction para mais informações.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top