Android is socket-connected, although it cannot

Firstly, I know what you think is a strange question, and it seems that this may not be true, but here I am on this ...

A project is a project that sends an array of bytes over a socket and also receives data from the socket. I am currently working on protection. I want to make sure that the user knows when the socket cannot connect for any reason ... You can view the connection code below.

So, I have an Android application that connects to an executable file on a computer running Windows Server in an office elsewhere in the world. The socket is connected through a regular IP address and port number.

I am testing the application using an Android 5.0 phone (lollipop) ...

Now there are enough boring things: If I close the server and turn it off completely and test it on wi-fi, then the socket will not be executed - this is correct. It cannot connect and it will throw a SocketException. This is normal and works as expected. Now, if I had to turn off Wi-Fi and use mobile data (I use o2 here in the sunny United Kingdom!), Then there is a problem using the same code when the server is still turned off, I do not throw a SocketException, instead simple code thinks it is connected to a socket. The following is an example connection.

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 without socket errors. There are also no timeouts.

 outputstream = socket.getOutputStream(); 

also works. I get the result " java.net.PlainSocketImpl$PlainSocketOutputStream@48e6f6e ", which is also great.

socket.getInputStream() returns " java.net.PlainSocketImpl$PlainSocketInputStream@1ea305a5 "

+5
source share
2 answers

It is interesting. I have tested and can reproduce the behavior you described.

Firstly, the highest voted answer gives a good discussion of the Java Socket API:

Java socket API: how do I know if a connection is closed?

From this, I wondered if trying to read a byte from a socket would allow us to check if it was 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 case the endpoint is invalid, I found that the call to read() returns -1 after about 10 seconds.

In the case where there is a listener on the IP and port, the read() call seems to block forever until something happens, for example, actually receiving data from the server or losing the Internet connection, etc.

Based on this difference, I wrapped this test in 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 test it like this:

 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 in Wifi, the VerifiedSocket constructor throws an exception with a timeout. If I use an invalid endpoint for mobile data, it throws an IOException with the message "Endpoint is invalid." Not perfect, but hopefully this can help you.

+3
source

If the environment in which you run the code does not behave as you need (for some reason, Robert wrote one), you can enable the working environment with the advent of a new abstraction.

You can do a lot to solve it; here is one solution - to implement a shell on top of the protocol used (java.net.Socket), which will extend the Socket class and override the connect () / getInputStream () / getOutputStream () / whatever-method-you-need () methods the way the code first tries to contact the server before it returns a valid stream.

In the shell, a pair of bytes (client key) is sent to the server, to which the server responds with a response (server key). Keep the keys secret, only clients and the server will know the keys. If the server does not respond with a timeout, throw an exception.

 private class MySocket extends Socket { Socket inner; MySocket(Socket inner) { this.inner = inner; } @Override InputStream getInputStream() { // do your ping here // if ping fails, thrown an exception return inner.getInputStream(); } ... override additional methods as you need } 

You can use this ping pattern in all places to find that the connection is lost.

I know that you are doing what the main layer should do ... but if it does not, what do you expect, what can you do?

0
source

Source: https://habr.com/ru/post/1213995/


All Articles