Prawidłowe zamykanie WebSocket (HTML5, Javascript)

127

Bawię się z HTML5 WebSockets. Zastanawiałem się, jak z wdziękiem zamknąć połączenie? Na przykład, co się stanie, jeśli użytkownik odświeży stronę lub po prostu zamknie przeglądarkę?

Występuje dziwne zachowanie, gdy użytkownik po prostu odświeża stronę bez wywoływania websocket.close()- kiedy wróci po odświeżeniu, trafi na websocket.onclosezdarzenie.

Andy Hin
źródło

Odpowiedzi:

110

Zgodnie ze specyfikacją protokołu v76 (która jest wersją przeglądarki z obecną obsługą implementacji):

Aby czysto zamknąć połączenie, ramka składająca się tylko z bajtu 0xFF, po którym następuje bajt 0x00, jest wysyłana od jednego peera z prośbą o zamknięcie połączenia przez drugiego peera.

Jeśli piszesz na serwerze, powinieneś upewnić się, że wysyłasz bliską ramkę, gdy serwer zamyka połączenie klienta. Normalna metoda zamykania gniazda TCP może czasami być powolna i powodować, że aplikacje myślą, że połączenie jest nadal otwarte, nawet jeśli nie jest.

Przeglądarka powinna naprawdę zrobić to za Ciebie po zamknięciu lub ponownym załadowaniu strony. Możesz jednak upewnić się, że zostanie wysłana zamknięta ramka, przechwytując zdarzenie beforeunload:

window.onbeforeunload = function() {
    websocket.onclose = function () {}; // disable onclose handler first
    websocket.close();
};

Nie wiem, jak można uzyskać zdarzenie onclose po odświeżeniu strony. Obiekt websocket (z obsługą onclose) nie będzie już istniał po ponownym załadowaniu strony. Jeśli od razu próbujesz nawiązać połączenie WebSocket na swojej stronie podczas ładowania strony, możesz napotkać problem polegający na tym, że serwer odmawia nowego połączenia tak szybko, jak stary się rozłączył (lub przeglądarka nie jest gotowa aby nawiązać połączenia w punkcie, w którym próbujesz się połączyć) i otrzymujesz zdarzenie onclose dla nowego obiektu websocket.

kanaka
źródło
2
Możliwe, że w przeglądarce Firefox połączenie wydaje się zawieszać podczas ładowania następnej strony. Nie mogę znaleźć odniesienia, ale myślę, że mógł być w tym błąd. Inną możliwością jest to, że onclosezdarzenie jest wywoływane nieoczekiwanie lub celowo, gdy użytkownik nawiguje / strona jest ponownie ładowana. Mam napisali pytanie pytaniem, co oczekiwane zachowanie powinno być, co przeglądarka ma rację i jak wdrożyć automatycznie połączy.
leggetter
4
rozważ te problemy z onbeforeunloadwydarzeniem
artkoenig
35

Chodzi o to, że obecnie używane są 2 główne wersje protokołu WebSockets. Stara wersja korzystająca z [0x00][message][0xFF]protokołu, a także nowa wersja korzystająca z pakietów sformatowanych w Hybi .

Stara wersja protokołu jest używana przez Operę i iPoda / iPada / iPhony, więc tak naprawdę ważne jest, aby na serwerach WebSockets zaimplementowano kompatybilność wsteczną. W przypadku tych przeglądarek korzystających ze starego protokołu odkryłem, że odświeżenie strony, opuszczenie strony lub zamknięcie przeglądarki powoduje automatyczne zamknięcie połączenia przez przeglądarkę. Wspaniały!!

Jednak w przypadku przeglądarek korzystających z nowej wersji protokołu (np. Firefox, Chrome i ewentualnie IE10), dopiero zamknięcie przeglądarki spowoduje automatyczne zamknięcie połączenia. Oznacza to, że jeśli odświeżysz stronę lub opuścisz stronę, przeglądarka NIE zamknie automatycznie połączenia. Jednak przeglądarka wysyła pakiet hybi do serwera z pierwszym bajtem (proto ident) 0x88(lepiej znanym jako zamknięta ramka danych). Gdy serwer odbierze ten pakiet, może wymusić samo zamknięcie połączenia, jeśli tak zdecydujesz.

theoobe
źródło
4
praktyczny przykład po stronie serwera, jak zarządzać tym pakietem hybi?
albanx
9
Wydaje się szalone, że nie zamyka połączenia. Zmienna WebSocket jest usuwana podczas ponownego ładowania strony, więc dlaczego połączenie miałoby pozostać otwarte, jeśli nie można uzyskać do niego dostępu? Ponowne użycie połączenia również nie miałoby sensu.
Triynko
@albanx sprawdź moją odpowiedź poniżej
artkoenig
Wszelkie przykłady kodu dotyczące sposobu wysyłania ramek zamykających z klienta sieci Web (np. Przeglądarki). Używam modułu ws npm
Shaik Syed Ali
3

Jak wspomniał theoobe , niektóre przeglądarki nie zamykają gniazd sieciowych automatycznie. Nie próbuj obsługiwać żadnych zdarzeń „zamknięcia okna przeglądarki” po stronie klienta. Obecnie nie ma niezawodnego sposobu, aby to zrobić, jeśli weźmiesz pod uwagę obsługę głównych przeglądarek stacjonarnych i mobilnych (np. onbeforeunloadNie będzie działać w Mobile Safari). Miałem dobre doświadczenia z obsługą tego problemu po stronie serwera. Np. Jeśli używasz Java EE, spójrz na javax.websocket.Endpoint , w zależności od przeglądarki OnClosemetoda lub OnErrormetoda zostanie wywołana po zamknięciu / przeładowaniu okna przeglądarki.

artkoenig
źródło
1
W moim przypadku Połączenie zamyka się podczas przesyłania danych, działa dobrze w przypadku przeglądarek z rodziny Opera i iOS. Proszę, pomóż mi… Walczę z tym problemem od dwóch tygodni. stackoverflow.com/q/30799814/2225439
Mrug
2

Używając zamkniętej metody gniazda sieciowego, możesz napisać dowolną funkcję zgodnie z wymaganiami.

var connection = new WebSocket('ws://127.0.0.1:1337');
    connection.onclose = () => {
            console.log('Web Socket Connection Closed');
        };
Akanksha Raizada
źródło