C 来自“网络编程”示例的代码是如何工作的?

C 来自“网络编程”示例的代码是如何工作的?,c,sockets,network-programming,C,Sockets,Network Programming,我正在读比吉的书 在他的一个介绍示例中,他谈到了获取主机名的IP地址,例如google.com或yahoo.com。 这是代码 /* ** 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>

我正在读比吉的书

在他的一个介绍示例中,他谈到了获取主机名的IP地址,例如google.com或yahoo.com。 这是代码

/*
** 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;
}

有人介意一步一步地通过psuedo了解发生了什么或这些事情是什么吗?它是在一个链表中迭代吗?。。我对struct addrinfo有一个大致的概念,但是struct*res和struct*p或void*addr和*char ipversion到底是什么。是的,res指向一个addrinfo结构的链接列表,这些结构表示主机的不同IP地址。天气很好。我不知道您在哪个平台上运行,但在其他平台上应该没有太大区别。

是的,res指向一个addrinfo结构的链接列表,这些结构表示主机的不同IP地址。天气很好。我不知道你在哪个平台上运行,但在其他平台上应该没有太大的不同。

嗯,没有那么复杂。返回手册页中addrinfo structs struct addrinfo**res的链接列表,其中每个结构包含有关手册页中给定接口const char*节点可用的一个地址的信息

现在,正在检查每个结构,并打印出有关该结构的信息。要打印IPv4或IPv6,应相应设置变量ipver。在打印出信息之前,地址必须从二进制格式转换为字符串。这是由*n*编号到*p*点数完成的


生成的inet_ntop ipstr和ipver字符串现在打印到控制台。然而,打印ipver并不是必需的,因为您可以从ipstr中识别地址类型:我们都知道,IPv4地址被写入192.168.1.10,而IPv6地址使用冒号分隔地址元素:2001:0db8:85a3:0000:0000:8a2e:0370:7334。

好吧,这并不复杂。返回手册页中addrinfo structs struct addrinfo**res的链接列表,其中每个结构包含有关手册页中给定接口const char*节点可用的一个地址的信息

现在,正在检查每个结构,并打印出有关该结构的信息。要打印IPv4或IPv6,应相应设置变量ipver。在打印出信息之前,地址必须从二进制格式转换为字符串。这是由*n*编号到*p*点数完成的


生成的inet_ntop ipstr和ipver字符串现在打印到控制台。但是,打印ipver并不是必需的,因为您可以从ipstr中识别地址类型:我们都知道,IPv4地址写为192.168.1.10,而IPv6地址使用冒号分隔地址元素:2001:0db8:85a3:0000:0000:8a2e:0370:7334。

首先,您知道a是什么吗?若你们明白了这一点,你们就会意识到for循环在进行什么。p是指向结构的指针,该结构还引用列表中下一个结构的链接。因此,您正在遍历这些结构的列表,它们是addrinfo结构。四,

现在,关于网络数据包,你需要知道的是它们是由一个报头组成的。特别是。这是硬件对硬件协议。它可以让你在一个物理的、有边界的网络上四处走动,但对跨越物理网络边界的路由一无所知

接下来是另一个传输层协议,它位于两个级别之间。TCP与UDP与X是关于如何管理数据包的,例如TCP要求按顺序重新组装数据包,而UDP是广播类型的协议

最后,您还有internet协议套件IPv4、IPv6。这些是更高层次的协议,控制更广泛意义上的路由,因此它们对整个互联网都很了解,但对到达互联网所需的步骤了解较少

关于这一点的一个很好的解释是这一点的方便的图表。完整的图片,是路由器如何知道如何移动东西

tcp/udp适合这种情况,因为它是相关协议IPv4中的一部分

因此,以太网帧包含其他协议,最著名的是IPv4协议,其中包含路由器在多个物理网络上通过internet传输信息所需的信息。互联网协议指定了你想去的地方,从哪里开始。因此,典型的IPv4主体在整个传输过程中保持不变,但每次它通过物理网络时,它都会被包装在不同的以太网数据包中

现在,在ethernet报头中有一个字段,用于查找ethernet主体包含的内容。这一行:

 if (p->ai_family == AF_INET) {
是的。AF_INET是一个常量,它与tcp用于将数据包主体标识为IPv4的值相匹配。因此,如果您正在查看IPv4报头,那么这个循环将继续读取该信息

else子句在技术上是错误的,因为不使用IPv4不会自动产生错误 它是IPv6。您可以将其更改为测试IPv6,如下所示:

 else if (p->ai_family == AF_INET6) { 
这是你可能想做的,以防你捡到别的东西

现在值得解释一下这一点魔力:

struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
这基本上采用网络或原始数据的形式,以字节序列的形式显示,并将其强制转换为结构中的字段。因为您知道字段将有多大,所以这是一种非常快速和简单的方法来提取您需要的内容

最后需要解释的是:

inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
实现这一点还有其他方法,特别是ntohs

基本上,网络数据是以big-endian编码传输的,为了读取它,您可能需要将数据转换为系统的编码。它可以是大端,也可以是小端,这在很大程度上取决于您的系统。读一读维基百科上的文章


小结:你在这里看到的是计算机科学结构、网络如何工作和C代码的结合。

首先,你知道a是什么吗?若你们明白了这一点,你们就会意识到for循环在进行什么。p是指向结构的指针,该结构还引用列表中下一个结构的链接。因此,您正在遍历这些结构的列表,它们是addrinfo结构。四,

现在,关于网络数据包,你需要知道的是它们是由一个报头组成的。特别是。这是硬件对硬件协议。它可以让你在一个物理的、有边界的网络上四处走动,但对跨越物理网络边界的路由一无所知

接下来是另一个传输层协议,它位于两个级别之间。TCP与UDP与X是关于如何管理数据包的,例如TCP要求按顺序重新组装数据包,而UDP是广播类型的协议

最后,您还有internet协议套件IPv4、IPv6。这些是更高层次的协议,控制更广泛意义上的路由,因此它们对整个互联网都很了解,但对到达互联网所需的步骤了解较少

关于这一点的一个很好的解释是这一点的方便的图表。完整的图片,是路由器如何知道如何移动东西

tcp/udp适合这种情况,因为它是相关协议IPv4中的一部分

因此,以太网帧包含其他协议,最著名的是IPv4协议,其中包含路由器在多个物理网络上通过internet传输信息所需的信息。互联网协议指定了你想去的地方,从哪里开始。因此,典型的IPv4主体在整个传输过程中保持不变,但每次它通过物理网络时,它都会被包装在不同的以太网数据包中

现在,在ethernet报头中有一个字段,用于查找ethernet主体包含的内容。这一行:

 if (p->ai_family == AF_INET) {
是的。AF_INET是一个常量,它与tcp用于将数据包主体标识为IPv4的值相匹配。因此,如果您正在查看IPv4报头,那么这个循环将继续读取该信息

else子句在技术上是错误的,因为不是IPv4并不自动成为IPv6。您可以将其更改为测试IPv6,如下所示:

 else if (p->ai_family == AF_INET6) { 
这是你可能想做的,以防你捡到别的东西

现在值得解释一下这一点魔力:

struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
这基本上采用网络或原始数据的形式,以字节序列的形式显示,并将其强制转换为结构中的字段。因为您知道字段将有多大,所以这是一种非常快速和简单的方法来提取您需要的内容

最后需要解释的是:

inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
实现这一点还有其他方法,特别是ntohs

基本上,网络数据是以big-endian编码传输的,为了读取它,您可能需要将数据转换为系统的编码。它可以是大端,也可以是小端,这在很大程度上取决于您的系统。读一读维基百科上的文章


小结:这里介绍的是计算机科学结构、网络工作原理和C代码的组合。

Unix平台。我猜*p只是链接列表中的迭代器或其他东西?是的,*p被设置在循环的顶部,指向列表res中的第一个元素,然后对于每个迭代,它被重置为p->ai_next,这是指向列表中下一个元素的指针。当p->ai_next为空时,您就在列表的末尾。好的,这比.Unix平台更有意义。我猜*p只是链接列表中的迭代器或其他东西?是的,*p被设置在循环的顶部,指向列表res中的第一个元素,然后对于每个迭代,它被重置为p->ai_next,这是指向列表中下一个元素的指针。当p->ai_next为空时,你就在列表的末尾。好吧,那就更有意义了。我知道这很简单,但这确实是我第一次与网络公关面对面地较量
编程;所以对于像我这样的傻瓜来说,这可能有点让人困惑,哈哈。我知道这很简单,但这确实是我第一次面对面地与网络编程作斗争;所以,对于像我这样的小家伙来说,这可能有点让人困惑。该死,很好的解释!谢谢这正是我要找的!如果每本书都能描述这一点的话,我将所有这些都作为一份旧工作的一部分来学习——我必须基本上重新设计tcpdump的一小部分。。。如果你有时间研究他们的代码库,它会教你所有你想知道的关于每个网络协议的知识…该死,很好的解释!谢谢这正是我要找的!如果每本书都能描述这一点的话,我将所有这些都作为一份旧工作的一部分来学习——我必须基本上重新设计tcpdump的一小部分。。。如果你有时间研究他们的代码库,它会教你所有你想知道的关于每一个网络协议的知识……关于}其他{//IPv6:在大多数实际情况下,这应该是可以的,但我不喜欢它作为教学示例;它给出了错误的想法。套接字支持IP4和IPv6以外的协议,以及除AF_INET和AF_INET6 IPv6以外的ai_系列的值。事实上,我确信AF_INET6是列表中最新添加的。至少,我可能已经做到了更广泛的评论。关于}其他{//IPv6:在大多数实际情况下,这应该是可以的,但我不喜欢它作为教学示例;它给出了错误的想法。套接字支持IP4和IPv6以外的协议,以及除AF_INET和AF_INET6 IPv6以外的ai_系列的值。事实上,我确信AF_INET6是列表中最新添加的。至少,我可能已经做到了更广泛的评论。