浅谈getaddrinfo

最近在看CSAPP的网络编程小节,盯着getaddrinfo这个api看了两天,总算是琢磨出了为何要引入这样复杂的一个api。

其依赖头文件如下,涉及的3个接口如下

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

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

void freeaddrinfo(struct addrinfo *result);

const char *gai_strerror(int errcode);

就我个人理解,引入getaddrinfo有以下三点好处:

1. 写网络相关代码时不需要将ipv4或者ipv6相关的预定义常数指明,通过查询填入。

2. 不用操心字节序问题。

 

首先说第一点,如果写代码时使用 AF_INET这样指明ipv4协议的预定义常数的话,相应代码只能用于ipv4。同理,如果使用了AF_INET6,那么代码只能用于ipv6.

为了使代码既能用于ipv4又能用于ipv6的话,当需要指明下图中结构体socketaddr_in里的协议族时sin_family,可以避免硬编码为AF_INET或AF_INET6,而是采用getaddrinfo去查询,然后填入,查询结果是ipv4就填AF_INET,查询结果是ipv6就填AF_INET6。因为没有深入底层源码,查询具体如何实施的不太清楚,初步推测可能是通过访问当前网段的DNS server来获取相应的信息。

/*IP socket address structure*/
struct sockaddr_in{
    uint16_t            sin_family;
    uint16_6            sin_port;
    struct in_addr      sin_addr;
    unsigned char       sin_zero[8];
};

 

 

再说第二点,在上图中,sin_port 和 sin_addr 都是需要按照大端法在网络中传输的。

简单说下大小端法的区分,设整数 v 占 4个字节 为0x12345678,  v的地址&v指 栈或堆或.data段中(取决于我们声明的方式)的最低地址,如下图

virtual memory:

                        0x40000F:

                        0x40000E:

                        0x40000D:

                        0x40000C:

如果变量v放在这4个字节里,则其地址 &v 为0x40000C,如果0x40000C里存的是最低有效位0x78则称其为小端法,这也是大多数机器的做法,如果0x40000C里存的是最高有效位0x12则是大端法。

 

 

假设port 为 80,80的16比特表示为  00000000  01010000,其16进制表示为 0x00 0x50

在存储时,声明一个结构体 struct sockaddr_in addr_example; 我们想将addr_example的端口号设为80。

如果我们的机器是大端法,那直接通过以下语句即可,

addr_example.sin_port = 80

 

但如果我们的机器是小端法,则需要通过以下4句代码来将 结构体里的2字节端口号设为指定的80;

char *temp = (char *)(addr_example.sin_port)
*temp = 0x00;
temp++;
*temp = 0x50;

这是非常麻烦的,通过查询getaddrinfo并赋值的方式,不管我们的机器是大端法还是小端法,它都将相应的sin_port设置为了大端法表示下的80,即通过getaddrinfo查询并复制后,如下检测为真。

* ((char *)(addr_example.sin_port)) == 0x00 &&

* ((char *)(add_example.sin_port) + 1) == 0x50;

 

最后就是为啥 声明一个 struct sockaddr *result 而传了&result,

如果api 里声明成 struct sockaddr *result的话,我们想要修改这个结构体就需要先声明并且定义一个 struct sockaddr result,然后把其地址传进去。

但是如果我们想要在接口内修改一个 struct sockaddr * 类型,那我们就必须传一个 struct sockaddr ** 类型,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值