javasocketstcpdata-loss

Data loss even if remote socket is gracefully closed in java


I am using TCP in my application. I am facing data loss issue if even if close socket gracefully. Here is the two sample program to replicate scenario.

//TCP Sender program continuously sends data

public class TCPClient {

 public static void main(String[] args) throws UnknownHostException, IOException {
    Socket soc = new Socket("xx.xx.xx.xx",9999);
    OutputStream stream = soc.getOutputStream();
    int i=1 ;
    while(true) {

        System.out.println("Writing "+ i);
        stream.write(i++);
        stream.flush();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            break;
        }
    }

  }

}

// TCP Server/Receiver

  public class TCPServer2 {

 public static void main(String[] args) throws IOException {
    System.out.println("program started");
    ServerSocket serverSock = new ServerSocket(9999);
    Socket socket =serverSock.accept();
    System.out.println("client connected");
     InputStream stream = socket.getInputStream();
     InputStreamReader reader = null;
      reader = new InputStreamReader(stream);
      socket.setTcpNoDelay(true);
      int n=0;
      int i=1;
      while (true) {
        try {
            n = reader.read();
            System.out.println("Msg No.: "+n);
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
            break;
        }
        if(i==5) {
            System.out.println("closing socket");
            socket.close();
            //client should get exception while sending 6th msg
            break;
        }i++;

    }
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      br.readLine();

}

}

Now Ideally TCPClient program should get exception while sending 6th message but it gets Exception while sending 7th message. Is there any way to avoid this data loss problem apart from using high level protocols and SO_LINGER(linger does helps in this program but it may cause data loss issue in several other scenarios)?
NB: This message loss issue occurs if we use two different windows machine. On the same machine it works fine.


Solution

  • EJP is correct but I think your question was is there any way to avoid data loss without using higher level protocols? In your scenario Answer is no.

    Why ? To understand why we need to understand difference between abortive close vs graceful socket close behaviour.

    To understand this distinction we need to look at what happens at the TCP protocol level. It is helpful to imagine an established TCP connection as actually two separate, semi-independent streams of data. If the two peers are A and B, then one stream delivers data from A to B, and the other stream from B to A. An orderly release occurs in two stages. First one side (say A) decides to stop sending data and sends a FIN message across to B. When the TCP stack at B's side receives the FIN it knows that no more data is coming from A, and whenever B reads all preceding data off the socket, further reads will return the value -1 to indicate end-of-file. This procedure is known as the TCP half-close, because only one half of the connection is closed. Not surprisingly, the procedure for the other half is exactly the same. B sends a FIN message to A, who eventually receives a -1 after reading all preceding data sent by A off the socket.

    By contrast, an abortive close uses the RST (Reset) message. If either side issues an RST, this means the entire connection is aborted and the TCP stack can throw away any queued data which has not been sent or received by either application.

    Sending a TCP FIN message means "I am finished sending", whereas Socket.close() means "I am finished sending and receiving." When you call Socket.close(), clearly it is no longer possible to send data; nor, however, is it possible to receive data. So what happens, for example, when A attempts an orderly release by closing the socket, but B continues to send data? This is perfectly allowable in the TCP specification, because as far as TCP is concerned only one half of the connection has been closed. But since A's socket is closed there is nobody to read data if B should continue sending. In this situation A's TCP stack must send an RST to forcibly terminate the connection.

    Possible Solution in your scenario: Use higher-level protocol.

    Source: https://docs.oracle.com/javase/8/docs/technotes/guides/net/articles/connection_release.html