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

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

问题描述

这是经常被忽略,无需外部库,但基本没有任何文件的功能。


解决方案

更新(2010-10-11):Linux手册的页面现在有getaddrinfo_a的文档,你可以在这里找到:<一href=\"http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html\">http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html

作为一个免责声明我要补充一点,我是很新的C,但不完全是一个新手,所以可能有错误,或不良编码实践,请大家指正(和我的语法吸过)。

我个人不知道它,直到我来到由亚当·兰利这个帖子,我给一些code片段来说明它的用法和澄清一些事情,可能不会在第一次使用清楚。使用这个的好处是,你回来()的中的插座()听随手可用的数据等功能,并且如果使用得当,你将不必担心关于支持IPv4 / v6的要么。结果
因此,为了从基础开始,从上面的链接采取(你需要对libanl(-lanl)链接):结果
下面是函数原型:

  INT getaddrinfo_a(INT模式,结构gaicb *名单[],INT耳鼻喉科,
                  结构的sigevent *);


  1. 模式的要么是GAI_WAIT(这可能不是你想要的),并为GAI_NOWAIT异步查找

  2. gaicb 的参数接受主机的阵列中的耳鼻喉科的参数指定有多少元素数组有
  3. 来查找
  4. 的sigevent 的负责告诉函数如何,我们得到通知,更多关于这个一会儿

一个gaicb结构是这样的:

 结构gaicb {
    为const char *的ar_name;
    为const char * ar_service;
    常量的struct addrinfo中的* ar_request;
    结构addrinfo中的* ar_result;
};

如果你熟悉的getaddrinfo,那么这些字段对应于他们,像这样:

  INT的getaddrinfo(为const char *节点,为const char *的服务,
                常量的struct addrinfo中的*提示,
                结构addrinfo中**水库);

节点是ar_name字段,服务是端口,所述提示的参数对应于ar_request构件和结果存储在其余部分。结果,
现在,您指定要如何通过sigevent结构即可通知:

 结构的sigevent {
    sigval_t sigev_value;
    INT sigev_signo;
    INT sigev_notify;
    无效(* 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成员respectivley值

  3. 您可以在一个新的线程通过sigev_notify设置SIGEV_NONE请求回调

所以基本上,如果你想查询你设置的ar_name主机并设置一切为主机名的 NULL 的,如果你想连接到你设定的ar_name和ar_service主机,如果你要创建您指定ar_service和ar_result领域的服务器。当然,你可以自定义ar_request成员,你的心内容,看看 男人的getaddrinfo 获取更多信息。

如果你有一个事件循环使用select /调查/ epoll的/ kqueue的,你可能想使用的 signalfd 方便。 Signalfd创建可以在其上使用u​​suall事件轮询机制,像这样一个文件描述符:

 的#define _GNU_SOURCE //这是不会那么standardish
#包括LT&;&netdb.h中GT;
#包括LT&;&signal.h中GT;
#包括LT&; SYS / signalfd.h&GT;无效signalfd_setup(无效){
    INT SFD;
    sigset_t口罩;    sigemptyset(安培;面罩);
    sigaddset(安培;面膜,SIGRTMIN);
    sigprocmask(SIG_BLOCK,&放大器;掩模,NULL); //我们阻挡信号
    SFD = signalfd(-1,&放大器;掩模,0);
    //将其添加到事件队列
}
无效signalfd_read(INT FD){
    ssiz​​e_t供S;
    结构signalfd_siginfo FDSI;
    结构gaicb *主机;    而((S =读(FD,和放大器; FDSI,的sizeof(结构signalfd_siginfo)))0){
    如果(S = sizeof的(结构signalfd_siginfo)!)回报; //那很糟
    主机= fdsi.ssi_ptr; //指针传递给sigevent结构即可;
            //结果是在主机的&GT; ar_result成员
            create_server(主机);
    }
}
无效create_server(结构gaicb *主机){
    结构addrinfo中的* RP,*结果;
    INT的fd;    结果=主机的&GT; ar_result;
    对(RP =结果; RP = NULL;!RP = RP-GT&; ai_next){
        FD =插座(RP-GT&; ai_family,RP-GT&; ai_socktype,RP-GT&; ai_protocol);
        绑定(FD,RP-GT&; ai_addr,RP-GT&; ai_addrlen);
        听(FD,SOMAXCONN);
        //错误检查缺失!        freeaddrinfo(主机的&GT; ar_request);
        freeaddrinfo(结果);
        //你应该释放所有你投入gaicb
    }
}
INT主(INT ARGC,CHAR *的argv []){
    结构gaicb *主机;
    结构addrinfo中的*提示;
    结构的sigevent SIG;    主机=释放calloc(1,sizeof的(结构gaicb));
    提示=释放calloc(1,sizeof的(结构addrinfo中));    hints-&GT; ai_family = AF_UNSPEC; //我们不关心,如果它的V4或V6
    hints-&GT; ai_socktype = SOCK_STREAM;
    hints-&GT;填上ai_flags = AI_PASSIVE;
    //每隔场,由calloc NULL-D    主机的&GT; ar_service =8888; //我们将监听的端口
    主机的&GT; ar_request =提示;    sig.sigev_notify = SIGEV_SIGNAL;
    sig.sigev_value.sival_ptr =主机;
    sig.sigev_signo = SIGRTMIN;    getaddrinfo_a(GAI_NOWAIT,&安培;主机,1,&安培; SIG);    signalfd_setup();    //启动事件循环
    返回0;
}

当然你也可以使用一个简单的信号处理这项工作过,看的 男人的sigaction 获取更多信息。

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

解决方案

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

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).

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. 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

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

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);

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. 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

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.

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

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天全站免登陆