Java ソケットと切断された接続
-
19-09-2019 - |
質問
ソケットがドロップされたかどうかを検出する最も適切な方法は何ですか?それともパケットが実際に送信されたかどうか?
Apple ゲートウェイ (GitHub で入手可能)。クライアントはソケットを開いて各メッセージのバイナリ表現を送信する必要があります。しかし残念なことに、Apple は何の確認も返しません。接続を再利用して複数のメッセージを送信することもできます。単純な Java Socket 接続を使用しています。関連するコードは次のとおりです。
Socket socket = socket(); // returns an reused open socket, or a new one
socket.getOutputStream().write(m.marshall());
socket.getOutputStream().flush();
logger.debug("Message \"{}\" sent", m);
場合によっては、メッセージの送信中またはその直前に接続が切断される場合があります。 Socket.getOutputStream().write()
無事に終了しましたが。TCP ウィンドウがまだ使い果たされていないためだと思います。
パケットが実際にネットワークに入ったかどうかを確実に知る方法はありますか?次の 2 つの解決策を試してみました。
追加で挿入する
socket.getInputStream().read()
250ミリ秒のタイムアウトでの操作。これにより、接続が切断された場合は失敗する読み取り操作が強制されますが、それ以外の場合は 250 ミリ秒間ハングします。TCP 送信バッファ サイズを設定します (例:
Socket.setSendBufferSize()
) をメッセージのバイナリ サイズに変換します。
どちらの方法も機能しますが、サービスの品質が大幅に低下します。スループットは、100 メッセージ/秒から最大約 10 メッセージ/秒に増加します。
助言がありますか?
アップデート:
記載されている可能性を疑問視する複数の回答が求められます。私は、説明している動作の「単体」テストを作成しました。ユニットケースをチェックしてください。 要点 273786.
どちらの単体テストにもサーバーとクライアントの 2 つのスレッドがあります。クライアントがデータを送信している間、サーバーは IOException をスローせずに閉じます。主な方法は次のとおりです。
public static void main(String[] args) throws Throwable {
final int PORT = 8005;
final int FIRST_BUF_SIZE = 5;
final Throwable[] errors = new Throwable[1];
final Semaphore serverClosing = new Semaphore(0);
final Semaphore messageFlushed = new Semaphore(0);
class ServerThread extends Thread {
public void run() {
try {
ServerSocket ssocket = new ServerSocket(PORT);
Socket socket = ssocket.accept();
InputStream s = socket.getInputStream();
s.read(new byte[FIRST_BUF_SIZE]);
messageFlushed.acquire();
socket.close();
ssocket.close();
System.out.println("Closed socket");
serverClosing.release();
} catch (Throwable e) {
errors[0] = e;
}
}
}
class ClientThread extends Thread {
public void run() {
try {
Socket socket = new Socket("localhost", PORT);
OutputStream st = socket.getOutputStream();
st.write(new byte[FIRST_BUF_SIZE]);
st.flush();
messageFlushed.release();
serverClosing.acquire(1);
System.out.println("writing new packets");
// sending more packets while server already
// closed connection
st.write(32);
st.flush();
st.close();
System.out.println("Sent");
} catch (Throwable e) {
errors[0] = e;
}
}
}
Thread thread1 = new ServerThread();
Thread thread2 = new ClientThread();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
if (errors[0] != null)
throw errors[0];
System.out.println("Run without any errors");
}
[ちなみに、私は並行性テスト ライブラリも持っています。これを使用すると、セットアップが少し良くなり、明確になります。gist のサンプルもチェックしてください。
実行すると次の出力が得られます。
Closed socket
writing new packets
Finished writing
Run without any errors
解決
これはあなたに多くの助けになることはないが、あなたの提案されたソリューションの技術的に両方が正しくありません。 OutputStream.flush()および他のどんなAPIはあなたが必要なものをやろうとしていませんと考えることができます呼び出します。
パケットがピアによって受信されたかどうかを決定するための唯一のポータブルかつ信頼性の高い方法は、ピアからの確認を待つことです。この確認は、いずれかの実際の応答、または優雅なソケットにシャットダウンすることができます。物語の終わり - 実際には、このJavaではない特定がそこには他に方法はありません、そして - それは、基本的なネットワークプログラミングです。
。これは、永続的な接続ではない場合 - あなたはそれはあなたが(それらのいずれかがエラーを示す)すべてIOExceptionsをキャッチで行う方法と、あなたは優雅なソケットのシャットダウンを実行する - あなただけの何かを送信し、接続を閉じる場合には、あるが、 :
1. socket.shutdownOutput();
2. wait for inputStream.read() to return -1, indicating the peer has also shutdown its socket
他のヒント
は、切断された接続を持つ多くの問題の後、私は<のhref =「http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html」RELに私のコードを移動しました=「nofollowをnoreferrer」>ほとんどあなたはこのように見えるためにあなたのパッケージを変更する意味拡張フォーマットを、使用します:
この方法Appleは、エラーが発生した場合、接続をドロップしませんが、ソケットにフィードバック・コードを記述します。
あなたは確認応答を受信する必要があります。しかし、あなたは次のように述べています。
Appleはいずれも返しません 全く確認
あなたはこれとはどういう意味ですか? TCP / IPしたがって、受信機が受信を確認しなければならないの配信を保証します。配信が行われるとき、それはしかし、保証するものではありません。
あなたはアップルに通知を送信し、あなたが成功したかではないので、あなたは、単にそれを再度送信しなければならないかどうかを指示する方法はありませんACKを受信する前に、あなたの接続を切断した場合。二回同じ情報をプッシュすることは問題であるか、デバイスが適切に処理されない場合は、問題があります。解決策は、重複プッシュ通知のデバイスの取り扱いを修正することです:あなたは押し側にできることは何もありません。
。@comment明確化/質問
[OK]をクリックします。あなたが理解の最初の部分は、第二部へのあなたの答えです。 ACKを受け取った場合のみパケットが正しく送信され、受信されています。私たちは、個々のパケット自分自身を追跡するいくつかの非常に複雑なスキームを考えることができ確信しているが、TCPは、この層を抽象化すると仮定して、あなたのためにそれを扱います。あなたの側で、あなたは、単に(これらのいずれかが、例外が発生した発生した場合、Javaで)発生する可能性の障害の多くに対処する必要があります。例外がない場合は、単に送信しようとしたデータは、TCP / IPプロトコルによって保証送信されます。
データは、一見「送られ」が、例外が発生していない場所を受信することが保証されていない状況がありますか?答えはノーである必要があります。
@Examples
ニースの例は、これはかなりのものを明確にしています。私は、エラーがスローされるだろうと思っているだろう。例では、エラーが2回目の書き込みにスローされる投稿が、最初ではありません。これはおもしろい動作です...と私はそれがこのように振る舞う理由を説明する多くの情報を見つけることができませんでした。私たちは配達を確認するために、当社独自のアプリケーションレベルのプロトコルを開発しなければならない理由は、しかし、説明しない。
あなたが確認のためのプロトコルせずにAppleデバイスが通知を受信する保証は自分ではないことを正しいように見えます。 Appleはまた、唯一のキューの最後のメッセージです。私はこのサービスを決定することができたサービスで少し見ると、顧客の利便性のためのより多くのですが、サービスを保証するために使用することはできませんし、他の方法と組み合わせる必要があります。私は、次のソースからこれを読みます。
<のhref = "http://blog.boxedice.com/2009/07/10/how-to-build-an-apple-push-notification-provider-server-tutorial/" のrel = "nofollowをnoreferrer 「> http://blog.boxedice.com/2009/07/10/how-to-build-an-apple-push-notification-provider-server-tutorial/ の
答えは何であなたは確実に伝えることができるかどうかではないように思えます。あなたはそれが送信された場合伝えるためにWiresharkのようにパケットスニファを使用することができるかもしれないが、これはまだそれを受信し、サービスの性質により、デバイスに送信された保証するものではありません。