Podłączanie klienta IPv4 do serwera IPv6: odrzucono połączenie
Eksperymentuję z gniazdami IPv6, w szczególności z „podwójnym stosem” oferowanym w Windows Vista i nowszych wersjach, a domyślnie w Uniksie. Stwierdzam, że kiedy wiążę serwer z określonym adresem IP lub z rozdzielczością nazwy hosta na moim komputerze lokalnym, nie mogę zaakceptować połączenia od klienta IPv4. Kiedy jednak łączę się z INADDR_ANY, mogę to zrobić.
Proszę wziąć pod uwagę następujący kod dla mojego serwera. Widać, że postępuję zgodnie z zaleceniami Microsoftu dotyczącymi tworzenia gniazda IPv6, a następnie ustawiania flagi IPV6_V6ONLY na zero:
addrinfo* result, *pCurrent, hints;
memset(&hints, 0, sizeof hints); // Must do this!
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // We intend to use the addrinfo in a call to connect(). (I know it is ignored if we specify a server to connect to...)
int nRet = getaddrinfo("powerhouse", "82", &hints, &result);
SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
int no = 0;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0)
return -1;
if (bind(sock, result->ai_addr, result->ai_addrlen) == SOCKET_ERROR)
return -1;
if (listen(sock, SOMAXCONN) == SOCKET_ERROR)
return -1;
SOCKET sockClient = accept(sock, NULL, NULL);
Oto kod dla mojego klienta. Widać, że tworzę gniazdo IPv4 i próbuję połączyć się z moim serwerem:
addrinfo* result, *pCurrent, hints;
memset(&hints, 0, sizeof hints); // Must do this!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo("powerhouse", "82", &hints, &result) != 0)
return -1;
SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
int nRet = connect(sock, result->ai_addr, result->ai_addrlen);
Wynik mojego połączenia jest zawsze 10061: połączenie odrzucone.
Jeśli zmienię kod serwera, aby powiązać z :: (lub przekazać host NULL do getaddrinfo () (to samo)), i zmienić kod mojego klienta, aby określić host NULL w wywołaniu getaddrinfo (), to klient V4 może się połączyć w porządku.
Czy ktoś może wyjaśnić, dlaczego proszę? Nie przeczytałem niczego, co musimy określić jako host NULL (stąd użyj INADDR_ANY), jeśli chcemy zachowania dual-socket. Nie może to być wymogiem, ponieważ to, co mam hosta wieloadresowego i chcę zaakceptować IPv4 tylko na niektórych dostępnych IP?
EDYTUJ 15/05/2013:
Jest to odpowiednia dokumentacja, która sprawiła, że pomyliłem się, dlaczego mój kod nie działa:
ZGniazda Dual-Stack dla aplikacji Winsock IPv6
„System Windows Vista i nowsze oferują możliwość utworzenia pojedynczego gniazda IPv6, które obsługuje zarówno ruch IPv6, jak i IPv4. Na przykład tworzone jest gniazdo nasłuchiwania TCP dla IPv6, przełączane w tryb podwójnego stosu i powiązane z portem 5001. Ten podwójny gniazdo stosu może akceptować połączenia od klientów TCP IPv6 łączących się z portem 5001 iz klientów TCP IPv4 łączących się z portem 5001. "
„Domyślnie gniazdo IPv6 utworzone w systemie Windows Vista i nowszym działa tylko za pośrednictwem protokołu IPv6. Aby gniazdo IPv6 stało się gniazdem z dwoma stosami, należy wywołać funkcję setsockopt za pomocą opcji gniazda IPV6_V6ONLY, aby ustawić tę wartość na zero, zanim gniazdo zostanie powiązane z adresem IP.Gdy opcja gniazda IPV6_V6ONLY jest ustawiona na zero, gniazdo utworzone dla rodziny adresów AF_INET6 może być używane do wysyłania i odbierania pakietów do iz adresu IPv6 lub adresu mapowanego IPv4. (podkreślenie moje)