Une fonction souvent négligée qui ne nécessite pas de bibliothèque externe, mais qui n'a pratiquement aucune documentation.Comment utiliser getaddrinfo_a pour résoudre async avec la glibc
Répondre
MISE À JOUR (2010-10-11): La commande linux man-pages ont maintenant la documentation de l'getaddrinfo_a, vous pouvez le trouver ici: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html
Comme un avertissement, je dois ajouter que je suis tout à fait nouveau à C mais pas exactement un débutant, donc il pourrait y avoir des bugs, ou de mauvaises pratiques de codage, s'il vous plaît faites-moi corriger (et ma grammaire suce aussi). Personnellement, je ne savais pas à ce sujet jusqu'à ce que je suis tombé sur this post par Adam Langley, je vais donner quelques extraits de code pour illustrer l'utilisation de celui-ci et clarifier certaines choses qui pourraient ne pas être clair lors de la première utilisation. Les avantages de l'utilisation de ceci est que vous récupérez des données facilement utilisables dans socket(), listen() et d'autres fonctions, et si cela est fait correctement, vous ne devrez pas vous inquiéter de ipv4/v6 non plus.
Donc, pour commencer avec les bases, comme pris à partir du lien ci-dessus (vous devez créer un lien contre libanl (-lanl)):
Voici le prototype de fonction:
int getaddrinfo_a(int mode, struct gaicb *list[], int ent,
struct sigevent *);
- Le mode est soit GAI_WAIT (ce qui est probablement pas ce que vous voulez) et GAI_NOWAIT pour les recherches async
- le gaicb argument de accepte un tableau d'hôtes pour rechercher avec le ent l'argument spécifiant comment de nombreux éléments du tableau a
- Le sigevent sera responsable de dire la fonction comment nous devons être informés, plus à ce sujet dans un moment
Un struct gaicb ressemble à ceci:
struct gaicb {
const char *ar_name;
const char *ar_service;
const struct addrinfo *ar_request;
struct addrinfo *ar_result;
};
Si vous êtes familier avec getaddrinfo, ces champs correspondent à eux comme si:
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
Le noeud est le champ ar_name, le service est le port, l'argument hints correspond au membre ar_request et le résultat est stocké dans le reste.
Maintenant, vous indiquez comment vous voulez être averti par 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;
};
- Vous pouvez ignorer la notification par la mise en _sigev_notify_ à SIGEV_NONE
- Vous pouvez déclencher un signal via la mise sigev_notify à SIGEV_SIGNAL et sigev_signo au signal désiré. Notez que lors de l'utilisation d'un signal en temps réel (SIGRTMIN-SIGRTMAX, utilisez toujours via les macros et plus SIGRTMIN 2, etc.), vous pouvez passer le long d'un pointeur ou de la valeur dans le sigev_value.sival_ptr ou sigev_value.membre sival_int respectivley
- Vous pouvez demander un rappel dans un nouveau thread via la mise sigev_notify à SIGEV_NONE
Donc, fondamentalement, si vous voulez rechercher un nom d'hôte vous définissez ar_name à l'hôte et mettre tout le reste à NULL , si vous voulez vous connecter à un hôte, vous définissez ar_name et ar_service, et si vous voulez 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 contenu de coeurs, regardez man getaddrinfo pour plus d'informations.
Si vous avez une boucle d'événement avec select/poll/epoll/kqueue, vous pouvez utiliser signalfd pour plus de commodité. Signalfd crée un descripteur de fichier sur lequel vous pouvez utiliser les mécanismes de vote des événements usuall 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 utiliser un gestionnaire de signal simple pour ce travail aussi regarder man sigaction pour plus d'informations.
Si cela répond à votre question (vous semblez avoir compris comment l'utiliser), alors vous devriez la marquer comme la réponse «acceptée». En outre, vous pouvez combiner la phrase que vous avez dans votre autre réponse dans celui-ci de sorte que l'information est tout en un seul endroit. –
Déjà fait quand j'ai écrit la réponse, aucune idée de pourquoi il est revenu. – sztanpet
Lien est rompu pour "ce message par Adam Langle" – Joakim
Je ne vois pas comment cette fonction est très utile. Il suffit simplement d'appeler 'pthread_create' avec une fonction qui exécute' getaddrinfo' suivi de tout code que vous auriez mis dans le rappel. Cela semble beaucoup plus facile que de configurer toutes les structures de contrôle pour 'getaddrinfo_a'. –
nous avons certainement des vues divergentes sur ce qui est la méthode "plus facile" réaliser cette fonctionnalité, et traitant des threads n'est pas plus facile pour moi – sztanpet
Traiter des threads sera juste quelques lignes de code ici, et en fait 'getaddrinfo_a' utilise en interne des threads de toute façon ... –