`

getaddrinfo()函数详解

 
阅读更多

1. 概述

IPv4中使用gethostbyname()函数完成主机名到地址解析,这个函数仅仅支持IPv4,且不允许调用者指定所需地址类型的任何信息,返回的结构只包含了用于存储IPv4地址的空间。IPv6中引入了getaddrinfo()的新API,它是协议无关的,既可用于IPv4也可用于IPv6。getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个addrinfo的结构(列表)指针而不是一个地址清单。这些addrinfo结构随后可由套接口函数直接使用。如此以来,getaddrinfo函数把协议相关性安全隐藏在这个库函数内部。应用程序只要处理由getaddrinfo函数填写的套接口地址结构。该函数在 POSIX规范中定义了。


2. 函数说明

包含头文件
#include<netdb.h>

函数原型
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );

参数说明
hostname:一个主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串)
service:服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等
hints:可以是一个空指针,也可以是一个指向某个addrinfo结构体的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。举例来说:如果指定的服务既支持TCP也支持UDP,那么调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM使得返回的仅仅是适用于数据报套接口的信息。
result:本函数通过result指针参数返回一个指向addrinfo结构体链表的指针。
返回值:0——成功,非0——出错


3. 参数设置

在getaddrinfo函数之前通常需要对以下6个参数进行以下设置:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol。
在6项参数中,对函数影响最大的是nodename,sername和hints.ai_flag,而ai_family只是有地址为v4地址或v6地址的区别。ai_protocol一般是为0不作改动。

getaddrinfo在实际使用中的几种常用参数设置
一般情况下,client/server编程中,server端调用bind(如果面向连接的还需要listen),client则不用掉bind函数,解析地址后直接connect(面向连接)或直接发送数据(无连接)。因此,比较常见的情况有
(1)    通常服务器端在调用getaddrinfo之前,ai_flags设置AI_PASSIVE,用于bind;主机名nodename通常会设置为NULL,返回通配地址[::]。
(2)    客户端调用getaddrinfo时,ai_flags一般不设置AI_PASSIVE,但是主机名nodename和服务名servname(更愿意称之为端口)则应该不为空。
(3)    当然,即使不设置AI_PASSIVE,取出的地址也并非不可以被bind,很多程序中ai_flags直接设置为0,即3个标志位都不设置,这种情况下只要hostname和servname设置的没有问题就可以正确bind。

上述情况只是简单的client/server中的使用,但实际在使用getaddrinfo和参考国外开源代码的时候,曾遇到一些将servname(即端口)设为NULL的情况(当然,此时nodename必不为NULL,否则调用getaddrinfo会报错)。
以下分情况进行了测试:
(1)    如果nodename是字符串型的IPv6地址,bind的时候会分配临时端口;
(2)    如果nodename是本机名,servname为NULL,则根据操作系统的不同略有不同,本文仅在WinXP和Win2003上作了测试。
        a)    WinXP系统(SP2)返回loopback地址[::1]
        b)    Win2003则将本机的所有IPv6地址列表加以返回。因为通常一台IPv6主机都有可能不止一个IPv6地址,比如fe80::1(本机 loopback地址)、fe80::***的Link-Local地址、3ffe:***的全局地址等等。这种情况下调用getaddrinfo会将这些地址全部返回,调用者应该注意如何使用这些地址。另外要注意的是,对于fe80::的地址在绑定的时候必须标明接口地址,即使用 fe80::20d:60ff:fe78:51c2%4或fe80::1%1这样的地址格式,通过getaddrinfo直接取出fe80地址好像无法直接bind。
        
        
4. 使用细节

如果本函数返回成功,那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo结构链表。可以导致返回多个addrinfo结构的情形有以下2个:
    1.    如果与hostname参数关联的地址有多个,那么适用于所请求地址簇的每个地址都返回一个对应的结构。
    2.    如果service参数指定的服务支持多个套接口类型,那么每个套接口类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype成员。

我们必须先分配一个hints结构,把它清零后填写需要的字段,再调用getaddrinfo,然后遍历一个链表逐个尝试每个返回地址。
        
getaddrinfo解决了把主机名和服务名转换成套接口地址结构的问题。

其中,如果getaddrinfo出错,那么返回一个非0的错误值。
#include<netdb.h>
const char *gai_strerror( int error );
该函数以getaddrinfo返回的非0错误值的名字和含义为他的唯一参数,返回一个指向对应的出错信息串的指针。

由getaddrinfo返回的所有存储空间都是动态获取的,这些存储空间必须通过调用freeaddrinfo返回给系统。
#include< netdb.h >
void freeaddrinfo( struct addrinfo *ai );
ai参数应指向由getaddrinfo返回的第一个addrinfo结构。这个连表中的所有结构以及它们指向的任何动态存储空间都被释放掉。


5. 例子

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s hostname\n",
argv[1]);
exit(1);   
}

struct addrinfo *answer, hint, *curr;
char ipstr[16];   
bzero(&hint, sizeof(hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;

int ret = getaddrinfo(argv[1], NULL, &hint, &answer);
if (ret != 0) {
fprintf(stderr,"getaddrinfo: &s\n",
gai_strerror(ret));
exit(1);
}

for (curr = answer; curr != NULL; curr = curr->ai_next) {
inet_ntop(AF_INET,
&(((struct sockaddr_in *)(curr->ai_addr))->sin_addr),
ipstr, 16);
printf("%s\n", ipstr);
}

freeaddrinfo(answer);
exit(0);
}
分享到:
评论

相关推荐

    python中getaddrinfo()基本用法实例分析

    ### Python中的`getaddrinfo()`函数详解 在Python网络编程中,经常需要用到主机名与IP地址之间的转换。其中,`socket.getaddrinfo()`是一个非常重要的函数,它可以帮助开发者完成这样的转换工作。本篇文章将深入...

    php gethostbyname获取域名ip地址函数详解

    如果需要纯IPv6地址,可以使用`getaddrinfo`函数。 - 错误处理是必要的,因为`gethostbyname`在无法解析时不会抛出异常,而是返回原始的主机名。你应该检查返回值是否为预期的IP地址,如果不是,可能需要进行异常...

    libuv API说明及用例

    uv_getaddrinfo_t* req = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t)); uv_getaddrinfo(loop, req, on_resolve, "example.com", NULL, NULL); uv_run(loop, UV_RUN_DEFAULT); uv_loop_close(loop); ...

    Linux常用C函数速查(中文版)

    - `gethostbyname`解析主机名,`getaddrinfo`获取地址信息。 8. **时间处理**: - `time`获取当前时间,`localtime`和`gmtime`将时间戳转换为本地或UTC时间,`strftime`格式化时间输出。 9. **数学运算**: - `...

    Python实现的简单dns查询功能示例

    #### 五、`socket.getaddrinfo()` 函数详解 - **函数签名**:`socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)` - **参数解释**: - `host`:要查询的主机名或 IP 地址。 - `port`:端口号...

    Windows Sockets网络程序设计指南.rar

    3. **Winsock函数详解**:书中会详细介绍一系列用于创建、配置、连接、监听、发送和接收数据的Winsock函数,例如`socket()`、`bind()`、`listen()`、`accept()`、`connect()`、`send()`、`recv()`等,以及错误处理...

    基于C_S结构的Socket程序设计

    - **地址解析**:使用gethostbyname()或getaddrinfo()函数获取服务器的IP地址。 - **连接/监听**:客户端调用connect(),服务器调用bind()和listen()。 - **数据传输**:连接建立后,使用send()和recv()函数发送...

    UNIX网络编程 卷1 (第三版) 源代码

    7. **网络函数详解**:从`socket()`、`bind()`、`listen()`到`accept()`、`connect()`、`send()`和`recv()`,源代码会逐个解析这些函数的使用,帮助理解它们在不同场景下的作用。 8. **地址解析与名字服务**:`...

    Winsock编程

    - **地址与端口**:使用`gethostbyname()`或`getaddrinfo()`函数获取服务器的IP地址信息,结合端口号准备连接或监听。 - **连接服务器**:对于客户端,调用`connect()`函数与服务器建立连接;对于服务器,调用`...

    rt-thread 通信

    2. **地址解析**:调用`lwip_getaddrinfo()`函数进行地址解析,将域名转换为具体的IP地址。 - 解析后的结果存储在一个`addrinfo`类型的结构体中,该结构体包含了目标地址的信息。 - 结构体中的关键成员会被复制...

    VC 局域网聊天(自动获取服务器IP地址)

    查找服务器IP地址可以通过DNS解析或者广播方式获取,这里我们假设已知服务器的端口号,可以使用`gethostbyname`或`getaddrinfo`函数来获取服务器的IP地址。一旦获取到IP地址,客户端就可以调用`connect`函数尝试连接...

    kanzi-master.zip_kanzi_socket

    使用`getaddrinfo()`函数获取服务器的IP和端口信息,然后调用`bind()`函数将Socket与特定的IP地址和端口关联起来,为接收连接或发送数据做好准备。 3. **监听与连接**: 对于服务器端,`listen()`函数设置Socket...

    Winsockt多线程下载

    3. **解析URL**: 将从m3u文件中读取的URL转换成IP地址,可以使用`gethostbyname`或`getaddrinfo`函数。 4. **连接服务器**: 使用`connect`函数与服务器建立连接,提供服务器的IP地址和端口号。 5. **发送HTTP请求*...

    Socket通信通信

    - **查找服务器**:通过gethostbyname()或getaddrinfo()获取服务器的IP地址和端口信息。 - **连接服务器**:使用connect()函数尝试连接到服务器。 - **数据收发**:客户端同样使用recv()和send()函数进行数据交换...

    TFTP客户端(C++)

    2. **地址解析**:使用`gethostbyname`或`getaddrinfo`获取服务器的IP地址,根据服务器的主机名。 3. **绑定本地端口**:使用`bind`函数将socket与本地端口关联,以便接收服务器的响应。 4. **连接服务器**:虽然...

    C语言socket编程

    在socket编程中,可能需要使用gethostbyname()或getaddrinfo()等函数查询域名对应的IP地址。 【客户-服务器背景知识】 客户-服务器模型是网络通信的基本模式之一,其中客户端向服务器发起请求,服务器响应客户端的...

    Ping程序设计(c语言课程设计).pdf

    5. **Host-to-IP转换**: 使用`gethostbyname()`或`getaddrinfo()`函数,根据主机名获取对应的IP地址,这是ping命令中必不可少的部分。 6. **内存管理**: 程序可能需要从堆中动态分配内存,例如分配空间存储数据报,...

    hyper-system-resolver:与hyper一起使用的高级系统解析器

    该库的核心功能是通过`getaddrinfo`函数来解析名称,这是一个标准的Unix API,用于获取主机名的地址信息。`hyper-system-resolver`利用`getaddrinfo`的强大功能,不仅能够处理IPv4和IPv6的地址,还能处理SRV记录,这...

    毕业设计 java ipv6/ipv4 socket 网络编程

    - **兼容性的API调用**:使用能够同时支持IPv4和IPv6的函数,如`getaddrinfo()`。 #### 八、结论 本文详细介绍了基于Java的IPv4/IPv6 Socket网络编程的关键步骤和技术要点。通过理解Socket的工作原理及其在TCP和UDP...

Global site tag (gtag.js) - Google Analytics