域名解析API介绍(深入了解域名解析API)

深入了解域名解析API,域名解析API介绍。小编来告诉你更多相关信息。域名解析API介绍为网友解答域名解析API介绍方面的讲解,具体介绍如下:为了便于记忆,有时候我们需要我们的程序可以使用域名和端口号去连

深入了解域名解析API,域名解析API介绍。小编来告诉你更多相关信息。

域名解析API介绍

为网友解答域名解析API介绍方面的讲解,具体介绍如下:

为了便于记忆,有时候我们需要我们的程序可以使用域名和端口号去连接服务,这种情况下,我们需要使用 socket API gethostbyname 函数先把域名转换成 ip 地址,再使用 connect函数连接。

域名解析API介绍(深入了解域名解析API)

在Linux系统上, gethostbyname函数的签名如下:

#include        struct hostent* gethostbyname(const char* name);

域名转换成 ip 时,转换结果存在一个 hostent 结构体中。

转换成功后的 ip 地址存放在 hostent 最后一个字段中,hostent 结构体类型定义如下:

struct hostent {    char*  h_name;            /* official name of host */    char** h_aliases;         /* alias list */    int    h_addrtype;        /* host address type */    int    h_length;          /* length of address */    char** h_addr_list;       /* list of addresses */}#define h_addr h_addr_list[0] /* for backward compatibility */
  • 字段h_name:地址的正式名称;
  • 字段h_aliases:地址的预备名称指针;
  • 字段h_addrtype:地址类型,通常是AF_INET;
  • 字段h_length:地址的长度,以字节数目为计量单位;
  • 字段h_addr_list:主机网络地址指针,网络字节顺序。其中,h_addr是字段h_addr_list中的第一地址。

注意:虽然h_addr_list[0]看起来是一个char* 类型,但实际上是一个uint32_t,这是 ip 地址的 32 bit 整数表示形式,如果需要转换成十进制点分法字符串再调用 inet_ntoa() 函数即可。

我们来看一段示例代码:

#include   #include #include #include #include #include //extern int h_errno;bool connect_to_server(const char* server, short port){    int hSocket = socket(AF_INET, SOCK_STREAM, 0);    if (hSocket == -1)        return false;    struct sockaddr_in addrSrv = { 0 };    struct hostent* pHostent = NULL;    //unsigned int addr = 0;    //如果传入的参数 server 的值是 somesite.com 这种域名域名形式则 if 条件成立, //接着调用 gethostbyname 解析域名为 4 字节的 ip 地址(整型) if (addrSrv.sin_addr.s_addr = inet_addr(server) == INADDR_NONE)    {         pHostent = gethostbyname(server);        if (pHostent == NULL)                  return false;                //当使用 gethostbyname 解析域名时可能会得到多个 ip 地址,一般最常用的使用第一个 ip 地址        addrSrv.sin_addr.s_addr = *((unsigned long*)pHostent->h_addr_list[0]);    }    addrSrv.sin_family = AF_INET;    addrSrv.sin_port = htons(port);    int ret = connect(hSocket, (struct sockaddr*)&addrSrv, sizeof(addrSrv));    if (ret == -1)        return false;    return true;}int main(){ if (connect_to_server(\"baidu.com\", 80))  printf(\"connect successfully.\\n\"); else  printf(\"connect error.\\n\");  return 0;}

深入了解域名解析API,域名解析API介绍。小编来告诉你更多相关信息。

域名解析API介绍

上述 connect_to_server 函数既可以支持直接传入域名,也可以传入 ip 地址:

connect_to_server(\"127.0.0.1\", 8888);connect_to_server(\"localhost\", 8888);connect_to_server(\"61.135.169.125\", 80);connect_to_server(\"baidu.com\", 80);

实际在使用gethostbyname函数时需要注意以下:

  • gethostbyname函数是不可重入函数,在 Linux 下建议使用gethostbyname_r函数替代;
  • gethostbyname在解析域名时,会阻塞当前执行线程的,直到得到返回结果;
  • 在使用 gethostbyname 函数出错时,你不能使用 errno 获取错误码信息(因此也不能使用 perror() 函数打印错误信息),你应该使用 h_errno 错误码(也可以调用 herror() 打印错误信息),herror() 函数签名如下:
void herror(const char *s);

在新的 Linux 系统中,gethostbyname 和 gethostbyaddr 一样,已经被标记为废弃的,你应该使用新的函数 getaddrinfo 去替代它们,getaddrinfo 签名如下:

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

getaddrinfo 函数调用成功返回 0,失败返回非 0 值,调用成功后结果存储在参数 res 中。

addrinfo 结构体定义如下:

struct addrinfo{    int              ai_flags;    int              ai_family;    int              ai_socktype;    int              ai_protocol;    socklen_t        ai_addrlen;    struct sockaddr* ai_addr;    char*       ai_canonname;    struct addrinfo* ai_next;};

如果你不再需要 res 这个变量,记得使用 freeaddrinfo 函数将其指向的资源释放:

void freeaddrinfo(struct addrinfo* res);

getaddrinfo 使用示例如下:

struct addrinfo hints = {0}; hints.ai_flags = AI_CANONNAME;hints.ai_family = family;hints.ai_socktype = socktype;struct addrinfo* res;int n = getaddrinfo(host, service, &hints, &res);if(n == 0){    //调用成功,使用 res        //释放 res 资源    freeaddr(res);}

getaddrinfo 函数不仅支持 ipv4,同时也支持 ipv6 的解析,redis-server 的源码中就使用了这个函数去解析 ipv4 和 ipv6 的地址(位于 net.c 文件中):

static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,                                   const struct timeval *timeout,                                   const char *source_addr) {    int s, rv, n;    char _port[6];  /* strlen(\"65535\"); */    struct addrinfo hints, *servinfo, *bservinfo, *p, *b;    int blocking = (c->flags & REDIS_BLOCK);    int reuseaddr = (c->flags & REDIS_REUSEADDR);    int reuses = 0;    long timeout_msec = -1;    servinfo = NULL;    c->connection_type = REDIS_CONN_TCP;    c->tcp.port = port;    /* We need to take possession of the passed parameters     * to make them reusable for a reconnect.     * We also carefully check we don\'t free data we already own,     * as in the case of the reconnect method.     *     * This is a bit ugly, but atleast it works and doesn\'t leak memory.     **/    if (c->tcp.host != addr) {        if (c->tcp.host)            free(c->tcp.host);        c->tcp.host = strdup(addr);    }    if (timeout) {        if (c->timeout != timeout) {            if (c->timeout == NULL)                c->timeout = malloc(sizeof(struct timeval));            memcpy(c->timeout, timeout, sizeof(struct timeval));        }    } else {        if (c->timeout)            free(c->timeout);        c->timeout = NULL;    }    if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {        __redisSetError(c, REDIS_ERR_IO, \"Invalid timeout specified\");        goto error;    }    if (source_addr == NULL) {        free(c->tcp.source_addr);        c->tcp.source_addr = NULL;    } else if (c->tcp.source_addr != source_addr) {        free(c->tcp.source_addr);        c->tcp.source_addr = strdup(source_addr);    }    snprintf(_port, 6, \"%d\", port);    memset(&hints,0,sizeof(hints));    hints.ai_family = AF_INET;    hints.ai_socktype = SOCK_STREAM;    /* Try with IPv6 if no IPv4 address was found. We do it in this order since     * in a Redis client you can\'t afford to test if you have IPv6 connectivity     * as this would add latency to every connect. Otherwise a more sensible     * route could be: Use IPv6 if both addresses are available and there is IPv6     * connectivity. */    if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {         hints.ai_family = AF_INET6;         if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {            __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));            return REDIS_ERR;        }    }    for (p = servinfo; p != NULL; p = p->ai_next) {addrretry:        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)            continue;        c->fd = s;        if (redisSetBlocking(c,0) != REDIS_OK)            goto error;        if (c->tcp.source_addr) {            int bound = 0;            /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */            if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {                char buf[128];                snprintf(buf,sizeof(buf),\"Can\'t get addr: %s\",gai_strerror(rv));                __redisSetError(c,REDIS_ERR_OTHER,buf);                goto error;            }            if (reuseaddr) {                n = 1;                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,                               sizeof(n)) ai_next) {                if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {                    bound = 1;                    break;                }            }            freeaddrinfo(bservinfo);            if (!bound) {                char buf[128];                snprintf(buf,sizeof(buf),\"Can\'t bind socket: %s\",strerror(errno));                __redisSetError(c,REDIS_ERR_OTHER,buf);                goto error;            }        }        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {            if (errno == EHOSTUNREACH) {                redisContextCloseFd(c);                continue;            } else if (errno == EINPROGRESS && !blocking) {                /* This is ok. */            } else if (errno == EADDRNOTAVAIL && reuseaddr) {                if (++reuses >= REDIS_CONNECT_RETRIES) {                    goto error;                } else {                    redisContextCloseFd(c);                    goto addrretry;                }            } else {                if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)                    goto error;            }        }        if (blocking && redisSetBlocking(c,1) != REDIS_OK)            goto error;        if (redisSetTcpNoDelay(c) != REDIS_OK)            goto error;        c->flags |= REDIS_CONNECTED;        rv = REDIS_OK;        goto end;    }    if (p == NULL) {        char buf[128];        snprintf(buf,sizeof(buf),\"Can\'t create socket: %s\",strerror(errno));        __redisSetError(c,REDIS_ERR_OTHER,buf);        goto error;    }error:    rv = REDIS_ERR;end:    freeaddrinfo(servinfo);    return rv;  // Need to return REDIS_OK if alright}

本节完。

上面为您介绍的域名解析API介绍的详细讲解,仅供大家参考建议!

本文内容由互联网用户自发贡献,该文观点仅代表作者本人。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至 5733401@qq.com 举报,一经查实,本站将立刻删除。本文链接:https://www.fajihao.com/i/270003.html

(0)
星空的头像星空
上一篇 2024-01-10
下一篇 2024-01-10

相关推荐