简体   繁体   中英

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?

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.

For your purpose, you can either (1) keep track of your message lengths (2) implement message level ack .

(1) Keeping track of your message lengths

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.

(2) Implement message level 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM