Linux下客户端程序的域名解析


一、域名解析是什么?

域名解析是让我们可以通过网站的域名来找到它对应的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);
}
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邓永豪

打赏一下,好运来敲门!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值