C getaddrinfo和INADDR_ANY

C getaddrinfo和INADDR_ANY,c,windows,sockets,tcp,getaddrinfo,C,Windows,Sockets,Tcp,Getaddrinfo,花了几个小时寻找,仍然困惑不解。据我所知,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 =

花了几个小时寻找,仍然困惑不解。据我所知,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);

我需要了解InAdr_ANY是如何工作的,因为我觉得我只是不知何故误用了它。任何帮助都将不胜感激。

INADDR\u任何
与您的问题无关

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

您没有考虑的是
getaddrinfo()
返回一个地址链接列表,其中可能包含多个地址,尤其是在使用
AF\u unsec
时。使用该族告诉
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在评论中所提到的,您可以跳过
getaddrinfo()
,它在这种情况下并没有真正帮助您。您可以直接创建和绑定两个套接字,一个用于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的套接字:

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... 

INADDR\u ANY
在作为参数直接传递给
bind()
时,具有您所期望的效果。你没有这么做。您正在将此值传递给
getaddrinfo()
。这是一个完全不同的故事。现在,在本例中,为什么不检查
getaddrinfo()
返回的内容,然后自己解决这个问题呢。如果要接受任何本地IP地址上的连接,则不需要
getaddrinfo()
。彻底摆脱这一切,直接调用
bind()
。你怎么知道
getaddrinfo()
是否有效?您不必费心检查返回值。Per:“
getaddrinfo()
的零返回值表示成功完成;非零返回值表示失败。错误部分列出了失败的可能值。”
getaddrinfo
可以返回多个地址;你还需要
freeaddrinfo(result)
但是你只绑定了第一个。你读了吗?为了简洁起见,我省略了错误检查,我的错。谢谢你,最后两个选项就是我想要的。我设法找到的指南都是旧的和狭窄的,这是我第一次在没有框架帮助的情况下编写网络代码,我对API应该如何工作一无所知。