ソケットがドロップされたかどうかを検出する最も適切な方法は何ですか? または、パケットが実際に送信されたかどうか?
Apple ゲートウェイ ( GitHub で入手可能)を介して Apple プッシュ通知を iPhone に送信するためのライブラリがあります。クライアントはソケットを開き、各メッセージのバイナリ表現を送信する必要があります。残念ながら、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()
) をメッセージ バイナリ サイズに設定します。
どちらの方法も機能しますが、サービスの品質が大幅に低下します。スループットは 1 秒あたり 100 メッセージから、最大で 1 秒あたり約 10 メッセージになります。
助言がありますか?
アップデート:
説明の可能性を疑問視する複数の回答に挑戦。私が説明している動作の「単体」テストを作成しました。Gist 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