getaddrinfo用法

参考的文章:

https://www.cnblogs.com/LubinLew/p/POSIX-DataStructure.html#struct_addrinfo

https://baike.baidu.com/item/getaddrinfo/9021771?fr=aladdin

在做的一个项目,用到getaddrinfo函数,从linux 移植到windows上,做一小结。

getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个sockaddr结构的链表而不是一个地址清单。这些sockaddr结构随后可由套接口函数直接使用。如此一来,getaddrinfo函数把协议相关性安全隐藏在这个库函数内部。应用程序只要处理由getaddrinfo函数填写的套接口地址结构。该函数在 POSIX规范中定义了。

IPv6中引入了新的API getaddrinfo(),它是协议无关的,既可用于IPv4也可用于IPv6。

包含头文件

Linux下为#include<netdb.h>

windows下为#include <ws2tcpip.h>

函数原型

int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );

参数说明

hostname:一个主机名或者地址串(IPv4点分十进制串或者IPv6的16进制串)              //本项目使用的是地址串

例如:主机名("www.baidu.com")或者是数字化的地址字符串(IPv4的点分十进制串("192.168.1.100")或者IPv6的16进制串("2000::1:2345:6789:abcd")),

如果 ai_flags 中设置了AI_NUMERICHOST 标志,那么该参数只能是数字化的地址字符串,不能是域名,该标志的作用就是阻止进行域名解析。

service:服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等   //本项目使用的是端口号

例如:服务名可以是十进制的端口号("8080")字符串,也可以是已定义的服务名称,如"ftp"、"http"等,详细请查看/etc/services 文件,最后翻译成对应服务的端口号。如果此参数设置为NULL,那么返回的socket地址中的端口号不会被设置。

hints:可以是一个空指针,也可以是一个指向某个addrinfo结构体的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。举例来说:指定的服务既可支持TCP也可支持UDP,所以调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM使得返回的仅仅是适用于数据报套接口的信息。

具体介绍一下hints结构体的参数设置

hint.ai_flags = AI_NUMERICSERV;  

1)如果 ai_flags 设置了AI_NUMERICSERV 标志并且service未设置为NULL,那么该参数必须是一个指向10进制的端口号字符串,不能设定成服务名,该标志就是用来阻止服务名解析。

2)如果设置了 AI_PASSIVE 标志,并且 nodename 是 NULL, 那么返回的socket地址可以用于的bind()函数,返回的地址是通配符地址(wildcard address, IPv4时是INADDR_ANY,IPv6时是IN6ADDR_ANY_INIT),这样应用程序(典型是server)就可以使用这个通配符地址用来接收任何请求主机地址的连接;

3)如果 nodename 不是NULL,那么 AI_PASSIVE 标志被忽略;如果未设置AI_PASSIVE标志,返回的socket地址可以用于connect(), sendto(), 或者 sendmsg()函数。

4)如果 nodename 是NULL,那么网络地址会被设置为lookback接口地址(IPv4时是INADDR_LOOPBACK,IPv6时是IN6ADDR_LOOPBACK_INIT)。

hint.ai_family = AF_UNSPEC;                   /*  hint 的限定设置  */

当 ai_family 指定为AF_INT6(IPv6)时,如果没有找到IPv6地址,那么会返回IPv4-mapped IPv6 地址,

也就是说如果没有找到AAAA record(用来将域名解析到IPv6地址的DNS记录),那么就查询A record(IPv4),

将找到的IPv4地址映射到IPv6地址, IPv4-mapped IPv6 地址其实是IPv6内嵌IPv4的一种方式,

地址的形式为"0::FFFF:a.b.c.d",例如"::ffff:192.168.89.9"(混合格式)这个地址仍然是一个IPv6地址, 只是"0000:0000:0000:0000:0000:ffff:c0a8:5909"(16机制格式)的另外一种写法罢了。

当 ai_family 不是AF_INT6(IPv6)时,该标志被忽略。查询IPv4和IPv6地址

只有当主机配置了IPv4地址才进行查询IPv4地址;只有当主机配置了IPv6地址才进行查询IPv6地址.

ai_flags值的说明

如上表所示,ai_flags的值的范围为0~7,取决于程序如何设置3个标志位,比如设置ai_flags为“AI_PASSIVE|AI_CANONNAME”,ai_flags值就为3。三个参数的含义分别为:

(1)AI_PASSIVE当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。

当节点名位NULL,且此标志置位,则返回的地址将是通配地址。

如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。

(2)AI_CANNONAME当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。

(3)AI_NUMERICHOST当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。

result:本函数通过result指针参数返回一个指向addrinfo结构体链表指针

所以根据需要返回的addrinfo restult (使用目的),选择合适的参数组合。

返回值

如果 getaddrinfo() 函数执行成功,返回值为 0 , 其他情况返回值表示错误种别。使用函数gai_strerror() 可以获取可读性的错误信息,用法用strerror()相同,

错误种别如下:

指定的主机上没有请求的address family对应的网络地址.

DNS(name server)返回临时性错误. 可以稍后重试.

hints.ai_flags 包含了无效的标志; 或者 hints.ai_flags 包含了 AI_CANONNAME 标志但是 name 是 NULL.

DNS(name server)返回永久性错误

不支持的 address family(hints.ai_family).

内存耗尽.

指定的网络主机存在,但是其未定义任何网络地址.

nodename 或者 servname 未知;或者两者都设置为NULL;

或者设置了 AI_NUMERICSERV 标志但是 servname 不是一个数字化的端口名字符串。

请求的socket类型不支持请求的服务类型.例如服务类型是 "shell" (基于流的socket服务),

但是 hints.ai_protocol 是  IPPROTO_UDP 或者hints.ai_socktype 是 SOCK_DGRAM;

或者 servname 不是NULL 但是 hints.ai_socktype 是 SOCK_RAW (原始套接字不支持服务的概念).

不支持请求的socket类型. 例如, hints.ai_socktype 和 hints.ai_protocol 冲突 (例如分别是SOCK_DGRAM、IPPROTO_TCP).

系统调用错误,检查 errno.

使用说明

在getaddrinfo函数之前通常需要对以下6个参数进行以下设置:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol。

在6项参数中,对函数影响最大的是nodename,sername和 h i n t s . ai_flag,而ai_family只是有地址为v4地址或v6地址的区别。ai_protocol一般是为0不作改动。

实际常用设置

一般情况下,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。

 

在项目中,应该是第二种方式使用,hostname = IP地址,目的是获取一个符合要求的addrinfo 结构体,以便socket的connect。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值