Я экспериментирую с сокетами IPv6, особенно с возможностью «двойного стека», предлагаемой в Windows Vista и более поздних версиях, и, очевидно, по умолчанию в Unix. Я обнаружил, что когда я привязываю свой сервер к определенному IP-адресу или к разрешению имени хоста моей локальной машины, я не могу принять соединение от клиента IPv4. Однако, когда я привязываюсь к INADDR_ANY, я могу.
Пожалуйста, рассмотрите следующий код для моего сервера. Вы можете видеть, что я следую совету Microsoft по созданию сокета IPv6, а затем устанавливаю флаг IPV6_V6ONLY на ноль:
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);
Вот код моего клиента. Вы можете видеть, что я создаю сокет IPv4 и пытаюсь подключиться к моему серверу:
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);
Результатом моего вызова соединения всегда будет 10061: соединение отклонено.
Если я изменяю код сервера для привязки к :: (или передаю NULL-хост в getaddrinfo () (то же самое)) и изменяю свой клиентский код, чтобы указать NULL-хост в вызове getaddrinfo (), то клиент V4 может подключиться отлично.
Кто-нибудь может объяснить, пожалуйста, почему? Я не читал ничего, что мы должны указать хост NULL (следовательно, использовать INADDR_ANY), если мы хотим работать с двумя сокетами. Это не может быть требованием, потому что у меня многосетевой хост, и я хочу принимать IPv4 только на некоторых из доступных IP-адресов?
ИЗМЕНИТЬ 15.05.2013:
Это соответствующая документация, которая сбила меня с толку относительно того, почему мой код не работает:
Из сокетов двойного стека для приложений Winsock IPv6
«Windows Vista и более поздние версии предлагают возможность создания одного сокета IPv6, который может обрабатывать трафик как IPv6, так и IPv4. Например, сокет прослушивания TCP для IPv6 создается, переводится в режим двойного стека и привязан к порту 5001. Этот двойной- сокет стека может принимать соединения от клиентов TCP IPv6, подключающихся к порту 5001, и от клиентов TCP IPv4, подключающихся к порту 5001. "
"По умолчанию сокет IPv6, созданный в Windows Vista и более поздних версиях, работает только по протоколу IPv6. Чтобы преобразовать сокет IPv6 в сокет с двойным стеком, необходимо вызвать функцию setsockopt с параметром сокета IPV6_V6ONLY, чтобы установить это значение на ноль до того, как сокет будет привязан к IP-адресу. Когда для параметра сокета IPV6_V6ONLY установлено значение 0, сокет, созданный для семейства адресов AF_INET6, может использоваться для отправки и получения пакетов на и от IPv6-адреса или сопоставленного IPv4 адрес. (выделено мной) "
bind
явно является конечной точкой IPv6, а не подстановочным знаком. - person Steve-o   schedule 10.05.2013