域名系统一般指的是DNS协议,将我们填写的网址转换成点分十进制形式的ip地址(ipv4)。
在这里我们不讨论DNS服务器的细节,只需要知道它是udp协议之上的即可,下面我们来学习如何用函数完成域名转换的目的。
一、一组函数
struct hostent *gethostbyname (const char *__name)
name就是传入的字符串,如果网址是不合法的将返回null并且error设置为h_error,如果是合法的,将返回一个结构体:
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 from name server. */
#if defined __USE_MISC || defined __USE_GNU
# define h_addr h_addr_list[0] /* Address, for backward compatibility.*/
#endif
};
h_name是地址的规范名字
h_addrtype是主机类型,一般是AF_INET
h_length是4,32bit的ip地址是4字节
h_aliases是它所有的别名,一组char*字符串,以NULL结尾
h_addr_list是所有可用地址的字符串表示形式,也是以NULL结尾
不多说,写个小程序试一下。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main(){
hostent* hostent_ptr=gethostbyname("www.baidu.com");
cout<h_name<h_length<h_addrtype==AF_INET)<h_aliases;
while(*p1){
cout<<"#"<<*p1<h_addr_list;*pp!= nullptr;++pp){
cout<<"address:"<
运行结果为:
www.a.shifen.com
4
is AF_INET? true
aliases:
#www.baidu.com
address:61.135.169.125address:61.135.169.121
这样就得到了百度的ip和正式的名。
二、获取服务名
主要是以下两个函数:
struct servent *getservbyname (const char *__name, const char *__proto);
第一个参数是服务名,比如ftp,ntp,http之类的。
第二个参数是运输层协议名,可以填tcp/udp或nullptr。
下面看一个例子:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main(){
servent* sp=getservbyname("http", nullptr);
cout<<"name:"<s_name<s_aliases;*p;++p){
cout<<"#"<<*p<s_proto<s_port)<
输出为:
name:http
aliases:
#www
#www-http
protocol:tcp
port:80
可以看出http有俩别名,传入nullptr时候会选择tcp作为协议
值得注意的是返回的段口号是网络字节序,所以要用ntoh转换。
如果我们用的是下面的函数:
struct servent *getservbyport (int __port, const char *__proto)
注意,传入端口号的时候应该用hton转换
就是根据端口号查看对应的服务:
int main(){
/*getserbyport*/
servent* sp=getservbyport(htons(80), nullptr);
cout<<"name:"<s_name<s_aliases;*p;++p){
cout<<"#"<<*p<s_proto<s_port)<
上面的函数会返回一样的结果。
三、现代标准——getaddrinfo
此函数同时支持ipv4和ipv6,能够处理名字到地址,服务到端口两种转换,
int getaddrinfo (const char *__restrict __name,
const char *__restrict __service,
const struct addrinfo *__restrict __req,
struct addrinfo **__restrict __pai)
第一个参数是主机名或地址字符串;
第二个参数是服务名或者十进制端口号;
第三个参数是hint保存期望得到的返回类型
第四个是用于保存的指针,结果将保存为一个链表;
如果成功则返回0,否则为其他值
下面依然以百度为例写一个例子:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main(){
addrinfo hints,*res;
bzero(&hints,sizeof(hints));
hints.ai_family=AF_INET;
hints.ai_flags=AI_CANONNAME;
getaddrinfo("www.baidu.com","80",&hints,&res);
addrinfo* temp=res;
while(temp){
sockaddr_in* addr=(sockaddr_in*)temp->ai_addr;
temp=temp->ai_next;
char buf[30];
cout<sin_port)<<";"<sin_addr,buf,sizeof(buf))<
运行结果为:
80;61.135.169.121
80;61.135.169.121
80;61.135.169.121
80;61.135.169.125
80;61.135.169.125
80;61.135.169.125
实际上只有两个ip剩下都是重复的,我们应当对这个链表进一步处理。
最后应当使用freeaddrinfo 来把整个链表释放掉。