IPv6 连接错误 WSAEAFNOSUPPORT
IPv6 connect error WSAEAFNOSUPPORT
我使用主机名解析器为 IPv4 / IPv6 编写了一个小型客户端。
对于 IPv4 和解析器,它很好,但是当连接()时 IPv6 没有问题 WSAGetLastError()说 WSAEAFNOSUPPORT。
我将所有结构(AF_INET -> AF_INET6,SOCKADDR_IN -> SOCKADDR_IN6)切换到 IPv6 版本。
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
printf("Simple_Client IPv4 & IPv6\n\n");
// Initiates Winsock
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 0), &WSAData);
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
BOOL is_IPv6 = FALSE;
std::string str_dest_ip = "";
addrinfo hints = { 0 };
hints.ai_flags = AI_ALL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo * pResult;
getaddrinfo(str_HOSTNAME.c_str(), NULL, &hints, &pResult);
if (pResult == NULL)
{
printf("pResult error\n");
return -1;
}
if (pResult->ai_family == AF_INET)
{
printf("getaddrinfo = AF_INET (IPv4)\n");
is_IPv6 = FALSE;
}
if (pResult->ai_family == AF_INET6)
{
printf("getaddrinfo = AF_INET6 (IPv6)\n");
is_IPv6 = TRUE;
}
char str[128];
memset(str, 0, sizeof(str));
if (is_IPv6 == FALSE) // IPv4
{
if (inet_ntop(AF_INET, &(*((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr))), str, INET_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
else
printf("inet_ntop error\n");
}
if (is_IPv6 == TRUE) // IPv6
{
if (inet_ntop(AF_INET6, &(*((ULONG*)&(((sockaddr_in6 *)pResult->ai_addr)->sin6_addr))), str, INET6_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
}
printf("%s : %s | Port : %i\n", is_IPv6 ? "IPv6" : "IPv4", str_dest_ip.c_str(), PORT);
// Connect to the HOSTNAME
SOCKET sock;
if (is_IPv6 == TRUE)
{
SOCKADDR_IN6 sin;
sin.sin6_family = AF_INET6;
if(inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin) != 1)
printf("ERROR inet_pton %i\n", WSAGetLastError());
sin.sin6_port = htons(PORT);
sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
return -2;
if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR)
{
printf("Connect Success to %s | PORT : %i\n", str_dest_ip.c_str(), PORT);
}
else
{
printf("ERROR connect to %s | PORT : %i : %i\n", str_dest_ip.c_str(), PORT, WSAGetLastError());
Sleep(10000);
return -2;
}
}
char buf[1024] = { 0 };
int size_recv = recv(sock, buf, sizeof(buf), 0);
printf("SIZE RECV = %i | DATA RECV = %s\n", size_recv, char_to_string(buf, size_recv).c_str());
WSACleanup();
getchar();
return 0;
}
如果有人有想法,感谢阅读。
问题出在这里:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin)
这会将 IPv6 地址写入 sin6_family 字段,破坏了整个结构。
应该是:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin.sin6_addr)
在开头 zero-initialize 整个 sin
结构也是一个好主意,因为它的字段比您填写的要多。
您没有正确使用 getaddrinfo()
。
一方面,getaddrinfo()
returns 您忽略的错误代码。
另一方面,getaddrinfo()
returns 一个 链表 可能 包含多个混合地址IPv4 and/or IPv6,由于您使用 AF_UNSPEC
。如果您只对 IPv6 感兴趣,请将 hints.ai_family
设置为 AF_INET6
而不是 AF_UNSPEC
.
但无论哪种方式,一个给定的主机名可能有多个与之关联的 IP,并且它们可能无法从您的位置访问,因此您应该 connect()
'ing 到列表中的每个地址,或者一次一个或并行,直到其中一个成功。
此外,在这种情况下根本不需要使用 inet_pton()
(正如@rustyx 的回答所解释的,您没有正确使用它)。 getaddrinfo()
returns 完全填充 sockaddr_in(6)
结构,您可以将 as-is 传递给 connect()
。
尝试更像这样的东西:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#pragma comment(lib, "ws2_32.lib")
std::string addr_to_str(addrinfo *addr)
{
char str[128];
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
if (inet_ntop(AF_INET, &(((sockaddr_in*)(addr->ai_addr))->sin_addr), str, INET_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
case AF_INET6: // IPv6
{
if (inet_ntop(AF_INET6, &(((sockaddr_in6*)(addr->ai_addr))->sin6_addr), str, INET6_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
default:
ret = WSAEAFNOSUPPORT;
break;
}
std::cerr << "inet_ntop error: " << ret << std::endl;
return "";
}
int main()
{
std::cout << "Simple_Client IPv4 & IPv6" << std::endl << std::endl;
// Initiates Winsock
WSADATA WSAData;
int ret = WSAStartup(MAKEWORD(2, 0), &WSAData);
if (ret != 0)
{
std::cerr << "WSAStartup error: " << ret << std::endl;
return -1;
}
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
addrinfo hints = { 0 };
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo *pResult = NULL;
ret = getaddrinfo(str_HOSTNAME.c_str(), std::to_string(PORT).c_str(), &hints, &pResult);
if (ret != 0)
{
std::cerr << "getaddrinfo error: " << ret << std::endl;
WSACleanup();
return -1;
}
// Log the IPs
bool has_IPv4 = false;
bool has_IPv6 = false;
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
has_IPv4 = true;
std::cout << "IPv4 : " << addr_to_str(addr);
break;
}
case AF_INET6: // IPv6
{
has_IPv6 = true;
std::cout << "IPv6 : " << addr_to_str(addr);
break;
}
}
}
// Connect to the HOSTNAME
SOCKET sock = INVALID_SOCKET;
if (has_IPv6)
{
// try IPv6 first...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET6)
continue;
std::cout << "Connecting to IPv6 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
if ((sock == INVALID_SOCKET) && (has_IPv4))
{
// try IPv4 next...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET)
continue;
std::cout << "Connecting to IPv4 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
freeaddrinfo(pResult);
if (sock == INVALID_SOCKET)
{
WSACleanup();
return -2;
}
std::cout << "Connect Successful" << std::endl;
char buf[1024];
int size_recv = recv(sock, buf, sizeof(buf), 0);
if (size_recv == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "recv error: " << ret << std::endl;
}
else
{
std::cout << "SIZE RECV = " << size_recv;
if (size_recv > 0)
{
std::cout << " | DATA RECV = ";
std::cout.write(buf, size_recv);
}
std::cout << std::endl;
}
closesocket(sock);
WSACleanup();
std::cin.get();
return 0;
}
我使用主机名解析器为 IPv4 / IPv6 编写了一个小型客户端。 对于 IPv4 和解析器,它很好,但是当连接()时 IPv6 没有问题 WSAGetLastError()说 WSAEAFNOSUPPORT。
我将所有结构(AF_INET -> AF_INET6,SOCKADDR_IN -> SOCKADDR_IN6)切换到 IPv6 版本。
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
printf("Simple_Client IPv4 & IPv6\n\n");
// Initiates Winsock
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 0), &WSAData);
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
BOOL is_IPv6 = FALSE;
std::string str_dest_ip = "";
addrinfo hints = { 0 };
hints.ai_flags = AI_ALL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo * pResult;
getaddrinfo(str_HOSTNAME.c_str(), NULL, &hints, &pResult);
if (pResult == NULL)
{
printf("pResult error\n");
return -1;
}
if (pResult->ai_family == AF_INET)
{
printf("getaddrinfo = AF_INET (IPv4)\n");
is_IPv6 = FALSE;
}
if (pResult->ai_family == AF_INET6)
{
printf("getaddrinfo = AF_INET6 (IPv6)\n");
is_IPv6 = TRUE;
}
char str[128];
memset(str, 0, sizeof(str));
if (is_IPv6 == FALSE) // IPv4
{
if (inet_ntop(AF_INET, &(*((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr))), str, INET_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
else
printf("inet_ntop error\n");
}
if (is_IPv6 == TRUE) // IPv6
{
if (inet_ntop(AF_INET6, &(*((ULONG*)&(((sockaddr_in6 *)pResult->ai_addr)->sin6_addr))), str, INET6_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
}
printf("%s : %s | Port : %i\n", is_IPv6 ? "IPv6" : "IPv4", str_dest_ip.c_str(), PORT);
// Connect to the HOSTNAME
SOCKET sock;
if (is_IPv6 == TRUE)
{
SOCKADDR_IN6 sin;
sin.sin6_family = AF_INET6;
if(inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin) != 1)
printf("ERROR inet_pton %i\n", WSAGetLastError());
sin.sin6_port = htons(PORT);
sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
return -2;
if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR)
{
printf("Connect Success to %s | PORT : %i\n", str_dest_ip.c_str(), PORT);
}
else
{
printf("ERROR connect to %s | PORT : %i : %i\n", str_dest_ip.c_str(), PORT, WSAGetLastError());
Sleep(10000);
return -2;
}
}
char buf[1024] = { 0 };
int size_recv = recv(sock, buf, sizeof(buf), 0);
printf("SIZE RECV = %i | DATA RECV = %s\n", size_recv, char_to_string(buf, size_recv).c_str());
WSACleanup();
getchar();
return 0;
}
如果有人有想法,感谢阅读。
问题出在这里:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin)
这会将 IPv6 地址写入 sin6_family 字段,破坏了整个结构。
应该是:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin.sin6_addr)
在开头 zero-initialize 整个 sin
结构也是一个好主意,因为它的字段比您填写的要多。
您没有正确使用 getaddrinfo()
。
一方面,getaddrinfo()
returns 您忽略的错误代码。
另一方面,getaddrinfo()
returns 一个 链表 可能 包含多个混合地址IPv4 and/or IPv6,由于您使用 AF_UNSPEC
。如果您只对 IPv6 感兴趣,请将 hints.ai_family
设置为 AF_INET6
而不是 AF_UNSPEC
.
但无论哪种方式,一个给定的主机名可能有多个与之关联的 IP,并且它们可能无法从您的位置访问,因此您应该 connect()
'ing 到列表中的每个地址,或者一次一个或并行,直到其中一个成功。
此外,在这种情况下根本不需要使用 inet_pton()
(正如@rustyx 的回答所解释的,您没有正确使用它)。 getaddrinfo()
returns 完全填充 sockaddr_in(6)
结构,您可以将 as-is 传递给 connect()
。
尝试更像这样的东西:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#pragma comment(lib, "ws2_32.lib")
std::string addr_to_str(addrinfo *addr)
{
char str[128];
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
if (inet_ntop(AF_INET, &(((sockaddr_in*)(addr->ai_addr))->sin_addr), str, INET_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
case AF_INET6: // IPv6
{
if (inet_ntop(AF_INET6, &(((sockaddr_in6*)(addr->ai_addr))->sin6_addr), str, INET6_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
default:
ret = WSAEAFNOSUPPORT;
break;
}
std::cerr << "inet_ntop error: " << ret << std::endl;
return "";
}
int main()
{
std::cout << "Simple_Client IPv4 & IPv6" << std::endl << std::endl;
// Initiates Winsock
WSADATA WSAData;
int ret = WSAStartup(MAKEWORD(2, 0), &WSAData);
if (ret != 0)
{
std::cerr << "WSAStartup error: " << ret << std::endl;
return -1;
}
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
addrinfo hints = { 0 };
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo *pResult = NULL;
ret = getaddrinfo(str_HOSTNAME.c_str(), std::to_string(PORT).c_str(), &hints, &pResult);
if (ret != 0)
{
std::cerr << "getaddrinfo error: " << ret << std::endl;
WSACleanup();
return -1;
}
// Log the IPs
bool has_IPv4 = false;
bool has_IPv6 = false;
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
has_IPv4 = true;
std::cout << "IPv4 : " << addr_to_str(addr);
break;
}
case AF_INET6: // IPv6
{
has_IPv6 = true;
std::cout << "IPv6 : " << addr_to_str(addr);
break;
}
}
}
// Connect to the HOSTNAME
SOCKET sock = INVALID_SOCKET;
if (has_IPv6)
{
// try IPv6 first...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET6)
continue;
std::cout << "Connecting to IPv6 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
if ((sock == INVALID_SOCKET) && (has_IPv4))
{
// try IPv4 next...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET)
continue;
std::cout << "Connecting to IPv4 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
freeaddrinfo(pResult);
if (sock == INVALID_SOCKET)
{
WSACleanup();
return -2;
}
std::cout << "Connect Successful" << std::endl;
char buf[1024];
int size_recv = recv(sock, buf, sizeof(buf), 0);
if (size_recv == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "recv error: " << ret << std::endl;
}
else
{
std::cout << "SIZE RECV = " << size_recv;
if (size_recv > 0)
{
std::cout << " | DATA RECV = ";
std::cout.write(buf, size_recv);
}
std::cout << std::endl;
}
closesocket(sock);
WSACleanup();
std::cin.get();
return 0;
}