Question

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.

Était-ce utile?

La 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

Autres conseils

client should get exception while sending 6th msg

Not at all. The sender will get an exception eventually, if he keeps sending, but because of TCP buffering at both the sender and receiver, and because TCP sends are asynchronous, it definitely won't happen on the next write after the peer closes. It will happen afterwards, after:

  1. TCP has decided to send its send buffer.
  2. TCP has detected an incoming RST response.
  3. the application next calls send().

EJP's answer describes why your current solution will not work.

As for an alternative, the only way to do what you want is to make the protocol bi-directinoal. in other words, you need to set up some sort of server acknowledgement of each message received. the "simple" way to do this is to have the server immediately ack each message received. more complex protocols could allow for some sort of periodic ack. even with this solution, there are still problem areas. for instance, the client may not receive an ack (if the socket closes prematurely) and therefore the server may see the same message twice. long story short, you need some sort of reliable message transfer protocol, and there is plenty of documentation for these already online.

Interesting problem. Perhaps try flushing the OutputStream after each client write.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top