IT袋

当前位置:主页 > 经验教程 > 建站编程 >

域名解析API介绍

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

时间:2024-01-09 15:57:48 来源:IT袋 作者:马勇
导读:域名解析API介绍,上述  connect_to_server  函数既可以支持直接传入域名,也可以传入 ip 地址: connect_to_server("127.0.0.1", 8888);connect_to_server("localhost", 8888);connect_to_server("61.135.

域名解析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 <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
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)) < 0) {
                    goto error;
                }
            }
            for (b = bservinfo; b != NULL; b = b->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
}

本节完。

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

相关阅读

  • 如何注册公司网站域名 关于申请建立网站

    如何注册公司网站域名 关于申请建立网站

    今日重点为您介绍如何注册公司网站域名和关于申请建立网站方面的内容,一定能解决您的问题的,一起来了解吧! 很多企业都开始建网站了,但是有很多小企业没有注册过,所以不太明白,

  • 自己制作网站需要多少钱 开网站需要投资的费用

    自己制作网站需要多少钱 开网站需要投资的费用

    今天小编详解自己制作网站需要多少钱和开网站需要投资的费用方面的介绍,接下来IT袋带大家一起了解。 做一个网站,所花的几百块钱,只是一个模板的基础费用,你还要去做一些设置工作

  • 服务器的作用和用途 常见的服务器种类介绍

    服务器的作用和用途 常见的服务器种类介绍

    关于服务器的作用和用途和常见的服务器种类介绍的介绍,相关内容具体如下: 众所周知,机房是服务器托管商的重要资源,选择一家靠谱的服务器托管商就必须要对IDC其机房进行考察和参观

  • 做网站大概要多少钱 网页制作费用了解

    做网站大概要多少钱 网页制作费用了解

    为关注IT袋网的网友们详解做网站大概要多少钱和网页制作费用了解的内容,接下来IT袋带大家一起了解。 着互联网的普及和发展,网站建设已经成为一项必不可少的任务。然而,网站建设费用