深入了解域名解析API,域名解析API介绍。小编来告诉你更多相关信息。
域名解析API介绍
为网友解答域名解析API介绍方面的讲解,具体介绍如下:
为了便于记忆,有时候我们需要我们的程序可以使用域名和端口号去连接服务,这种情况下,我们需要使用 socket API gethostbyname 函数先把域名转换成 ip 地址,再使用 connect函数连接。
在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介绍的详细讲解,仅供大家参考建议!
阅读前请先查看【免责声明】本文内容由互联网用户自发贡献,该文观点仅代表作者本人,本站仅供展示。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至 1217266901@qq.com 举报,一经查实,本站将立刻删除。 转载请注明出处:https://www.jingfakeji.com/tech/56735.html