3
votes

I'm programming a server/client system using winsock2 and it works great when I connect the client to the server name or the server IPv6 address. However, when I use the server IPv4 address I get error "Connection refused" from the call to connect() in the client.

This error occurs with either my client or using telnet. However, I can successfully ping the server using either of the three name, IPv4 or IPv6.

I've tried this running both server and client on the same machine, on separate machines, and firewalls deactivated on all machines.

Here is an excerpt of my server initialization and listening code:

SOCKET sockfd = INVALID_SOCKET, in_socketID;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
sockaddr_storage incoming_addr;
int addr_size;
int tmp_err;
const char *sPort = "20152";

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;

tmp_err = getaddrinfo(NULL, sPort, &hints, &servinfo);
if (tmp_err != 0)
    throw exception("ERROR: getaddrinfo failed");

// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
    ip = p;
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == INVALID_SOCKET)
    {
        cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
    } // end if
    else if (bind(sockfd, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
    {
        cerr << "ERROR on bind(): " << WSAGetLastError() << endl;
        closesocket(sockfd);
        sockfd = INVALID_SOCKET;
    } // end if
} // end for

if (sockfd == INVALID_SOCKET)
{
    // looped off the end of the list with no successful bind
    throw exception("ERROR: Failed to bind socket");
}

// clean up
if (servinfo)
    freeaddrinfo(servinfo);

if (listen(sockfd, SOMAXCONN ) == SOCKET_ERROR)
    throw exception("Listen failed");

while (true)
{
    memset(&incoming_addr, 0, sizeof(incoming_addr));
    addr_size = sizeof(incoming_addr);
    in_socketID = accept(socketID, (sockaddr *)&incoming_addr, &addr_size);

    // do stuff with incoming connection
}

This is my client code:

int sockfd = INVALID_SOCKET;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
int tmp_err;
const char *sHost = "192.168.1.136";
const char *sPort = "20152";

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // use TCP

tmp_err = getaddrinfo(sHost,        // web address or ip to connect to
                      sPort,        // port or protocol
                      &hints,       // initialized hints structure
                      &servinfo);   // return structure
if (tmp_err != 0)
    throw exception("ERROR: getaddrinfo failed");

// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
    ip = p;
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == INVALID_SOCKET)
    {
        cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
        //continue;
    } // end if
    else if (connect(sockfd, p->ai_addr, p->ai_addrlen) < 0)
    {
        cerr << "ERROR on connect(): " << WSAGetLastError() << endl;
        closesocket(sockfd);
        sockfd = INVALID_SOCKET;
        //continue;
    } // end if
} // end for

if (sockfd == INVALID_SOCKET)
    throw exception("ERROR: Failed to connect");


// clean up
if (servinfo)
    freeaddrinfo(servinfo);

// do stuff with new socket

I already read several similar questions in the site, but none answered this issue.

How can I connect also to the server IPv4 address? I need help, please.

Thanks.

EDIT:

From a suggestion given by user Sorayuki, I made some changes just to test if his theory was correct.

I was able to connect to the IPv4 by changing on the server

hints.ai_family = AF_UNSPEC;

to

hints.ai_family = AF_INET;

I knew it would obviously work, but when I do this, of course IPv6 doesn't work.

It appears user Sorayuki was right and my loop was connecting to IPv6.

It seems that there is no easy way to unify IPv6 and IPv4. Your socket must listen to either one or the other which makes the process really annoying.

According to the documentation, the old style to listen to both IPv4 and IPv6 is to create a socket for each and listen on both. This is for Windows Server 2003 and Windows XP SP1.

The preferred modern style (Windows Vista, 7 and 8) is to turn your socket into a dual socket and it will listen to both IPv4 and IPv6. However, your client must also be able to set up a dual socket, so, if your application is serving an old client, you are stuck with the old method.

Thanks!

3

3 Answers

1
votes

Is it because that you bind your server socket to an IPv6 address? in the "for" loop, IPv6 address appearing before IPv4 address seems to cause your server's socket listen on an IPv6 address. So your server is not listening on any IPv4 address, cause all connection towards IPv4 address of server is refused.

Try to see all listening port is on which IP address with tool or some command (eg. netstat)

3
votes

This is because binding to an IPv6 address does not magically bind to an IPv4 address as well.

On Linux, by default binding to [::] will cause IPv6 and IPv4 to work (unless /proc/sys/net/ipv6/bindv6only is set to 1).

However, on Mac OS X and Windows, binding to [::] will only work for IPv6. You must also bind to an IPv4 address (or 0.0.0.0) for it to work.

Your logic described in your comment "loop through all the results and bind to the first we can" is precisely the problem here. You should both bind to [::] with the IPV6_V6ONLY flag (see setsockopt()) and 0.0.0.0.

-1
votes

have you tried to run the server and client on the same machine?

this sounds like a firewall problem. if you succeed connecting telnet / your application on the same machine you'll know this is the problem.