Beej的网络编程入门教程 第六篇 必要的系统调用 getaddrinfo

5.必要的系统调用

       这一节我们开始接触那些允许你访问网络功能的系统调用。当你在UNIX或其它支持套接字API的系统上(BSDWindowsLinuxMac等等)调用这些功能时系统会自动为你做好所有的工作。

       因为不知道按什么顺序调用,许多人到这里非常困惑。你可能已经发现了我没有使用完整的例子。好的,为了解决这个问题我试着按在程序中调用的顺序来按排章节。

       (请注意,例子中的代码都不健壮,因为几乎没有错误检查。请你把这些代码只当框架使用。)

5.1.getaddrinfo()------准备开始

       这确是个有大堆选项的复杂函数,但用法简单,它用来建立一些后面要使用的结构体。

       以前:如果要用gethostbyname()DNS查询,你需要在使用前手动填充sockaddr_in结构

       现在已经不需要了,你有了函数getaddrinfo()它为你做许多事情,包括DNS和服务查询并且会填充你需要的结构体。

       看一下!

       #include <sys/types.h>

       #include <sys/socket.h>

       #include <netdb.h>

       int getaddrinfo(  const char *node, //eg. www.example.com or IP

                                   const char *service, //eg. “http” or port number

                                   const struct addrinfo * hints,

                                   struct addrinfo ** res);

       你给这个函数3个参数,它返回一个链表指针res,参数node是要连接的一个主机名称或一个IP地址,参数Service是一个端口号例如“80,或是一个公知的服务名称像“http”或“ftp”或“telnet”或“smtp”等等。最后,hints参数指向你填写好相关的addrinfo结构体。

       这里有一个简单的调用,假设你是服务器想要监听本机IP地址3490端口。注意这里没有监听操作;仅仅是准备后面要用到的结构体。

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(stdder, “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_familyAF_UNSPEC,就是说我不需要关心使用IPv4还是IPv6。你可以设置为AF_INETAF_INET6

       你也看到了AI_PASSIVE标记;这是告诉getaddrinfo()把本机的IP地址填充到套接字结构。这是很有用的,你不用手动做这些工作了(或者你可以在第一个参数处填写特殊地址,上面写的是NULL)。

       我们调用getaddrinfo()如果有错误发生,可以使用gai_strerror()打印出错误原因来。如果正常servinfo将指向一个链表结构addrinfo,每个结点有一个sockaddr结构,我们在后面可以使用。最后,当我们处理完所有的工作后,我们可以(也需要)用freeaddrinfo()释放它。

       这里有个例子:假设你是想通过www.example.net端口”3490”连接特定的服务器的客户机。重复,这并不是实际的进行了连接,但是为我们准备了后面要用到的结构。

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 read to connect

Status = getaddrinfo(www.example.net, “3490”, &hints, &servinfo);

//servinfo now point to a linked list of 1 or more struct addrinfos

// etc.

要强调的一点是servinfo是一个链表包含有许多地址信息,让我们写一个样例显示这些信息。这个短程序将打印出你在命令行中键入的主机的IP地址。

/*
** showip.c -- show IP addresses for a host given on the command line
*/
 
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
 
int main(int argc, char *argv[])
{
    struct addrinfo hints, *res, *p;
    int status;
    char ipstr[INET6_ADDRSTRLEN];
 
    if (argc != 2) {
        fprintf(stderr,"usage: showip hostname/n");
        return 1;
    }
 
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
    hints.ai_socktype = SOCK_STREAM;
 
    if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
        fprintf(stderr, "getaddrinfo: %s/n", gai_strerror(status));
        return 2;
    }
 
    printf("IP addresses for %s:/n/n", argv[1]);
 
    for(p = res;p != NULL; p = p->ai_next) {
        void *addr;
        char *ipver;
 
        // get the pointer to the address itself,
        // different fields in IPv4 and IPv6:
        if (p->ai_family == AF_INET) { // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
            addr = &(ipv4->sin_addr);
            ipver = "IPv4";
        } else { // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr = &(ipv6->sin6_addr);
            ipver = "IPv6";
        }
 
        // convert the IP to a string and print it:
        inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
        printf("  %s: %s/n", ipver, ipstr);
    }
 
    freeaddrinfo(res); // free the linked list
 
    return 0;
}

正像你看到的,代码调用getaddrinfo(),不论命令行传递什么内容,填充好res结构后,我们就可以遍历链表并打印出内容或进行别的操作。

运行一下例子!每个人都喜欢屏幕输出。

$ showip www.example.net
IP addresses for www.example.net:
 
  IPv4: 192.0.2.88
 
$ showip ipv6.example.com
IP addresses for ipv6.example.com:
 
  IPv4: 192.0.2.101
  IPv6: 2001:db8:8c00:22::171

在我们的控制下,我们得到了getaddrinfo()的返回值并传递给了另外的套接字,我们将一步步的建立我们的连接。继续阅读。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值