Odd behavior with Java socket on Android and IPv6

Viewed 1272

I have some code that uses sockets. Simple stuff like:

Socket socket = new Socket();
InetSocketAddress endpoint = new InetSocketAddress(host, port);
socket.connect(endpoint, 120000);

For some web addresses it will freeze until it times out but only for some of my users. I finally tracked it down by having the users send me logs and the issue happens when endpoint is an IPv6 address. So just to clarify, if a web address has no IPv6 records, then it will just work for those users with the issue. But if the web address has an IPv6 record then it will timeout for those users. All other users (including myself) have no issue with it.

What is strange is that this issue does not happen if I use OkHttp. Also if the user just loads the page using Chrome on the phone then the issue doesn't happen. I know newer Android versions use OkHttp for the HttpUrlConnection so that might be why.

I have fixed it for some users by using:

java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");

But for other users not even that helps.

I am trying to understand why this is even an issue and why only for a few users? And why on some phones the system properties fix it and on others it just gets ignored.

EDIT: I have found a confirmed solution for the users for whom the system properties don't fix the issue.

Basically my code was:

Socket socket = new Socket();
InetSocketAddress endpoint = new InetSocketAddress(host, port);
try{
    socket.connect(endpoint, 120000);
    ....

Now it is:

    Socket socket = new Socket();
    InetAddress address = InetAddress.getByName(host);
    if (address instanceof Inet6Address) {
        Log.w(TAG, "Found ipv6 address, looking for ipv4 " + address);
        InetAddress[] inetAddressArray = InetAddress.getAllByName(host);
        for (int i = 0; i < inetAddressArray.length; i++) {
            if (inetAddressArray[i] instanceof Inet4Address) {
                address = inetAddressArray[i];
                Log.w(TAG, "Found ipv4 " + address);
                break;
            }
        }
    }
    InetSocketAddress endpoint = new InetSocketAddress(address, port);
    //InetSocketAddress endpoint = new InetSocketAddress(host, port);
    try {
        socket.connect(endpoint, 120000);
        ......

Essentially I am forcing the socket to use an IPv4 address if InetAddress.getByName(host) returned an IPv6 address and an IPv4 is available. I'm a little nervous about doing this so I would still prefer if someone has a better solution.

0 Answers
Related