Jaki jest najlepszy sposób wykrywania, czy gniazdo zostało upuszczone, czy nie? Albo czy pakiet rzeczywiście został wysłany?Gniazda Java i odłączone połączenia
Mam bibliotekę do wysyłania powiadomień Apple Push na iPhone przez Apple Gatways (available on GitHub). Klienci muszą otworzyć gniazdo i wysłać binarną reprezentację każdej wiadomości; ale niestety Apple nie zwraca żadnego potwierdzenia. Połączenie może być ponownie użyte do wysyłania wielu wiadomości. Używam prostych połączeń Java Socket. Odpowiedni kod to:
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);
W niektórych przypadkach, gdy połączenie zostanie zerwane podczas wysyłania wiadomości lub wcześniej; Socket.getOutputStream().write()
kończy się pomyślnie. Spodziewam się, że jest to spowodowane tym, że okno TCP nie jest jeszcze wyczerpane.
Czy jest jakiś sposób, aby stwierdzić na pewno, czy pakiet rzeczywiście trafił do sieci, czy nie? I eksperymentowali z dwóch następujących rozwiązań:
umieścić dodatkowy
socket.getInputStream().read()
operację na 250 ms czas oczekiwania. Wymusza to operację odczytu, która kończy się niepowodzeniem, gdy połączenie zostało przerwane, ale zawiesza się w inny sposób na 250ms.ustawić rozmiar bufora wysyłania TCP (na przykład
Socket.setSendBufferSize()
) na rozmiar binarny komunikatu.
Obie metody działają, ale znacznie obniżają jakość usługi; przepustowość dochodzi ze 100 komunikatów na sekundę do około 10 wiadomości na sekundę.
Wszelkie sugestie?
UPDATE:
kwestionowane przez wielu odpowiedziach kwestionujących możliwość opisany. Skonstruowałem testy "jednostkowe" zachowania, które opisuję. Sprawdź skrzynki jednostkowe pod numerem Gist 273786.
Oba testy jednostkowe mają dwa wątki, serwer i klienta. Serwer jest zamykany, gdy klient wysyła mimo to dane bez wywołania IOException. Oto główne metody:
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");
}
[Nawiasem mówiąc, ja też mam bibliotekę testów współbieżności, który sprawia, że konfiguracja nieco lepiej i wyraźniej. Sprawdź również próbkę w istocie].
Po uruchomieniu pojawia się następujący komunikat:
Zaktualizowana odpowiedź. –
Jak już stwierdziłem w mojej odpowiedzi, jeśli chcesz mieć pewność, że nie wystąpił błąd, musisz wykonać pełne wdzięku zakończenie połączenia. Wykonaj shutdownOutput(), a następnie przeczytaj, aż otrzymasz EOF, a następnie zamknij gniazdo. Łagodne zakończenie połączenia jest w istocie otrzymaniem potwierdzenia od rówieśnika, że otrzymał on OK, co jest dokładnie tym, czego chcesz. –