如何使用 getaddrinfo_a 与 glibc 进行异步解析 [英] How to use getaddrinfo_a to do async resolve with glibc

查看:41
本文介绍了如何使用 getaddrinfo_a 与 glibc 进行异步解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个经常被忽视的函数,不需要外部库,但基本上没有任何文档.

An often overlooked function that requires no external library, but basically has no documentation whatsoever.

推荐答案

UPDATE (2010-10-11):Linux 手册页现在有 getaddrinfo_a 的文档,你可以在这里找到: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

UPDATE (2010-10-11): The linux man-pages now have documentation of the getaddrinfo_a, you can find it here: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

作为免责声明,我应该补充一点,我对 C 很陌生,但并不完全是新手,所以可能存在错误或糟糕的编码实践,请纠正我(我的语法也很糟糕).

As a disclaimer I should add that I'm quite new to C but not exactly a newbie, so there might be bugs, or bad coding practices, please do correct me (and my grammar sucks too).

直到我发现 这篇文章 作者:Adam Langley,我将给出一些代码片段来说明它的用法,并澄清一些在第一次使用时可能不太清楚的事情.使用它的好处是您可以在 socket()listen() 和其他函数中取回数据,如果操作正确,您就不必担心关于 ipv4/v6.
因此,从上面的链接中获取的基础知识开始(您需要链接到 libanl (-lanl)):
这是函数原型:

I personally didn't know about it until I came upon this post by Adam Langley, I shall give a few code snippets to illustrate the usage of it and clarify some things that might not be that clear on first use. The benefits of using this is that you get back data readily usable in socket(), listen() and other functions, and if done right you won't have to worry about ipv4/v6 either.
So to start off with the basics, as taken from the link above (you will need to link against libanl (-lanl)) :
Here is the function prototype:

int getaddrinfo_a(int mode, struct gaicb *list[], int ent, 
                  struct sigevent *);

  1. mode 要么是 GAI_WAIT(这可能不是你想要的)和用于异步查找的 GAI_NOWAIT
  2. gaicb 参数接受要查找的主机数组,ent 参数指定该数组有多少个元素
  3. sigevent 将负责告诉函数如何通知我们,稍后会详细介绍
  1. The mode is either GAI_WAIT (which is probably not what you want) and GAI_NOWAIT for async lookups
  2. The gaicb argument accepts an array of hosts to lookup with the ent argument specifying how many elements the array has
  3. The sigevent will be responsible for telling the function how we are to be notified, more on this in a moment

gaicb 结构体如下所示:

A gaicb struct looks like this:

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

如果您熟悉 getaddrinfo,那么这些字段与它们对应如下:

If you're familiar with getaddrinfo, then these fields correspond to them like so:

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

节点为ar_name字段,service为端口,hints参数对应ar_request成员,结果存放在rest中.
现在您指定您希望如何通过 sigevent 结构获得通知:

The node is the ar_name field, service is the port, the hints argument corresponds to the ar_request member and the result is stored in the rest.
Now you specify how you want to be notified through the sigevent structure:

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. 您可以通过将 _sigev_notify_ 设置为 SIGEV_NONE 来忽略通知
  2. 您可以通过将 sigev_notify 设置为 SIGEV_SIGNAL 并将 sigev_signo 设置为所需信号来触发信号.请注意,当使用实时信号(SIGRTMIN-SIGRTMAX,始终通过宏和添加 SIGRTMIN+2 等使用它)时,您可以在 sigev_value.sival_ptr 或 sigev_value.sival_int 成员各自传递一个指针或值
  3. 您可以通过将 sigev_notify 设置为 SIGEV_NONE 来在新线程中请求回调
  1. You can ignore the notification via setting _sigev_notify_ to SIGEV_NONE
  2. You can trigger a signal via setting sigev_notify to SIGEV_SIGNAL and sigev_signo to the desired signal. Note that when using a real-time signal (SIGRTMIN-SIGRTMAX, always use it via the macros and addition SIGRTMIN+2 etc.) you can pass along a pointer or value in the sigev_value.sival_ptr or sigev_value.sival_int member respectivley
  3. You can request a callback in a new thread via setting sigev_notify to SIGEV_NONE

所以基本上,如果您想查找主机名,请将 ar_name 设置为主机并将其他所有内容设置为 NULL,如果您想连接到主机,则设置 ar_name 和 ar_service ,如果您想要创建一个您指定 ar_service 和 ar_result 字段的服务器.你当然可以根据你的心内容自定义 ar_request 成员,看看 man getaddrinfo 了解更多信息.

So basically if you want to look up a hostname you set ar_name to the host and set everything else to NULL, if you want to connect to a host you set ar_name and ar_service , and if you want to create a server you specify ar_service and the ar_result field. You can of course customize the ar_request member to your hearts content, look at man getaddrinfo for more info.

如果你有一个带有 select/poll/epoll/kqueue 的事件循环,你可能想要使用 signalfd 为方便起见.Signalfd 创建一个文件描述符,您可以在其上使用通常的事件轮询机制,如下所示:

If you have an event loop with select/poll/epoll/kqueue you might want to use signalfd for convenience. Signalfd creates a file descriptor on which you can use the usuall event polling mechanisms like so:

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

您当然也可以使用简单的信号处理程序来完成这项工作,请查看 man sigaction 了解更多信息.

You can of course use a simple signal handler for this job too, look at man sigaction for more info.

这篇关于如何使用 getaddrinfo_a 与 glibc 进行异步解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆