《网络编程》中的这段代码是如何实现的?例子有效吗?

发布于 2024-10-18 04:52:43 字数 2700 浏览 2 评论 0原文

我正在阅读 Beej 的“网络编程指南”。

在他的一个介绍性示例中,他谈到了获取主机名的 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;
}

让我困惑的部分是 for 循环。

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);
}

有人介意逐步了解正在发生的事情或这些事情是什么吗?它是通过链表迭代吗?..我大致了解了 struct addrinfo 是什么,但 struct *res 和 struct *p 到底是什么 或 void *addr*char ipversion

I am reading Beej's "Guide to network programming".

In one of his intro examples he talks about getting the IP address for a hostname (like google.com or yahoo.com for instance).
Here is the code.

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

The part that confuses me is the for loop.

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);
}

Would anyone mind going through psuedo-step-by-step at whats going on or what these things are? Is it iterating through a linked list?.. I have a general idea of what the struct addrinfo are but what the heck is struct *res and struct *p or void *addr and *char ipversion.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

Hello爱情风 2024-10-25 04:52:43

首先,你知道什么是链接列表吗?如果你理解了这一点,你就会明白 for 循环的作用。 p 是一个指向结构的指针,该结构还引用(链接)列表中的下一个结构。因此,您将循环遍历这些结构的列表,这些结构是 addrinfo 结构。 4

现在,关于网络数据包,您需要了解的是它们由标头组成。具体来说是以太网框架。这是硬件到硬件的协议。它可以让您在物理、有界的网络上进行操作,但对跨物理网络边界的路由一无所知。

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

最后,您拥有互联网协议套件(IPv4、IPv6)。这些是更高级别的协议,控制更广泛的路由,因此它们了解整个互联网,但不太了解到达那里所需的步骤。

对此的一个很好的解释是这个页面上的方便图表。为了完成这幅图,BGP 是路由器知道如何移动内容的方式。

tcp/udp 通过成为相关协议(例如 IPv4)的一部分(封装在其中)来适应此图,

因此以太网帧包含其他协议,尤其是 IPv4,其中包含路由器需要通过互联网(通过多个物理网络)。 互联网协议指定您想要去哪里、从哪里出发。因此,典型的 IPv4 主体在整个传输过程中保持不变,但每次穿越物理网络时,它都会被包裹在不同的以太网数据包中。

现在,在以太网标头中,有一个字段用于查找“以太网主体”包含的内容。这一行:

 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()

基本上,网络数据以大端编码传输,为了读取它,您(可能)需要将数据转换为系统的编码。它可能是大端,也可能是小端,这在很大程度上取决于您的系统。阅读有关字节序的维基百科文章。

摘要:您在这里看到的是计算机科学结构、网络工作原理和 C 代码的组合。

First thing's first, do you know what a linked list is? If you understand that, you'll recognise what that for loop is going. p is a pointer to a structure that also references (links) the next structure in the list. So you're looping through a list of those structures, which are addrinfo structs. 4

Now, the thing you need to know about network packets is that they're made up of a header. Specifically the Ethernet frame. This is the hardware to hardware protocol. It let's you get things around on a physical, bounded network but knows nothing about routing across physical network boundaries.

Next up comes tcp or possibly another transport layer protocol, which sits somewhere inbetween the two levels. TCP versus UDP versus X is about how you manage the packets - for example TCP requires packets be reassembled in order, whereas UDP is a "broadcast"-type protocol.

Finally, you have the internet protocol suite (IPv4, IPv6). These are higher level protocols that control the broader sense of routing, so they know about the internet at large, but less about the steps needed to get there.

A great explanation of this is the handy diagram on this page. To complete the picture, BGP is how routers know how to move stuff around.

tcp/udp fit into this picture by being a part of (enscapulated in) the protocol in question (IPv4 for example)

So ethernet frames contain other protocols most notably IPv4, which contain the information routers need to get it out across the internet (across multiple physical networks). The internet protocol specifies where you want to go, from where you are. So a typical's IPv4 body remains unchanged across its whole transit, but every time it traverses physical networks it gets wrapped up in a different ethernet packet.

Now, in the ethernet header there is a field for finding out what the "ethernet body" contains. This line:

 if (p->ai_family == AF_INET) {

Does. AF_INET is a constant that matches the value tcp uses to identify the packet body as IPv4. So, if you're looking at an IPv4 header, this loop then goes on to read that information.

The else clause is technically wrong, because not being IPv4 doesn't automatically make it IPv6. You could change it to test for IPv6 like this:

 else if (p->ai_family == AF_INET6) { 

Which you might want to do, just in case you pick up something else.

Now it's worth explaining this bit of magic:

struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;

This basically takes the network, or raw, form of the data which appears as a sequence of bytes, and casts it (coverts it) into fields in a struct. Because you know how big the fields are going to be, this is a very quick and easy way to extract out what you need.

The last thing that needs explanation is this:

inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);

There are other ways of achieving this, specifically ntohs().

Basically network data is transmitted in big endian encoding, and in order to read it, you need (potentially) to convert the data to the encoding of your system. It could be big endian, or it could be little, it depends on your system for the most part. Have a read of the wikipedia article on endianness.

Summary: what you're looking at here is a combination of computer science structures, how networks work and C code.

调妓 2024-10-25 04:52:43

嗯,事情没那么复杂。 getaddrinfo 返回 addrinfo structs(手册页中的 struct addrinfo **res),其中每个结构体都包含有关给定接口可用的一个地址的信息(手册页中的 const char *node )。

现在,正在检查每个结构,并且正在打印有关该结构的信息。要打印 IPv4IPv6,请相应地设置变量 ipver。在打印信息之前,必须将地址从二进制形式转换为字符串。这是由 inet_ntop 完成的 (*n< /strong>*编号*p*指针)。

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

Well, it's not that complicated. getaddrinfo returns a linked list of addrinfo structs (struct addrinfo **res in the manpage) where each of these structs contains information about one address available to the given interface (const char *node in the manpage).

Now, every struct is being inspected and information about the struct is being printed out. To print out either IPv4 or IPv6, the variable ipver is set accordingly. Before printing out the information, the address has to be converted from a binary form to a string. This is done by inet_ntop (*n*umber to *p*ointer).

The resulting string of inet_ntop (ipstr) and ipver are now printed out to console. Printing ipver, however, is not neccessary since you would recognize the address type from the ipstr: an IPv4 address (as we all know) gets written 192.168.1.10 whereas IPv6 addresses use colons to separate the address elements: 2001:0db8:85a3:0000:0000:8a2e:0370:7334.

慕巷 2024-10-25 04:52:43

是的,res 指向表示主机不同 IP 地址的 addrinfo 结构的链接列表。 有关 getaddrinfo 函数的 MSDN 文档还不错。我不知道你在什么平台上运行,但在其他平台上应该没有太大不同。

Yes, res points to a linked list of addrinfo structures that represent the different IP addresses of a host. The MSDN documentation on the getaddrinfo function is pretty good. I don't know what platform you're running on, but it shouldn't be much different on other platforms.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文