Android - Socket Connected even though it cannot

android, sockets

Solution

This is interesting. I have been testing and can replicate the behaviour you describe.

Firstly, the highest voted answer here provides a good discussion on the Java Socket API:

Java socket API: How to tell if a connection has been closed?

From this, I wondered whether attempting to read a byte from the socket would allow us to check if it is really open

// Endpoint that does not exist
Socket socket = new Socket("1.2.3.4", 1234);
InputStream tmpIn = socket.getInputStream();
int result = tmpIn.read();
Log.i("SocketTest", "read() result: " + result);

In the case that the endpoint is invalid, I have found that the call to `read()` returns -1 after approximately 10 seconds.

In the case that there is a listener on the IP and port, the call to `read()` seems to block indefinitely until something happens, such as actually receiving data from the server, or losing internet connectivity etc.

Based on this difference, I have wrapped this test up into a new class:

public class VerifiedSocket extends Socket {

    public VerifiedSocket(String ip, int port, int millisecondTimeout) throws UnknownHostException, IOException{
        super(ip,port);

        ReadThread tryRead = new ReadThread();      
        tryRead.start();
        try {
            tryRead.join(millisecondTimeout);
        } catch (InterruptedException e) {
        }
        if(tryRead.getReadValue()==-1){
            // We read -1 so assume endpoint invalid
            throw new IOException("Endpoint is invalid");
        }       
    }

    private class ReadThread extends Thread{
        private int readValue = 512; // Impossible byte value

        @Override
        public void run(){              
            try {
                InputStream input = getInputStream();
                readValue = input.read();
            } catch (IOException e) {                   
            }               
        }

        public int getReadValue(){
            return readValue;
        }
    }       
}

And then tested it as follows:

try {
    VerifiedSocket socket = new VerifiedSocket("1.2.3.4", 1234, 20000);
    Log.i("SocketTest", "Endpoint is valid");
    socket.close();
} catch (UnknownHostException e) {      
    Log.e("SocketTest", e.getLocalizedMessage());
} catch (IOException e) {       
    Log.e("SocketTest", e.getLocalizedMessage());
}

If I use an invalid endpoint on Wifi, the constructor of the `VerifiedSocket` throws an exception with a timeout. If I use an invalid endpoint on mobile data, it throws the IOException with the 'Endpoint is invalid' message. Not perfect, but hopefully it might help you.

Problem

Firstly I know what your thinking, it's a strange question and it seems like it cannot be true but here me out on this one... The project is a project which sends an array of bytes over a socket and also receives data from a socket. I am currently working on protection. I want to make sure that the user knows when the socket can't connect for whatever reason... You can view the connection code below. So I have an android app which connects to a executable on a Windows Server computer in a office elsewhere in the world. The socket connects via the usual IP address and port number. I am testing the app using a Android 5.0 (lollipop) phone... Now enough of the boring stuff: If I close the server and turn it off completely and test this on wi-fi then the socket fails - which is correct. It cannot connect, and it will throw a SocketException. Which is fine and is working as expected. Now if I were to turn wi-fi off and use mobile data (I use o2 here in the sunny United Kingdom!) then an issue arises, using the same code, with the server still turned off, I do not throw a SocketException, instead the code simple thinks it has connected on a socket. The connection thread is below. ``` Thread StartConnection = new Thread() { @Override public void run() { int dstPort = 10600; socket = new Socket(); try { ipaddress = InetAddress.getByName(dstName); } catch (UnknownHostException uhe) { uhe.printStackTrace(); } j = new InetSocketAddress(IPString , dstPort); try { socket.setKeepAlive(true); socket.setReuseAddress(true); socket.setTcpNoDelay(true); socket.setSoTimeout(5000); socket.connect(j, 5000); connected = true; outputstream = socket.getOutputStream(); DataThread(); SocketError = false; } catch (SocketException se) { se.printStackTrace(); } catch (IllegalArgumentException iae) { iae.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }; StartConnection.start(); ``` ``` socket.connect(j, 5000); ``` works fine no socket errors. No timeouts either. ``` outputstream = socket.getOutputStream(); ``` also works. I get a result of "java.net.PlainSocketImpl$PlainSocketOutputStream@48e6f6e" which is also fine. `socket.getInputStream()` returns "java.net.PlainSocketImpl$PlainSocketInputStream@1ea305a5"

Original source

Related problems