I have an Android device that communicates wirelessly with a PC, using a java.net.Socket. Everything hums along fine, but if I do nothing (i.e., no network use) for exactly 1 minute then when the Android sends a packet of data to the PC the PC receives it and sends an ACK, but the Android responds with an RST.
From Wireshark ( 10.1.2.1 is the Android, 10.1.2.11 is the PC)...
356 0.112470 10.1.2.1 10.1.2.11 TCP 97 34360→181 [PSH, ACK] Seq=1
Ack=1 Win=4935 Len=31 TSval=156103571 TSecr=320673352
359 0.000011 10.1.2.11 10.1.2.1 TCP 66 181→34360 [ACK] Seq=1 Ack=32
Win=260 Len=0 TSval=320738236 TSecr=156103571
360 0.000304 10.1.2.1 10.1.2.11 TCP 60 34360→181 [RST] Seq=32 Win=0
Len=0
At this point if I interrogate the socket's member variables it says . . .
- isConnected = true
- isCreated = true
- isInputShutdown = false
- isOutputShutdown = false
- isClosed = false
- isBound = true
... which looks like I should still be receiving packets just fine. So how do I figure out why I'm sending RST?
N.B. - there are no settings to "sleep" or turn off the wifi or display or any other battery-saving features set enabled on this device.
The 1 minute delay looks like a timeout. It may be the SO_TIMEOUT
but this does not generate network activity on itself. Also the fact that the last packet sent contains 31 bytes of data seems to indicate that the application is involved. A possible scenario would be :
- The android application times out (on its own or triggered by a socket's
SO_TIMEOUT
)
- It sends a last chunk of data, e.g. by flushing an output stream.
- It closes abruptly the socket, for example by using the
setSoLinger(true, 0)
socket option.
None of those Socket
methods returns the state of the connection. They are all about the internal state of the java.net.Socket
object, as determined by which constructors and methods you have called on it. They don't magically start returning false
if the peer drops the connection.
You will find when you go to use the socket for I/O that you will get an IOException: 'connection reset'
.
Why the connection has been reset is another matter. The usual reason is that you had sent to a connection that had already been closed by the peer, or that the peer closed the connection without reading all the data that had already arrived. In other words, an application protocol error. There are other reasons, but these are the most common.
This could be caused by your NAT router that tries to close sockets after a timeout to free up resources. Turning on keep_alive may help. You can also try to create your own network with static IP addresses bypassing the router to rule out this hypothesis.
When the connection is closed the isClosed()
method should return TRUE.
It's OK for the isConnected()
method to return TRUE even if the connection has been closed because what that method tells you is "if ever you managed to get connected", the variable state is not changed/refreshed when you get disconnected.
However in your case the connection has definitely not been closed as per documentation.
For more info see:
https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#isConnected--
https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#isClosed--
Note: It might help you to set to TRUE setKeepAlive(boolean)
See also:
https://docs.oracle.com/javase/8/docs/api/java/net/SocketOptions.html#SO_KEEPALIVE
https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#setKeepAlive-boolean-
Back to the problem, check for one of the following:
- Can you make sure for some reason there isn't another device with
the same IP as the Android? Also turn off you 3G/4G and stay
connected only in the wireless network.
- Is there a router doing NAT ? In this case the router can send the
RESET flag.
If everything seems fine (it's your Android phone replying with RST) and the SO_KEEPALIVE does not work then it might be some application level error that is killing the connection.