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

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

问题描述

一个经常被忽视的功能,不需要外部库,但基本上没有任何文件。

解决方案

更新(2010-10-11):linux手册页现在有getaddrinfo_a,您可以在这里找到: http ://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html



作为免责声明,我应该补充说,我对C很新,但不完全是一个新手,所以可能会有错误或错误的编码实践,请做正确的我(我的语法也吸吮)。



我个人不知道,直到我来到这篇文章由Adam Langley,我将给出一些代码片段来说明它的用法,并澄清一些可能不清楚的东西首次使用。使用此功能的好处是您可以回收在(), listen()等功能中可以使用的数据,如果正确,则不用担心关于ipv4 / v6。

所以从基础开始,从上面的链接(你需要链接到libanl(-lanl)):

这里是函数原型:

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




  1. 模式是GAI_WAIT可能不是你想要的)和GAI_NOWAIT进行异步查找

  2. gaicb 参数接受一组主机,以查找 ent 指定数组中有多少元素的参数

  3. sigevent 将负责告诉函数我们将如何被通知, li>

一个gaicb结构如下所示:

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

如果您熟悉getaddrinfo,那么这些字段对应于他们:

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

节点是ar_name字段,service是端口,hints参数对应于ar_request成员,结果存储在其余的位置。

现在您可以指定如何通过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. 您可以通过将_sigev_notify_设置为SIGEV_NONE
  2. 来忽略通知
  3. 您可以通过将sigev_notify设置为SIGEV_SIGNAL并将sigev_signo设置为所需信号来触发信号。请注意,当使用实时信号(SIGRTMIN - SIGRTMAX )时,始终通过宏和添加SIGRTMIN +2等使用它)可以通过sigev_value.sival_ptr或sigev_value.sival_int成员中的指针或值传递方向

  4. 您可以通过将sigev_notify设置为SIGEV_NONE
  5. $ b $在新线程中请求回调b

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



如果您使用select / poll / epoll / kqueue进行事件循环,则可能需要使用 signalfd 为方便起见。 Signalfd创建一个文件描述符,您可以在其中使用usuall事件轮询机制,如下所示:

  #define _GNU_SOURCE // yes不是那么标准的
#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); //我们阻塞信号
sfd = signalfd(-1,& mask,0);
//将其添加到事件队列
}
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; //指针传递给sigevent结构
//结果位于host-> ar_result成员
create_server(host);
}
}
void create_server(struct gaicb * host){
struct addrinfo * rp,* result;
int fd;

result = host-> ar_result; (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);
//错误检查丢失!

freeaddrinfo(host-> ar_request);
freeaddrinfo(result);
//你应该把你放在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; //我们不关心它的v4或v6
hints-> ai_socktype = SOCK_STREAM;
hints-> ai_flags = AI_PASSIVE;
//每个其他字段为NULL-d by calloc

host-> ar_service =8888; //我们将收听的端口
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();

//启动你的事件循环
return 0;
}

当然,你也可以使用一个简单的信号处理程序,看看 man 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天全站免登陆