Domanda

Una funzione spesso trascurata che non richiede librerie esterne, ma sostanzialmente non ha alcuna documentazione.

È stato utile?

Soluzione

AGGIORNAMENTO (2010-10-11):Le pagine man di Linux ora hanno la documentazione di getaddrinfo_a, puoi trovarla qui: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

Come disclaimer dovrei aggiungere che sono abbastanza nuovo in C ma non esattamente un principiante, quindi potrebbero esserci bug o cattive pratiche di codifica, per favore correggimi (e anche la mia grammatica fa schifo).

Personalmente non lo sapevo finché non mi sono imbattuto questo post di Adam Langley, fornirò alcuni frammenti di codice per illustrarne l'utilizzo e chiarire alcune cose che potrebbero non essere così chiare al primo utilizzo.Il vantaggio dell'utilizzo di questo è che si ottengono dati facilmente utilizzabili PRESA(), Ascoltare() e altre funzioni, e se fatto bene non dovrai preoccuparti nemmeno di ipv4/v6.
Quindi, per iniziare con le basi, come preso dal collegamento sopra (dovrai collegarti a libanl (-lanl)):
Ecco il prototipo della funzione:

int getaddrinfo_a(int mode, struct gaicb *list[], int ent, 
                  struct sigevent *);
  1. IL modalità è GAI_WAIT (che probabilmente non è quello che desideri) e GAI_NOWAIT per le ricerche asincrone
  2. IL gaicb l'argomento accetta un array di host da cercare con ent argomento che specifica quanti elementi ha l'array
  3. IL sigevent sarà responsabile di dire alla funzione come dobbiamo essere avvisati, ne parleremo più avanti tra poco

Una struttura gaicb assomiglia a questa:

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

Se hai familiarità con getaddrinfo, questi campi corrispondono in questo modo:

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

Il nodo è il campo ar_name, il servizio è la porta, l'argomentohints corrisponde al membro ar_request e il risultato viene archiviato nel resto.
Ora specifichi come vuoi essere avvisato attraverso la struttura 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. Puoi ignorare la notifica impostando _sigev_notify_ su SIGEV_NONE
  2. Puoi attivare un segnale impostando sigev_notify su SIGEV_SIGNAL e sigev_signo sul segnale desiderato.Si noti che quando si utilizza un segnale in tempo reale (SIGRTMIN-SIGRTMAX, usalo sempre tramite le macro e l'addizione SIGRTMIN+2 ecc.) è possibile passare un puntatore o un valore nel membro sigev_value.sival_ptr o sigev_value.sival_int rispettivamente
  3. Puoi richiedere una richiamata in un nuovo thread impostando sigev_notify su SIGEV_NONE

Quindi, in pratica, se vuoi cercare un nome host, imposta ar_name sull'host e imposta tutto il resto su NULLO, se vuoi connetterti a un host imposti ar_name e ar_service e se vuoi creare un server specifichi ar_service e il campo ar_result.Ovviamente puoi personalizzare il membro ar_request in base al contenuto del tuo cuore, guarda amico, ottieni informazioni per maggiori informazioni.

Se hai un ciclo di eventi con select/poll/epoll/kqueue potresti volerlo usare signalfd per comodità.Signalfd crea un descrittore di file sul quale è possibile utilizzare i consueti meccanismi di polling degli eventi in questo modo:

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

Ovviamente puoi usare anche un semplice gestore di segnale per questo lavoro, guarda sigazione dell'uomo per maggiori informazioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top