2

アプリケーションで TCP を使用しています。ソケットを正常に閉じても、データ損失の問題に直面しています。シナリオを複製する 2 つのサンプル プログラムを次に示します。

//TCP Sender プログラムは継続的にデータを送信します

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 サーバー/レシーバー

  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();

}

}

理想的には、TCPClient プログラムは 6 番目のメッセージの送信中に例外を取得する必要がありますが、7 番目のメッセージの送信中に例外を取得します。高レベルのプロトコルと SO_LINGER を使用する以外に、このデータ損失の問題を回避する方法はありますか?
注意:このメッセージ損失の問題は、2 つの異なる Windows マシンを使用している場合に発生します。同じマシンでは正常に動作します。

4

4 に答える 4

3

EJP は正しいですが、あなたの質問は、より高いレベルのプロトコルを使用せずにデータ損失を回避する方法があると思いますか? あなたのシナリオでは、答えはノーです。

なんで ?アボート クローズと正常なソケット クローズ動作の違いを理解する必要がある理由を理解するには。

この違いを理解するには、TCP プロトコル レベルで何が起こるかを調べる必要があります。確立された TCP 接続が、実際には 2 つの別個の半独立したデータ ストリームであると想像すると便利です。2 つのピアが A と B の場合、一方のストリームは A から B にデータを配信し、もう一方のストリームは B から A にデータを配信します。順序立ったリリースは 2 段階で発生します。最初に一方 (たとえば A) がデータの送信を停止することを決定し、B に FIN メッセージを送信します。B 側の TCP スタックが FIN を受信すると、A からデータが来ていないことを認識し、B が先行するすべてのデータを読み取るたびにさらに読み取ると、ファイルの終わりを示す値 -1 が返されます。この手順は、接続の半分だけが閉じられるため、TCP ハーフクローズと呼ばれます。当然のことながら、残りの半分の手順もまったく同じです。B が A に FIN メッセージを送信し、

対照的に、アボート クローズは RST (リセット) メッセージを使用します。いずれかの側が RST を発行した場合、これは接続全体が中止され、TCP スタックは、どちらのアプリケーションによっても送信または受信されていないキュー内のデータを破棄できることを意味します。

TCP FIN メッセージの送信は「送信が完了しました」を意味しますが、Socket.close() は「送受信が完了しました」を意味します。Socket.close() を呼び出すと、明らかにデータを送信できなくなります。ただし、データを受信することはできません。では、たとえば、A がソケットを閉じて正常な解放を試みたが、B がデータの送信を続けた場合はどうなるでしょうか? TCP に関する限り、接続の半分だけが閉じられているため、これは TCP 仕様では完全に許容されます。しかし、A のソケットが閉じられているため、B が送信を続けなければならない場合、データを読み取る人は誰もいません。この状況では、A の TCP スタックは接続を強制的に終了するために RST を送信する必要があります。

シナリオで考えられる解決策: より高いレベルのプロトコルを使用します。

ソース: https://docs.oracle.com/javase/8/docs/technotes/guides/net/articles/connection_release.html

于 2016-03-27T19:57:06.870 に答える
2

クライアントは 6 番目のメッセージの送信中に例外を受け取る必要があります

全くない。送信者が送信し続けると、最終的に例外が発生しますが、送信者と受信者の両方で TCP バッファリングが行われ、TCP 送信が非同期であるため、ピアが閉じた後の次の書き込みでは例外は発生しません。その後、次の後に発生します。

  1. TCP は、送信バッファを送信することを決定しました。
  2. TCP が着信 RST 応答を検出しました。
  3. アプリケーションは次にを呼び出しますsend()
于 2013-07-24T00:33:40.430 に答える
0

興味深い問題です。おそらく、各クライアントの書き込み後に OutputStream をフラッシュしてみてください。

于 2013-07-23T07:16:36.403 に答える