Mam pewne problemy z interfejsem API gniazd Java. Próbuję wyświetlić liczbę graczy aktualnie połączonych z moją grą. Łatwo jest określić, kiedy gracz się połączył. Jednak określenie, kiedy gracz rozłączył się za pomocą funkcji socket API, wydaje się niepotrzebnie trudne.
Dzwonienie isConnected()
do gniazda, które zostało odłączone zdalnie, zawsze wydaje się powracać true
. Podobnie, wywołanie isClosed()
gniazda, które zostało zdalnie zamknięte, zawsze wydaje się powracać false
. Przeczytałem, że aby faktycznie określić, czy gniazdo zostało zamknięte, czy nie, dane muszą zostać zapisane w strumieniu wyjściowym i wychwycony wyjątek. Wydaje się, że to naprawdę nieczysty sposób radzenia sobie z tą sytuacją. Musielibyśmy po prostu stale spamować wiadomością śmieciową w sieci, aby kiedykolwiek wiedzieć, kiedy gniazdo zostało zamknięte.
Czy jest jakieś inne rozwiązanie?
źródło
0
zamiast tego zwraca-1
.W różnych protokołach komunikacyjnych ogólną praktyką jest utrzymywanie pulsu (wysyłanie pakietów ping), pakiet nie musi być bardzo duży. Mechanizm sondujący pozwoli ci wykryć rozłączonego klienta nawet zanim TCP ogólnie to wykryje (limit czasu TCP jest znacznie wyższy) Wyślij sondę i poczekaj powiedzmy 5 sekund na odpowiedź, jeśli nie widzisz odpowiedzi, powiedzmy 2-3 kolejne próby, odtwarzacz zostanie rozłączony.
Również powiązane pytanie
źródło
Widzę, że inna odpowiedź właśnie została opublikowana, ale myślę, że jesteś interaktywny z klientami grającymi w twoją grę, więc mogę przedstawić inne podejście (podczas gdy BufferedReader jest zdecydowanie poprawny w niektórych przypadkach).
Gdybyś chciał… mógłbyś delegować odpowiedzialność za „rejestrację” na klienta. Oznacza to, że miałbyś kolekcję połączonych użytkowników z sygnaturą czasową ostatniej wiadomości otrzymanej od każdego z nich ... jeśli klient przekroczy limit czasu, wymusisz ponowną rejestrację klienta, ale to prowadzi do cytatu i pomysłu poniżej.
Jeśli twój kod Java nie zamknął / nie rozłączył Socket, to w jaki inny sposób zostałbyś powiadomiony, że zdalny host zamknął twoje połączenie? Ostatecznie twoja próba / złapanie robi mniej więcej to samo, co robiłby ankieter nasłuchujący zdarzeń na gnieździe ACTUAL. Rozważ następujące:
Myślę, że jedną z cech abstrakcyjnych języków jest to, że jesteś oderwany od drobiazgów. Pomyśl o słowie kluczowym using w C # (spróbuj / w końcu) dla SqlConnection s lub czegokolwiek ... to tylko koszt prowadzenia biznesu ... Myślę, że try / catch / w końcu jest akceptowanym i niezbędnym wzorcem do użycia Socket.
źródło
Myślę, że taka jest natura połączeń TCP, w tych standardach potrzeba około 6 minut ciszy podczas transmisji, zanim stwierdzimy, że połączenie zostało przerwane! Więc nie sądzę, aby można było znaleźć dokładne rozwiązanie tego problemu. Może lepszym sposobem jest napisanie przydatnego kodu do odgadnięcia, kiedy serwer powinien założyć, że połączenie użytkownika jest zamknięte.
źródło
Jak mówi @ user207421, nie ma możliwości poznania aktualnego stanu połączenia ze względu na model architektury protokołu TCP / IP. Serwer musi więc Cię zauważyć przed zamknięciem połączenia lub sam to sprawdzisz.
Oto prosty przykład, który pokazuje, jak sprawdzić, czy gniazdo jest zamknięte przez serwer:
sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT); socket = new Socket(); timeout = 5000; socket.connect(sockAdr, timeout); reader = new BufferedReader(new InputStreamReader(socket.getInputStream()); while ((data = reader.readLine())!=null) log.e(TAG, "received -> " + data); log.e(TAG, "Socket closed !");
źródło
Miałem podobny problem. W moim przypadku klient musi okresowo przesyłać dane. Mam nadzieję, że masz takie same wymagania. Następnie ustawiam SO_TIMEOUT,
socket.setSoTimeout(1000 * 60 * 5);
który jest rzucany,java.net.SocketTimeoutException
gdy upłynie określony czas. Wtedy mogę łatwo wykryć martwego klienta.źródło
Tak sobie z tym radzę
while(true) { if((receiveMessage = receiveRead.readLine()) != null ) { System.out.println("first message same :"+receiveMessage); System.out.println(receiveMessage); } else if(receiveRead.readLine()==null) { System.out.println("Client has disconected: "+sock.isClosed()); System.exit(1); } }
jeśli wynik.code == null
źródło
readLine()
dwa razy. Wiesz już, że welse
bloku było to zerowe .W Linuksie, kiedy zapisujesz () w gnieździe, którego druga strona, nieznana Tobie, zamknęła, sprowokuje sygnał / wyjątek SIGPIPE, jakkolwiek chcesz to wywołać. Jeśli jednak nie chcesz dać się złapać SIGPIPE, możesz użyć funkcji send () z flagą MSG_NOSIGNAL. Wywołanie send () zwróci wartość -1 iw tym przypadku możesz sprawdzić errno, które powie ci, że próbowałeś napisać przerwany potok (w tym przypadku gniazdo) o wartości EPIPE, która zgodnie z errno.h jest równoważna 32. W reakcji na EPIPE możesz cofnąć się i spróbować ponownie otworzyć gniazdo i spróbować ponownie wysłać informacje.
źródło
send()
Wywołanie zwróci -1 tylko wtedy, gdy dane wychodzące ale buforowane przez wystarczająco długi dla timerów Prześlij wygasa. Prawie na pewno nie nastąpi to przy pierwszym wysyłaniu po rozłączeniu, ze względu na buforowanie na obu końcach i asynchroniczny charaktersend()
pod maską.