TCP socket doesn't throw any exception when sending messages even though network connection is dropped
Clash Royale CLAN TAG#URR8PPP
TCP socket doesn't throw any exception when sending messages even though network connection is dropped
I have created a TCP web socket client in Java and connected it to a remote TCP server. In normal conditions it is working properly.
I need to detect the socket disconnection and handle some logic once disconnection triggers. I'm planning to detect the disconnection by catching the exception when socket client writing to its output stream for sending messages.
However when I turn off the wifi connection or remove the LAN cable of the socket client it doesn't identify the disconnection with the server within few seconds. It takes different time periods (seconds or minutes) for the client to identify the disconnection from the server and throw below exception when reading.
java.net.SocketTimeoutException: Read timed out.
My concern is during that disconnection period before socket client throws above exception, it successfully writes messages into his output stream and doesn't throw any socket exceptions. Because of that some of my messages get lost.
Below is the implementation of my sample socket client to test the scenario (Java).
Create socket connection
public void connect(String ipAddress, int port) throws MxLightAgentException
try
if (socket != null)
socket.close();
SocketAddress socketAddress = new InetSocketAddress(ipAddress, port);
socket = new Socket();
socket.connect(socketAddress, 20000);
socket.setKeepAlive(true);
setupSSL(ipAddress, port);
catch (IOException ex)
throw new MxLightAgentException
("Error when creating socket connection. IP: " + ipAddress + " Port: " + port, ex);
Disconnect socket connection
public void disconnect()
if (socket != null)
try
output.close();
incommingMessageProcessor.stopThread();
socket.close();
catch (IOException e)
LOGGER.info("disconnect", "Error on disconnection", e);
Writing to output stream
public boolean sendMessage(byte message) throws MxLightAgentException
try
LOGGER.info("Sending message via socket");
output = this.socket.getOutputStream();
byte len = DataConverter.toBytes(message.length);
output.write(len);
output.write(message);
output.flush();
LOGGER.info("Message sent and flushed");
return true;
catch (IOException ex)
throw new MxLightAgentException("Error when sending message to proxy.", ex);
I implemented a Netty socket client also and got the same result.
What could be the reason for not throwing any exception when writing to output stream even though the network connection is dropped?
Since I enabled "keepalive" of the socket, internally TCP connection should send keepalive messages by itself right? If so it should identify the disconnection.
– diyath.nelaka
Aug 13 at 1:47
2 Answers
2
TCP is a byte based medium. It keeps track of every byte you send. It waits for some time before reporting SocketTimeoutException
. It can automatically recover from short network failures. You can access bytesTransferred
field on the exception object. That will give you the number of bytes which are guaranteed to have reached the destination. There could be few more bytes which have reached the destination, but there is absolutely no way to know that.
SocketTimeoutException
bytesTransferred
For your purpose, you can either (1) keep track of your message lengths (2) implement message level ack
.
ack
You have byte len = DataConverter.toBytes(message.length);
You can have an ArrayList
of Integers
and add len
to that list. Now when exception occurs you can use this list to find out how many of the last messages were undelivered.
byte len = DataConverter.toBytes(message.length);
ArrayList
Integers
len
ack
You can use both the InputStream
and OutputSteam
of the socket. On the receiver side, after reading a message, you can send a reply back to sender. On sender side you can listen on the InputSteam
and when you do get a reply, you will know that your message successfully reached.
InputStream
OutputSteam
InputSteam
Honestly correctly implementing these, especially #2, is a respectable programming challenge. Good luck!
You should do your own probing on a periodic basis to see if it is still alive.
Keep a log of all messages sent so then if the socket does disconnect then you have a list of what may or may not have been sent on the server. When the connection comes back online you can compare what was missed and send the difference, or simply send it all again.
A note on your comment about keepalive. The javaDoc states, (emphasis mine):
When the keepalive option is set for a TCP socket and no data has been
exchanged across the socket in either direction for 2 hours (NOTE: the
actual value is implementation dependent), TCP automatically sends a
keepalive probe to the peer.
2 hours is a long time to wait for the default keep alive check, so you should do your own probing to see if it is still alive.
As for the answer to your question:
Your server and client has no way of immediately knowing if your connection has dropped unless the failure happened internally. It doesn't know if there was a bad network connection half way around the world, or if the destination server just has an incredibly bad ping (10seconds?), so instead, it needs to wait for a response instead of throwing an error straight away.
You need to write code that considers these issues. As mentioned above, a message log is one way to do it.
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
This is how TCP works. If you control both ends, you can send keepalive messages and assume the connection is lost if you miss one.
– Kevin Krumwiede
Aug 13 at 1:10