【问题标题】:getaddrinfo and INADDR_ANYgetaddrinfo 和 INADDR_ANY
【发布时间】:2018-03-19 11:52:54
【问题描述】:

花了几个小时搜索,仍然感到困惑。根据我的发现,INADDR_ANY 旨在指定套接字将接受与分配给服务器的任何地址的连接。但是,以下导致客户端只能从同一台机器连接到localhost:7777

addrinfo hints;
addrinfo* result;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(INADDR_ANY, "7777", &hints, &result);

SOCKET listenSocket = INVALID_SOCKET;
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);

我找到的唯一解决方案是将 INADDR_ANY 更改为机器的本地 IP:

getaddrinfo("192.168.0.105", "7777", &hints, &result);

我需要了解 INADDR_ANY 的工作原理,因为我觉得我只是在以某种方式滥用它。任何帮助将不胜感激。

【问题讨论】:

  • INADDR_ANY 具有您所期望的效果当它作为参数直接传递给bind()。你不这样做。您将此值传递给getaddrinfo()。这是一个完全不同的故事。现在,在这种情况下,您为什么不检查getaddrinfo()返回的内容,然后自己弄清楚。如果您想接受任何本地 IP 地址上的连接,则不需要getaddrinfo()。彻底摆脱这一切,直接致电bind()
  • 你怎么知道getaddrinfo()甚至工作?您不必费心检查返回值。根据the POSIX standard:“getaddrinfo() 的返回值为零表示成功完成;非零返回值表示失败。错误的可能值列在错误部分中。”
  • getaddrinfo 可以返回几个个地址;而且你还需要freeaddrinfo(result) 但你只绑定了第一个。
  • 你读过the documentation吗?
  • 为了简洁起见,我省略了错误检查,我的错。

标签: c windows sockets tcp getaddrinfo


【解决方案1】:

INADDR_ANY与你的问题无关。

getaddrinfo() 的第一个参数是 const char *,指定 IP 地址或主机名。但是INADDR_ANY 是一个整数。您的代码甚至可以编译的唯一原因是因为INADDR_ANY 被定义为整数常量 0,这是唯一允许分配给指针的整数常量。因此,您实际上是将 NULL 传递给第一个参数。在这种情况下这很好,因为无论如何这都是你需要的。

您没有考虑到getaddrinfo() 返回一个地址的链接列表,其中可能包含多个地址,尤其是如果您使用AF_UNSPEC。使用该系列告诉getaddrinfo() 它可以返回 IPv4 和 IPv6 的地址。但是您只使用列表中的第一个地址,它恰好对应于localhost(IPv4 中为 127.0.0.1,IPv6 中为 ::1)。

对于服务器,您应该为输出列表中的每个地址创建并绑定一个单独的侦听套接字。这将让您绑定到与您传递给getaddrinfo() 的条件相匹配的所有地址,例如:

addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;

addrinfo* result;

if (getaddrinfo(NULL, "7777", &hints, &result) == 0)
{
    for (addrinfo *addr = result; addr != NULL; addr = addr->ai_next)
    {
        SOCKET listenSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (listenSocket != INVALID_SOCKET)
        {
            bind(listenSocket, addr->ai_addr, (int)addr->ai_addrlen);
            listen(listenSocket, ...);
            // store listenSocket in a list for later use... 
        }
    }
    freeaddrinfo(result);
}

// use listening sockets as needed... 

或者,正如 Sam V 在 cmets 中提到的,您可以跳过 getaddrinfo(),在这种情况下它并不能真正帮助您。您可以直接创建和绑定 2 个套接字,一个用于 IPv4,一个用于 IPv6:

SOCKET listenSocket4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket4 != INVALID_SOCKET)
{
    sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7777);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(listenSocket4, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket4, ...);
}

SOCKET listenSocket6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket6 != INVALID_SOCKET)
{
    sockaddr_in6 addr = {};
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(7777);
    addr.sin6_addr = in6addr_any;

    bind(listenSocket6, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket6, ...);
}

// use listening sockets as needed... 

或者更好的是,创建并绑定一个支持同时 IPv4 和 IPv6 的 dual-stack 套接字:

SOCKET listenSocket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket != INVALID_SOCKET)
{
    BOOL off = FALSE;
    setsockopt(listenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off));

    sockaddr_in6 addr = {};
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(7777);
    addr.sin6_addr = in6addr_any;

    bind(listenSocket, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket, ...);
}

// use listening socket as needed... 

【讨论】:

  • 谢谢,最后两个选项正是我想要的。我设法找到的指南都很陈旧而且范围很窄,这是我第一次在没有框架帮助我的情况下编写网络代码,我对 API 应该如何工作一无所知。
猜你喜欢
  • 1970-01-01
  • 2016-09-19
  • 2011-08-22
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
  • 2018-02-14
相关资源
最近更新 更多