行业知识
《TCP/IP网络编程》(第八章)域名及网络地址
DNS(Domain Name System,域名系统) 是互联网上用于将域名和IP地址相互映射的一个分布式数据库系统。它的主要作用是让人们能够使用易于记忆的域名(如 www.example.com)来访问互联网上的资源,而不需要记住复杂的数字IP地址(如 192.0.2.1)。
1.DNS请求域名IP地址
所有计算机中都记录着默认DNS服务器地址,通过这个默认DNS服务器可以得到相应域名的IP地址信息,下图是请求过程:
计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息,若该DNS服务器无法解析,则会逐级向上询问DNS服务器,如果一直都无法解析,则最终会到达根服务器,它知道该向那个DNS服务器询问,得到IP地址信息后原路返回,然后通过IP地址向目标服务器发送请求。
2.程序编写中需要使用域名吗?
- 用户友好性: 域名通常比IP地址更易于记忆和识别,使用域名可以提高用户体验。
- 可扩展性: 域名允许在不更改程序代码的情况下更换服务器IP地址,这在服务器迁移或扩展时非常有用。
- 负载均衡: 通过域名,可以更容易地实现负载均衡,将请求分发到多个服务器上。
- 安全性: 域名可以与SSL/TLS证书结合使用,提供安全的HTTPS连接,保护数据传输的安全。
- 易于管理: 使用域名可以简化网络配置和维护,因为域名解析和管理通常比IP地址更简单。
3.利用域名获取IP地址
这里将介绍gethostbyname()和getaddrinfo()两种函数,使用这些函数可以通过传递字符串格式的域名,获取IP地址
①Linux系统
gethostbyname()函数是早期的网络编程API,用于将主机名转换为IP地址,只支持IPv4地址,并且返回的是一个静态的hostent结构。
struct hostent *gethostbyname(const char *name);//成功时返回hostent结构体地址,失败时返回NULL指针//name表示要查询的域名struct hostent { char *h_name; //官方域名 char **h_aliases; //其他域名,同一IP可以绑定多个域名 int h_addrtype; //表示主机地址的类型,最常见的是AF_INET,表示IPv4地址。 int h_length; //表示每个地址的字节长度 char **h_addr_list; //一个指向字符指针数组的指针,数组中的每个元素都指向一个IP地址 //因为用户较多的网站会给一个域名分配多个IP,利用多个服务器进行负载均衡}; #include #include #include #include #include #include void error_handling(char *message);int main(int argc, char *argv[]){ int i; struct hostent *host; if(argc != 2){ printf("Usage : %s \n", argv[0]); exit(1); } // 使用gethostbyname()函数获取主机信息 host = gethostbyname(argv[1]);// argv[1]是命令行输入的主机名或IP地址 if(!host){ error_handling("gethostbyname() error"); } // 打印主机的官方名称 printf("Official name : %s \n", host->h_name); for(i = 0; host->h_aliases[i]; i++){ printf("Aliases %d : %s \n", i+1, host->h_aliases[i]);// 打印主机的所有别名 } printf("Address type : %s \n", (host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6"); for(i=0; host->h_addr_list[i]; i++)// 打印所有IP地址 printf("IP addr %d : %s \n", i+1, inet_ntoa(*(struct in_addr*)host->h_addr_list[i])); inet_ntoa(*(struct in_addr*)host->h_addr_list[i]); return 0;}void error_handling(char *msg){ fputs(msg, stderr); fputc('\n', stderr); exit(1);} getaddrinfo()函数是一个现代的、可扩展的网络地址解析函数,它支持IPv4和IPv6,这里只给出基本语法
int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);//nodename: 要查询的域名//servname: 要查询的服务名,可以是NULL,表示使用默认端口。//hints: 指向addrinfo结构的指针,该结构提供了查询的选项,如地址族(IPv4或IPv6)、套接字类型(如流套接字SOCK_STREAM)、协议类型等。//res: 函数返回时,指向一个addrinfo结构链表的指针,每个结构包含一个解析后的地址。struct addrinfo { int ai_flags; /* 特殊选项标志 */ int ai_family; /* 地址族(如AF_INET,AF_INET6) */ int ai_socktype; /* 套接字类型(如SOCK_STREAM) */ int ai_protocol; /* 协议(如IPPROTO_TCP) */ socklen_t ai_addrlen; /* 结构ai_addr的长度 */ struct sockaddr *ai_addr; /* 结构ai_addr的指针 */ char *ai_canonname; /* 规范主机名 */ struct addrinfo *ai_next; /* 下一个addrinfo结构的指针 */}; ②Windows系统
gethostbyname()和getaddrinfo()两种函数在Windows系统中的语法,和Linux系统中完全相同,这里就不再赘述,直接给出示例代码:
gethostbyname()函数
#include #include #include void error_handling(char *message);int main(int argc, char *argv[]){ WSADATA wsaData; int i; struct hostent *host; if(argc != 2){ printf("Usage : %s \n", argv[0]); exit(1); } if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) error_handling("WSAStartup() error!"); host = gethostbyname(argv[1]); if(!host){ error_handling("gethostbyname() error!"); } printf("Official name : %s \n", host->h_name); for(i = 0; host->h_aliases[i]; i++) printf("Aliases %d : %s \n", i+1, host->h_aliases[i]); printf("Address type : %s \n", (host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6"); for(i = 0; host->h_addr_list[i]; i++) printf("IP addr %d : %s \n", i+1, inet_ntoa(*(struct in_addr*)host->h_addr_list[i])); WSACleanup(); return 0;}void error_handling(char *message){ fputs(message, stderr); fputc('\n', stderr); exit(1);} 4.利用IP地址获取域名
这里将介绍gethostbyaddr()和getnameinfo()两种函数,使用这些函数可以利用IP地址,获取域名。
①Linux系统
getthostbyaddr()函数是早期的网络编程API,成功时,返回一个指向hostent结构的指针;失败时,返回NULL。可能不是线程安全的,并且在某些系统上可能已被废弃,同样的不支持IPv6
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);//addr: 指向IP地址的指针,该地址以网络字节顺序给出。//len: IP地址的长度,对于IPv4地址通常是sizeof(struct in_addr),对于IPv6地址是sizeof(struct in6_addr)。//type: 地址类型,对于IPv4地址使用AF_INET,对于IPv6地址使用AF_INET6。
#include #include #include #include #include #include void error_handling(const char *message);int main(int argc, char *argv[]){ int i; struct hostent *host; struct sockaddr_in addr; if(argc != 2){ printf("Usage : %s \n", argv[0]); exit(1); } memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr = inet_addr(argv[1]); host = gethostbyaddr((char*)&addr.sin_addr, sizeof(addr.sin_addr), AF_INET);//将IP地址转换为域名 if(!host){ error_handling("gethost... error"); } printf("Official name : %s \n", host->h_name); for(i=0; host->h_aliases[i]; i++){ printf("Aliases %d : %s \n", i+1, host->h_aliases[i]); } printf("Address type : %s \n", (host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6"); for(i=0; host->h_addr_list[i]; i++){ printf("IP addr %d : %s \n", i+1, inet_ntoa(*(struct in_addr*)host->h_addr_list[i])); } return 0;}void error_handling(const char *msg){ fputs(msg, stderr); fputc('\n', stderr); exit(1);} 这里解析腾讯的IP地址
getnameinfo()函数用于将IP地址转换为主机名和端口号的可读字符串形式,这个函数支持IPv4和IPv6,这里只做语法介绍。
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);//sa: 指向sockaddr结构的指针,包含要解析的地址信息。//salen: sockaddr结构的大小。//host: 用于存储解析后的主机名的缓冲区的指针。//hostlen: host缓冲区的长度。//serv: 用于存储解析后的端口号的字符串的缓冲区的指针,可以为NULL,如果不需要服务名。//servlen: serv缓冲区的长度。//flags: 控制getnameinfo行为的标志位,可以是以下值的组合://0: 默认行为。//NI_NOFQDN: 仅返回主机名而不返回完全限定的域名(FQDN)。//NI_NUMERICHOST: 强制返回数值形式的主机地址,忽略gethostbyaddr的解析。//NI_NAMEREQD: 如果节点名未知,则要求返回错误。//NI_NUMERICSERV: 返回服务的数值形式而不是服务名称。//NI_DGRAM: 服务指定为数据报服务(UDP)。
①Windows系统
gethostbyaddr()和getnameinfo()两种函数在Windows系统中的语法,和Linux系统中完全相同,这里就不再赘述,直接给出示例代码:
gethostbyaddr()函数
#include #include #include #include #include void error_handling(const char *message); // 错误处理函数声明int main(int argc, char *argv[]){ WSADATA wsaData; int i; struct hostent *host; SOCKADDR_IN addr;// 用于存储IPv4地址信息的结构体 if(argc!=2){ printf("Usage : %s \n", argv[0]); exit(1); } if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0){ printf("WSAStartup() error!\n"); exit(1); } memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr = inet_addr(argv[1]); // 使用gethostbyaddr函数将IP地址转换为hostent结构 host=gethostbyaddr((char*)&addr.sin_addr, sizeof(addr.sin_addr), AF_INET); if(host==NULL){ error_handling("gethostbyaddr... error"); } // 打印官方主机名 printf("Official name: %s \n", host->h_name); for(i=0; host->h_aliases[i]; i++){// 遍历并打印所有别名 printf("Aliases %d: %s \n", i+1, host->h_aliases[i]); } // 打印地址类型 printf("Address type: %d \n", host->h_addrtype); for(i=0; host->h_addr_list[i]; i++){// 遍历并打印所有IP地址 printf("IP addr %d: %s \n", i+1, inet_ntoa(*(struct in_addr*)host->h_addr_list[i])); } // 清理Winsock WSACleanup(); return 0;}// 错误处理函数,打印错误信息并退出程序void error_handling(const char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1);} 这里解析腾讯的IP地址
上一篇:没有转换器怎么连接电脑主机
下一篇:企业域名和服务器怎么买?