Pytanie Zachowanie klienta protokołu HTTP na podstawie protokołu bajtowego na iPadzie / iPhonie


Testowałem Implementacja serwletów HTTP (uprzejmie udostępniana przez BalusC) który wspiera Żądania zakresu bajtów HTTP.

Znalazłem kilka osobliwych różnic między różnymi klientami HTTP i zastanawiałem się, czy niczego nie brakuje. Do moich testów użyłem pliku wideo> 2G mp4 i przechwytywałem pakiety za pomocą Wiresharka. Z grubsza to się dzieje:

  • Samsung Galaxy SII:

    • Żądanie HTTP GET dla pliku przychodzi, pytając o zakres bajtów [0; <almost the end of the file>]
    • serwer odpowiada, rozpoczyna przesyłanie strumieniowe pliku
    • każda kolejna porcja jest podawana w granicach ta sama odpowiedź HTTP. Żadne nowe żądanie HTTP nie zostanie wysłane (chyba że film zostanie szybko przekierowany na określoną pozycję). Streaming fragment kodu w tym jest dość prosty, czyta RandomAccessFile input i pisze do OutputStream output przez byte[] buffer:

      while ((read = input.read(buffer)) > 0) {
          output.write(buffer, 0, read);
      }
      
  • iPad 1
    • Żądanie HTTP GET dla pliku przychodzi, pytając o zakres bajtów [0; <almost the end of the file>]
    • serwer odpowiada, rozpoczyna przesyłanie strumieniowe pliku
    • iPad pobiera porcję lub dwie, a następnie jednostronnie decyduje się przestać akceptować bajty z serwera i wydaje a oddzielny GET żądanie dla następnej porcji pliku. Nowe granice zasięgu to np. [100, almost the end of the file]. Film jest wyświetlany prawidłowo.
    • cykl powtarza się ponownie od kroku 2. Lewa granica zawsze przesuwa się w kierunku końca pliku.

Nie sprawdzałem, jak dokładnie połączenie zostało zerwane. Możliwe, że iPad przestaje wysyłać pakiety TCP ACK, przypuszczam, że to nie ma większego znaczenia.

Mój problem polega na tym, że otrzymuję każde zakończone połączenie java.net.SocketException: Broken pipe wyjątek. To nie tylko zanieczyszcza dzienniki (co jest problemem pomniejszym / rozwiązującym), ale uważam, że może to zaszkodzić wydajności, ponieważ podnoszenie wyjątków jest dość kosztowne. Podczas oglądania prostego wideo współczynnik wyjątków wynosił około 1 wyjątku na sekundę, ale jeśli serwer miał 100 równoczesnych użytkowników, wówczas JVM prawdopodobnie spędzałby mnóstwo czasu tylko obliczając ślady stosu, zamiast wykonywać prawdziwą pracę.

Przetestowałem to również na iPhonie przy użyciu iOS 6 i mogłem obserwować to samo zachowanie co iPad 1. Tylko powtórzyć to nie wydarzyło sie na Samsunga Samsunga i innych przeglądarkach komputerowych, które wypróbowałem, w tym Safari na komputerowym Macu.

Pytania:

  • czy to znany błąd / funkcja iPada / iPhone'a?
  • czy istnieje obejście tego problemu?

12
2017-09-28 09:55


pochodzenie


Uznany to samo pytanie zadane przez kogoś innego, jak również inny, niestety brak przydatnych odpowiedzi do tej pory :( - mindas


Odpowiedzi:


IIRC, "potłuczona rura" oznacza po prostu, że druga strona otrzymała dane po zamknięciu jej końca odczytu.

Najrozsądniejszą rzeczą, jaką mogę sobie wyobrazić, jest to, że jest to próba nie marnowania dużych ilości pobierania pasma wideo, które nigdy nie są oglądane (być może coś, co zgodziło się z przewoźnikami, co, jak podejrzewam, jest uzasadnieniem Ograniczenie transmisji na żywo:

"Przesyłanie strumieniowe wideo przez sieć komórkową trwającą dłużej niż 10 minut musi korzystać z przesyłania strumieniowego na żywo przez protokół HTTP i zawierać podstawowy strumień HTTP Live o szybkości 64 kb / s."

Jedynym innym łatwym sposobem na dławienie pobierania jest zatrzymanie read()i czekać na wypełnienie okna odbioru, ale nie zawsze jest to łatwe (NSURLConnection tak naprawdę nie jest to łatwe, na przykład).

Jeśli masz wyjątkowe szczęście, klient zamknie swój koniec zapisu (tak, że będzie to serwer read() EOF) i poczekaj chwilę, zanim zamkniesz koniec odczytu. W tym przypadku to moc bezpiecznie założyć, że klient nie chce już reszty pobierania. RFC 2616 jest trochę mglisty (wydaje się zapominać, że gniazda można zamknąć tylko w jednym kierunku), ale wspomina o "pełnym gracji" (który według Microsoft obejmuje zamykanie strony zapisu i kończenie czytania od strony odczytu, aż upłynie limit czasu), ale także mówi

Serwery NIE POWINIEN zamykać połączenia w trakcie przesyłania odpowiedzi, chyba że podejrzewa się awarię sieci lub klienta.

Więc jeśli ty wiedzieć to urządzenie i czytasz EOF, wtedy możesz bezpiecznie zamknąć serwer, pod warunkiem, że masz dokładnie przetestowane że nic nie psuje - zmiana zachowania HTTP w zależności od User-Agenta wydaje się po prostu okropnym pomysłem.

Alternatywnie, nie przejmuj się. Możesz wykonać węszenie U-A i zignorować wyjątek, jeśli jest to iDevice (co wydaje się mniej przerażające niż zmiana zachowania HTTP). Obciążenie wyjątku jest prawie na pewno pomijalne i prawdopodobnie znacznie niższe niż narzut drukowania go do dziennika. 100 wyjątków, drugi to nic. Profiluj, jeśli nie masz pewności.

Możesz też zgłoś błąd w Apple, ale jak to się dzieje, to nie jest szczególnie wątpliwe zachowanie w sieci.


3
2017-10-09 00:29



Nie sądzę, że zgadzam się z twoją uwagą na temat "marnowania dużych ilości przepustowości"; klient może po prostu opóźnić ACK TCP, aby uzyskać następny fragment później. A przynajmniej może to zrobić, aby z wdziękiem zamknąć połączenie. W każdym razie, dziękuję za odpowiedź - to najlepsze, co mam. Przyznam Ci punkty, ale pozostawi to pytanie otwartym na wypadek, gdyby ktoś przedstawił lepsze wyjaśnienie, dlaczego zachowanie iPada / iPhone'a jest zepsute (jeśli tak). - mindas
@mindas Klient mógłby opóźnienia ACK, ale serwer będzie nadal wysyłał pakiety, dopóki okno wysyłania się nie zapełni, i znowu nie jest to łatwe do zrealizowania dzięki NSURLConnection. I znowu klient moc próba "pełnego gracji", ale czas oczekiwania; bez dodatkowych informacji trudno powiedzieć. - tc.


Musisz wysłać nagłówek Accept Ranges podczas przesyłania strumieniowego na iPhone'a. To może być przyczyną problemu. Spójrz na ten post

MP4 jest odtwarzany przy bezpośrednim dostępie, ale nie podczas czytania w PHP na iOS


1
2017-10-01 21:57



Dziękuję za odpowiedź. W rzeczywistości wysyłam Accept-Ranges: bytes nagłówek, w przeciwnym razie wideo nie byłoby w ogóle możliwe do odtworzenia. Działa również arbitralne pozycjonowanie strumienia, jedynym problemem, który mam, są zakończone połączenia. - mindas
Nie wiem, użyłem rozwiązania w tym łączu i to działa. Co z nagłówkiem Connection Keep Alive? Wystarczy google, wygląda na typowy problem, który może być niezwiązany z iOS: mikeschubert.com/2006/08/03/javanetsocketex - Ascii Dude
Rozwiązanie działa, ale chcę zrozumieć, dlaczego iPad / iPhone zachowuje się inaczej niż Android i komputer? Jeśli byłby to problem z bazą danych, nie udałoby się to również gdzie indziej, ale teraz kończy się niepowodzeniem w przypadku urządzeń mobilnych firmy Apple. - mindas
Czy to możliwe, że iOS nie zezwala na utrzymywanie połączeń tak długo, jak robi to większość przeglądarek? Postępowałbym zgodnie z tym pytaniem. Lub dodaj kod, aby zmusić połączenie do pozostania przy życiu, lub sprawdź, czy jest nadal otwarty, zanim zażąda następnego fragmentu. - Ascii Dude


Po pierwsze,

spędzanie mnóstwa czasu na obliczaniu śladów stosu

nie będzie obliczać, jeśli nie zadzwonisz get / printStackTrace (). wyłącz go z dziennika lub złap go i unikaj gdzieś.

Miałem te same problemy, nie dla mnie też. Cóż, to naprawdę głupie wybory, ale możesz użyć load balancera, który akceptuje połączenia i przekierowuje go na serwer tomcat lub glassfish, niezależnie od tego, czego używasz. zaobserwowałem ten brak zachowania połamanych przewodów, kiedy zacząłem używać ELB na AWS. NGINX lub Apache może dla ciebie zrobić jakąś komunikację graniczną.

Mówię to, ponieważ nawet system operacyjny może być powodem, dla którego JVM nie otrzymuje właściwego zamknięcia komunikacji TCP z powodu implementacji JVM w systemie operacyjnym.


1
2017-10-08 22:54



Chociaż może nie obliczyć symboli, informacje potrzebne do obliczenia śladu stosu muszą zostać zapisane przed nadpisaniem stosu; Widziałem nadpisanie kodu Throwable.fillInStackTrace() aby uniknąć tego narzutu (kod został w końcu przepisany, aby nie rzucał wyjątku na każdą linię obrazu). - tc.


Wracając do naszego wspólnego odkrycia. Spojrzeć na ta dyskusja na stronie Apple. Wygląda na to, że teraz ten problem powoduje problemy z korzystaniem z iOS6 za dużo danych podczas przesyłania strumieniowego.

(W języku holenderskim, ale dokładnie ten sam problem zgłoszony tutaj. Android robi jedno żądanie, iOS wykonuje wiele żądań)

Czas ponownie przetestować te rzeczy za pomocą iOS6.0.1, aby sprawdzić, czy rzeczywiście USTALIŁ problemy związane z żądaniem zakresu.

Właśnie testowałem na iPodzie Touch 5. generacji - iOS6.0.1: Żądanie zakresu wciąż żąda 0-1, potem kilka razy pełnego 0 pliku, a następnie mniejszych zakresów. Jednak nadal wygląda bałagan


1
2017-11-16 11:59



Dziękuję za udostępnienie tego. Z tego co zrozumiałem, wygląda na to, że uznali to za problem w jednej z aplikacji (podcast?), Ale nie w przeglądarce. W każdym razie, prosimy o wysyłanie nam wiadomości, jeśli coś znajdziesz. Już zrezygnowałem z tego wydania ;-( - mindas