I am experimenting with IPv6 sockets, especially the dual-stack features offered on Windows Vista and later, and apparently on Unix by default. I found that when I bind my server to a specific IP address or to the host name resolution on my local computer, I cannot accept the connection to the IPv4 client. However, when I get attached to INADDR_ANY, I can.
Please consider the following code for my server. You can see that I follow Microsoft's recommendations for creating an IPv6 socket, and then set the IPV6_V6ONLY flag to 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);
Here is the code for my client. You can see that I am creating an IPv4 socket and trying to connect to my server:
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);
The result of my connection is always 10061: connection failure.
If I changed my server code to bind to :: (or passed the getaddrinfo () NULL host (the same)) and changed the client code to specify the NULL host in the getaddrinfo () call, then the V4 client can connect normally.
Can someone explain why please? I have not read anything that we must specify a NULL host (hence using INADDR_ANY) if we want two-socket behavior. This may not be a requirement because I have a multihomed host and I want to accept IPv4 only on some available IP addresses?
EDIT 05/15/2013:
This is the relevant documentation that confused me why my code crashes:
From Double Stacks to IPv6 Winsock Applications
"Windows Vista and later offer the ability to create a single IPv6 that can handle both IPv6 and IPv4 traffic. For example, a TCP listening socket for IPv6 is placed in dual-stack mode and is connected to port 5001. This dual-core socket can accept TCP connections. IPv6 clients connecting to port 5001 and IPv4 TCP clients connecting to port 5001. "
"By default, the IPv6 socket created in Windows Vista and later works using IPv6 protocol. To make an IPv6 socket into a two-stack socket, the setsockopt function must be called using the IPv6_V6ONLY socket option to set this value to zero before the socket is bound to the IP address . If the socket option IPV6_V6ONLY is set to zero, you can use the socket created for the AF_INET6 address family. Send and receive packets to and from the IPv6 address or IPv4 displayed address. (Emphasis mine) "