一、域名解析是什么?
域名解析是让我们可以通过网站的域名来找到它对应的IP地址,以便更加方便的访问我们所需访问的网站的一种服务。
它通过DNS服务器来进行,我们输入所想要访问的域名,将会通过DNS服务器来对输入域名进行解析得到对应IP地址,然后经过一系列的映射等,最终实现输入域名对相应网站的访问。
二、为什么要做域名解析?
我们先抛出一个问题:“你是否记得baidu.com域名对应的网站是什么?但你是否记得它对应的IP地址是什么?”,想必大多数人都知道它是百度的域名,但并不记得它对应的IP地址吧,所以由这个例子我们可以明白域名解析的一大优点:“便于记忆”,因为便于记忆,我们用户可以高效的访问想要访问的网站,也让我们使用起来更加合乎我们的思维模式。
我们再考虑到一些网站的IP地址经常会有变动,就算我们记住了,在下一次我们访问时有可能已经找不到对应的网站了,而通过域名解析即可以准确的解析出对应IP地址,使得我们访问起来更加方便与准确了。
三、如何进行域名解析?
1.通过gethostbyname()函数
通过man gethostbyname可以得到:
头文件:
#include <netdb.h>
#include <sys/socket.h> /* for AF_INET */
函数原型:
struct hostent *gethostbyname(const char *name);//可见返回值为hostent结构体
传入参数为const char *name,也就是我们所要解析的域名,返回值为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 */
}
由于可以对应发现char **h_addr_list解释是“list of addresses”,也就是我们所需要的地址,但是我们还需要用inet_ntop函数,将点分文本的IP地址转换为二进制网络字节序的IP地址,才可以让我们进一步使用。
实例代码:
if(!servip)
{
if((servhost = gethostbyname(servdn)) == NULL)//gethostbyname()仅用于ipv4
{
printf("Get host error: %s\n", strerror(errno));
return -1;
}
switch(servhost->h_addrtype)
{
case AF_INET:
case AF_INET6:
hostip = servhost->h_addr_list;
for(; *hostip != NULL; hostip++)
printf("IP:%s\n", inet_ntop(servhost->h_addrtype, *hostip, ipstr, sizeof(ipstr)));
servip = ipstr;
break;
default:
printf("Error address!\n");
break;
}
}
2.通过getaddrinfo()函数
通过man 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);
该函数既适用于ipv4,也适用于ipv6。
传入的参数分别为:const char *node为主机名或数字地址、const char *service为进制数的端口号或服务名、const struct addrinfo *hints为一个空指针或指向一个addrinfo结构的指针、struct addrinfo **res用于存放返回addrinfo结构链表的指针。返回一个指向addrinfo结构体类型的指针。
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;
};
其中sockaddr类型的ai_addr即是我们需要用到的地址信息,但是因为我们写的是ipv4要把sockaddr类型转化转换为sockaddr_in来使用。
实例代码:
if(!servip)
{
memset(&ainfo, 0, sizeof(ainfo));//ainfo为一个addrinfo结构体
ainfo.ai_flags = AI_PASSIVE;
ainfo.ai_family = AF_INET;
ainfo.ai_socktype = SOCK_STREAM;
ainfo.ai_protocol = 0;
getaddrfd = getaddrinfo(servdn, NULL, &ainfo, &res);
if(getaddrfd != 0) //判断函数getaddrinfo是否成功,成功将会返回0,不成功返回非0
{
printf("Getaddrinfo faliure:%s\n",strerror(errno));
return -1;
}
printf("Getaddrinfo successfully\n");
for (hand = res; hand != NULL; hand = hand->ai_next)
{
seraddr = (struct sockaddr_in *)hand->ai_addr;
printf("IP address: %s\n", inet_ntoa(seraddr->sin_addr));//用inet_ntop函数,将点分文本的IP地址转换为二进制网络字节序的IP地址,才可以让我们进一步使用
servip=inet_ntoa(seraddr->sin_addr);
}
freeaddrinfo(res);//释放空间
}
四、 总结
以上就是今天要讲的内容,本文主要通过gethostbyname()函数和getaddrinfo()函数来讲解了Linux下客户端程序域名解析的方法,也在本文中对域名解析的由来以及它的作用等进行了适当的介绍,希望这篇博客可以让同学们有所收获。
代码示例:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>
#include <netdb.h>
#include <sys/socket.h>
#define message "Hello, here is client!\n"
void print_usage(char *progname)
{
printf("%s usage: \n", progname);
printf("-i(--ipaddr): sepcify server IP address.\n");
printf("-d(--domain name): sepcify server domain name.\n");
printf("-p(--port): sepcify server port.\n");
printf("-h(--Help): print this help information.\n");
return ;
}
int main(int argc, char **argv)
{
int sockfd = -1;
int rv = -1;
struct sockaddr_in servaddr;//我们是ipv4
char *servip = NULL;
char *servdn = NULL;
struct hostent *servhost = NULL;
char **hostip = NULL;
char ipstr[32];
int port = 0;
char buf[1024];
int ch;
float temp;
char tstr[]="temperature: ";
char str[64];
struct option opts[] = {
{"ipaddr", required_argument, NULL, 'i'},
{"domain name", required_argument, NULL, 'd'},
{"port", required_argument, NULL, 'P'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
while( (ch=getopt_long(argc, argv, "i:d:p:h", opts, NULL)) != -1)//h不用加参数,所以后面不加":"
{
switch(ch)
{
case 'i':
servip=optarg;
break;
case 'd':
servdn=optarg;
break;
case 'p':
port=atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if( !(!servip ^ !servdn) || !port )
{
print_usage(argv[0]);
return 0;
}
if(!servip)//用gethostbyname函数做示例
{
if((servhost = gethostbyname(servdn)) == NULL)//gethostbyname()适用于ipv4
{
printf("Get host error: %s\n", strerror(errno));
return -1;
}
switch(servhost->h_addrtype)//判断addrtype
{
case AF_INET:
case AF_INET6:
hostip = servhost->h_addr_list;
for(; *hostip != NULL; hostip++)
printf("IP:%s\n", inet_ntop(servhost->h_addrtype, *hostip, ipstr, sizeof(ipstr)));
servip = ipstr;
break;
default:
printf("Error address!\n");
break;
}
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);//ipv4选AF_INET,为TCP所以选SOCK_STREAM,参数3自适应为0
if(sockfd < 0)
{
printf("Create socket failure: %s\n", strerror(errno));
return -2;
}
printf("Creact socket[%d] successfully!\n", sockfd);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(servip, &servaddr.sin_addr);
rv = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//我们servaddr是sockaddr_in类型的,而我们的connect中第二个参数需要sockaddr类型,所以用一个强制转换
if(rv < 0)
{
printf("Connect to server[%s:%d] failure: %s\n",
servip, port, strerror(errno));
return -2;
}
printf("Connect to server[%s:%d] successfully!\n", servip, port);
while(1)
{
rv = write(sockfd, message, strlen(message));
if(rv < 0)
{
printf("write to server by sockfd[%d] failure : %s\n",
sockfd, strerror(errno));
break;
}
/* rv = get_temperature(&temp);//如果想要实现温度上报需要自行实现get_temperature函数
if(rv<0)
{
printf("get temperature failure, return value: %d", rv);
break;
}
sprintf(str,"%s%f\n",tstr,temp);
rv = write(sockfd, str, strlen(str));
if(rv < 0)
{
printf("write to server by sockfd[%d] failure : %s\n",
sockfd, strerror(errno));
break;
} */
memset(buf, 0, sizeof(buf));
rv = read(sockfd, buf, sizeof(buf));
if(rv < 0)
{
printf("Read data from server by sockfd[%d] failure: %s\n",
sockfd, strerror(errno));
break;
}
else if(rv == 0)
{
printf("Socket[%d] get disconnected\n", sockfd);
break;
}
else if(rv > 0)
{
printf("Read %d bytes data from Server: %s\n",
rv, buf);
sleep(10);
}
}
close(sockfd);
}