原文:https://beej.us/guide/bgnet/html/#getaddrinfoprepare-to-launch
5. 调用/捕获系统函数
这部分我们使用系统的函数来访问网络他们在Unix的头文件里,以及任何支持套接字APP的操作系统(BSD、Windows、Linux、Mac等等)。当你调用他们中间的函数时,内核会自动帮助完成所有工作。
大多数就卡在这些系统函数里。在这儿,因为你可能发现没有可用的man!好,为了帮助顺利适应它,我按照你的程序调用系统函数的顺序来讲解他们。
那么,你需要一些牛奶和饼干,再给你提供一些样例代码。其它的就是你的勇气、高昂的勇气!你会发现原来竟然是如此的快乐!
(请注意,为了代码的简洁,没有包含错误检查。在调用getaddrinfo()函数成功时会返回一个有效的链表(linked list)。有些程序使用原始的地址(properly addressed ---也可译原型的地址)这也可以。)
5.1 getaddrinfo() --- 准备运行
这个是真正的默默无闻的函数,虽然它有没多参数,但是实际用起来很简单。
它对于你建立一个地址结构起到了简化的作用。
A tiny bit of history: it used to be that you would use a function called gethostbyname() to do DNS lookups. Then you’d load that information by hand into a struct sockaddr_in, and use that in your calls.
值得庆幸的是,这不再是必要的了。在这现代化的时代,你已经有了getaddrinfo()函数并为你封装好了不少东西,其中包括DNS和服务名称查找!
让我们一起来看看!
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *node, // e.g. "www.example.com" or IP
const char *service, // e.g. "http" or port number
const struct addrinfo *hints,
struct addrinfo **res);
在这个函数里有3个输入参数和1个作为返回值的指向链表的指针res。
入参node是要连接的主机名或者IP地址。
下一个入参service是端口号,比如“80”,或者其它特定的服务名称像“http”、“ftp”、“telnet”、“smtp”等等。
最后,入参hints指向一个你已经填写好了的addrinfo结构。
下面的示例是服务器在你的主机IP地址和端口号3490监听。
请注意,他们没有做任何实际的监听或者网络设置;
它只是在设置了结构hints并且做了调用。
int status;
struct addrinfo hints;
struct addrinfo *servinfo; // will point to the results
memset(&hints, 0, sizeof(hints)); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
// servinfo now points to a linked list of 1 or more struct addrinfos
// ... do everything until you don't need servinfo anymore ....
freeaddrinfo(servinfo); // free the linked-list
请注意:因为ai_family设置为 AF_UNSPEC 以达到与版本(IPv4 or IPv6)无关。
你也能设置专为IPv4或者IPv6工作以及其它的地址族。
此外你在这儿看到了AI_PASSIVE标志;他告诉getaddrinfo()复制本机地址给套接字结构。这很好,因为没有硬编码。
译者注:上边的情况通常使用在服务器端!
下边说的就是客户端的情况。
示例:连接到www.example.net的3049端口。
int status;
struct addrinfo hints;
struct addrinfo *servinfo; // will point to the results
memset(&hints, 0, sizeof(hints)); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
// get ready to connect
status = getaddrinfo("www.example.net", "3490", &hints, &servinfo);
// servinfo now points to a linked list of 1 or more struct addrinfos
// etc.
我一直在说servinfo是包含所有地址信息的链接列表。
让我们编写一个快速的演示程序来展示这些信息。
这个程序在命令行上打印出你指定主机的IP地址。
showip.c所需头文件列表:
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netdb.h>
#include<arpa/inet.h>
流程图
正如您所看到的,代码调用的getaddrinfo()来填写由res指向的链表,然后我们可以遍历列表以及打印在命令行上。
译者注:测试IPv6失败!请读者自行查找国内可用的IPv6站点。
现在我们已经在控制之下,我们将使用getaddrinfo()得到的结果,我们要传递给其他socket函数,
终于,建立我们的网络连接!