Eksperymentuję z gniazdami IPv6, w szczególności z funkcją "podwójnego stosu" oferowaną w systemie Windows Vista i nowszych wersjach, a najwyraźniej w systemie Unix domyślnie. Zauważyłem, że kiedy wiążę mój serwer z określonym adresem IP lub z rozpoznawaniem nazwy hosta mojego komputera lokalnego, nie mogę zaakceptować połączenia z klienta IPv4. Kiedy jednak połączę się z INADDR_ANY, mogę.Podłączanie klienta IPv4 do serwera IPv6: odmówiono połączenia
Proszę uwzględnić poniższy kod dla mojego serwera. Widać, że śledzę informacje Microsoftu tworzenia gniazdo IPv6, a następnie ustawienie flagi IPV6_V6ONLY do zera:
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. Można zobaczyć utworzyć gniazdo IPv4 i próbować połączyć się z 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 z mojego connect rozmowy jest zawsze 10061: odmowa połączenia.
Jeśli zmienię mój kod serwera, aby powiązać się z :: (lub przekazać hosta NULL do getaddrinfo() (to samo)) i zmienić kod klienta, aby określić hosta NULL w wywołaniu getaddrinfo(), a następnie Klient V4 może połączyć się dobrze.
Czy ktoś może wyjaśnić, dlaczego? Nie czytałem niczego, co musimy określić hosta NULL (stąd używać INADDR_ANY), jeśli chcemy zachowanie dual-socket. Nie może to być wymagane, ponieważ mam hosta wieloadresowego i chcę akceptować IPv4 tylko na niektórych dostępnych adresach IP?
EDIT 15/05/2013:
To jest odpowiednia dokumentacja, która ma zdobyć mnie mylić, dlaczego mój kod nie powiedzie się:
Od Dual-Stack Sockets for IPv6 Winsock Applications
„Windows Vista i później oferuje możliwość utworzenia pojedynczego gniazda IPv6 , które może obsłużyć zarówno ruch IPv6, jak i IPv4. Na przykład, tworzone jest gniazdo nasłuchiwania TCP IP2 dla TCP , umieszczane w podwójnym stacku Tryb k, i zobowiązany do portu 5001. To gniazdo dual-stack może akceptować połączenia z klientów IPv6 TCP podłączeniu do portu 5001 i od klientów IPv4 TCP łączących do portu 5001. "
" Domyślnie gniazda IPv6 utworzone w systemie Windows Vista i późniejszym tylko działa przez protokół IPv6. Aby gniazdo IPv6 na było wyposażone w gniazdo z dwoma stosami, należy ustawić funkcję setsockopt za pomocą opcji gniazda IPV6_V6ONLY, aby ustawić tę wartość na zero, zanim gniazdo zostanie przypisane do adresu IP. Po ustawieniu opcji gniazda IPV6_V6ONLY na 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 odwzorowanego IPv4 . (Kopalnia nacisk)”
Potrzebujesz punktu końcowego IPv4 dla połączeń IPv4, "bind" jest jawnie punktem końcowym IPv6, a nie symbolem wieloznacznym. –
@ Steve-o Nie rozumiem, co masz na myśli, przepraszam. Możesz wyjaśnić? – Wad
Bindowanie implementuje filtr, aby akceptować tylko dane o adresie docelowym pasującym do powiązanego adresu. Pakiety IPv4 będą miały docelowy adres IPv4, dlatego zostaną odrzucone. –