getaddrinfo 和 INADDR_ANY
getaddrinfo and INADDR_ANY
找了几个小时,还是一头雾水。根据我的发现,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
与您的问题无关。
getaddrinfo()
的第一个参数是 const char *
指定 IP 地址或主机名。但是 INADDR_ANY
是一个整数。您的代码甚至可以编译的唯一原因是因为 INADDR_ANY
被定义为整数常量 0,这是唯一允许分配给指针的整数常量。因此,您实际上是将 NULL 传递给第一个参数。在这种情况下这很好,因为这正是您所需要的。
您没有考虑到的是 getaddrinfo()
return 是一个 链接列表 地址,其中可能包含多个地址, 特别是 如果你使用 AF_UNSPEC
。使用该系列告诉 getaddrinfo()
它可以 return 地址用于 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()
,在这种情况下它并不能真正帮助您。您可以直接创建和绑定 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...
找了几个小时,还是一头雾水。根据我的发现,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
与您的问题无关。
getaddrinfo()
的第一个参数是 const char *
指定 IP 地址或主机名。但是 INADDR_ANY
是一个整数。您的代码甚至可以编译的唯一原因是因为 INADDR_ANY
被定义为整数常量 0,这是唯一允许分配给指针的整数常量。因此,您实际上是将 NULL 传递给第一个参数。在这种情况下这很好,因为这正是您所需要的。
您没有考虑到的是 getaddrinfo()
return 是一个 链接列表 地址,其中可能包含多个地址, 特别是 如果你使用 AF_UNSPEC
。使用该系列告诉 getaddrinfo()
它可以 return 地址用于 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()
,在这种情况下它并不能真正帮助您。您可以直接创建和绑定 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...