# 网络,第 2 部分:使用 getaddrinfo
> 原文:<https://github.com/angrave/SystemProgramming/wiki/Networking%2C-Part-2%3A-Using-getaddrinfo>
## 如何使用`getaddrinfo`将主机名转换为 IP 地址?
函数`getaddrinfo`可以将人类可读域名(例如`www.illinois.edu`)转换为 IPv4 和 IPv6 地址。实际上它将返回 addrinfo 结构的链表:
```c
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
```
它非常易于使用。例如,假设您想在 [www.bbc.com](http://www.bbc.com) 中找到网络服务器的数字 IPv4 地址。我们分两个阶段完成。首先使用 getaddrinfo 构建可能的连接的链表。其次使用`getnameinfo`将二进制地址转换为可读形式。
```c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
struct addrinfo hints, *infoptr; // So no need to use memset global variables
int main() {
hints.ai_family = AF_INET; // AF_INET means IPv4 only addresses
int result = getaddrinfo("www.bbc.com", NULL, &hints, &infoptr);
if (result) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result));
exit(1);
}
struct addrinfo *p;
char host[256],service[256];
for(p = infoptr; p != NULL; p = p->ai_next) {
getnameinfo(p->ai_addr, p->ai_addrlen, host, sizeof(host), service, sizeof(service), NI_NUMERICHOST);
puts(host);
}
freeaddrinfo(infoptr);
return 0;
}
```
典型输出:
```
212.58.244.70
212.58.244.71
```
## [www.cs.illinois.edu](http://www.cs.illinois.edu) 如何转换为 IP 地址?
魔法!不用说,使用称为“DNS”(域名服务)的系统。如果计算机没有在本地保存答案,则它会将 UDP 数据包发送到本地 DNS 服务器。该服务器又可以查询其他上游 DNS 服务器。
## DNS 安全吗?
DNS 本身很快但不安全。 DNS 请求未加密,容易受到“中间人”攻击。例如,咖啡店互联网连接可以轻易地破坏您的 DNS 请求并发回特定域的不同 IP 地址
## 如何连接到 TCP 服务器(例如 Web 服务器?)
TODO 连接到远程计算机需要三个基本系统调用:
```
getaddrinfo -- Determine the remote addresses of a remote host
socket -- Create a socket
connect -- Connect to the remote host using the socket and address information
```
`getaddrinfo`调用成功,创建`addrinfo`结构的链接列表,并将给定指针设置为指向第一个。
套接字调用创建一个传出套接字并返回一个描述符(有时称为“文件描述符”),可以与`read`和`write`等一起使用。在这种意义上,它是`open`的网络模拟打开文件流 - 除了我们尚未将套接字连接到任何东西!
最后,connect 调用尝试连接到远程计算机。我们传递原始套接字描述符以及存储在 addrinfo 结构中的套接字地址信息。存在不同种类的套接字地址结构(例如,IPv4 与 IPv6),其可能需要更多存储器。因此,除了传递指针外,还传递了结构的大小:
```c
// Pull out the socket address info from the addrinfo struct:
connect(sockfd, p->ai_addr, p->ai_addrlen)
```
## 如何释放为 addrinfo 结构的链表分配的内存?
作为最顶层`addrinfo`结构上的清理代码调用`freeaddrinfo`的一部分:
```c
void freeaddrinfo(struct addrinfo *ai);
```
## 如果 getaddrinfo 失败,我可以用`strerror`打印出错误吗?
没有。`getaddrinfo`的错误处理有点不同:
* 返回值 _ 是 _ 的错误代码(即不使用`errno`)
* 使用`gai_strerror`获取等效的短英文错误文本:
```c
int result = getaddrinfo(...);
if(result) {
const char *mesg = gai_strerror(result);
...
}
```
## 我可以只请求 IPv4 或 IPv6 连接吗?仅 TCP?
是!使用传递给`getaddrinfo`的 addrinfo 结构来定义您想要的连接类型。
例如,要通过 IPv6 指定基于流的协议:
```c
struct addrinfo hints;
memset(hints, 0, sizeof(hints));
hints.ai_family = AF_INET6; // Only want IPv6 (use AF_INET for IPv4)
hints.ai_socktype = SOCK_STREAM; // Only want stream-based connection
```
## 那些使用`gethostbyname`的代码示例呢?
旧函数`gethostbyname`已弃用;这是将主机名转换为 IP 地址的旧方法。端口地址仍需要使用 htons 功能手动设置。使用较新的`getaddrinfo`编写支持 IPv4 和 IPv6 的代码要容易得多
## 这很容易!?
是的,不是。创建一个简单的 TCP 客户端很容易 - 但网络通信提供了许多不同的抽象级别,并且可以在每个抽象级别设置几个属性和选项(例如我们没有谈到可以操作选项的`setsockopt`插座)。有关详细信息,请参阅[指南](http://www.beej.us/guide/bgnet/output/html/multipage/getaddrinfoman.html)。
- UIUC CS241 系统编程中文讲义
- 0. 简介
- #Informal 词汇表
- #Piazza:何时以及如何寻求帮助
- 编程技巧,第 1 部分
- 系统编程短篇小说和歌曲
- 1.学习 C
- C 编程,第 1 部分:简介
- C 编程,第 2 部分:文本输入和输出
- C 编程,第 3 部分:常见问题
- C 编程,第 4 部分:字符串和结构
- C 编程,第 5 部分:调试
- C 编程,复习题
- 2.进程
- 进程,第 1 部分:简介
- 分叉,第 1 部分:简介
- 分叉,第 2 部分:Fork,Exec,等等
- 进程控制,第 1 部分:使用信号等待宏
- 进程复习题
- 3.内存和分配器
- 内存,第 1 部分:堆内存简介
- 内存,第 2 部分:实现内存分配器
- 内存,第 3 部分:粉碎堆栈示例
- 内存复习题
- 4.介绍 Pthreads
- Pthreads,第 1 部分:简介
- Pthreads,第 2 部分:实践中的用法
- Pthreads,第 3 部分:并行问题(奖金)
- Pthread 复习题
- 5.同步
- 同步,第 1 部分:互斥锁
- 同步,第 2 部分:计算信号量
- 同步,第 3 部分:使用互斥锁和信号量
- 同步,第 4 部分:临界区问题
- 同步,第 5 部分:条件变量
- 同步,第 6 部分:实现障碍
- 同步,第 7 部分:读者编写器问题
- 同步,第 8 部分:环形缓冲区示例
- 同步复习题
- 6.死锁
- 死锁,第 1 部分:资源分配图
- 死锁,第 2 部分:死锁条件
- 死锁,第 3 部分:餐饮哲学家
- 死锁复习题
- 7.进程间通信&amp;调度
- 虚拟内存,第 1 部分:虚拟内存简介
- 管道,第 1 部分:管道介绍
- 管道,第 2 部分:管道编程秘密
- 文件,第 1 部分:使用文件
- 调度,第 1 部分:调度过程
- 调度,第 2 部分:调度过程:算法
- IPC 复习题
- 8.网络
- POSIX,第 1 部分:错误处理
- 网络,第 1 部分:简介
- 网络,第 2 部分:使用 getaddrinfo
- 网络,第 3 部分:构建一个简单的 TCP 客户端
- 网络,第 4 部分:构建一个简单的 TCP 服务器
- 网络,第 5 部分:关闭端口,重用端口和其他技巧
- 网络,第 6 部分:创建 UDP 服务器
- 网络,第 7 部分:非阻塞 I O,select()和 epoll
- RPC,第 1 部分:远程过程调用简介
- 网络复习题
- 9.文件系统
- 文件系统,第 1 部分:简介
- 文件系统,第 2 部分:文件是 inode(其他一切只是数据...)
- 文件系统,第 3 部分:权限
- 文件系统,第 4 部分:使用目录
- 文件系统,第 5 部分:虚拟文件系统
- 文件系统,第 6 部分:内存映射文件和共享内存
- 文件系统,第 7 部分:可扩展且可靠的文件系统
- 文件系统,第 8 部分:从 Android 设备中删除预装的恶意软件
- 文件系统,第 9 部分:磁盘块示例
- 文件系统复习题
- 10.信号
- 过程控制,第 1 部分:使用信号等待宏
- 信号,第 2 部分:待处理的信号和信号掩码
- 信号,第 3 部分:提高信号
- 信号,第 4 部分:信号
- 信号复习题
- 考试练习题
- 考试主题
- C 编程:复习题
- 多线程编程:复习题
- 同步概念:复习题
- 记忆:复习题
- 管道:复习题
- 文件系统:复习题
- 网络:复习题
- 信号:复习题
- 系统编程笑话