2012-09-28 5 views
12

Stavo testando un HTTP servlet implementation (kindly shared by BalusC) che supporta HTTP byte range requests.Comportamento del client del protocollo dell'intervallo di byte HTTP su iPad/iPhone

Ho trovato alcune differenze particolari tra diversi client HTTP e mi chiedevo se non mi mancasse nulla. Ho usato un file video> 2G mp4 per i miei test e stavo catturando pacchetti con Wireshark. Questo è più o meno ciò che accade:

  • Samsung Galaxy SII:

    • richiesta HTTP GET per il file viene chiedendo intervallo di byte [0; <almost the end of the file>]
    • server risponde, dello streaming il file
    • ogni successivo chunk viene servito entro i limiti della stessa risposta HTTP. Nessuna nuova richiesta HTTP viene inviata (a meno che il video non venga inoltrato rapidamente a una determinata posizione). Codice Streaming pezzo di questo è abbastanza semplice, si legge RandomAccessFile input e scrive OutputStream output via byte[] buffer:

      while ((read = input.read(buffer)) > 0) { 
          output.write(buffer, 0, read); 
      } 
      
  • iPad 1
    • richiesta HTTP GET per il file viene chiedendo intervallo di byte [0; <almost the end of the file>]
    • server risponde, avvia lo streaming del file
    • L'iPad riceve un blocco o due e quindi decide unilateralmente di interrompere l'accettazione dei byte dal server ed emette un numero separata GET richiesta per il blocco successivo del file. Nuovi limiti di intervallo sono ad es. [100, almost the end of the file]. Il video è mostrato OK.
    • il ciclo si ripete nuovamente dal passaggio 2. Il limite sinistro si sposta sempre verso la fine del file.

Io non indagare su come esattamente la connessione viene terminata. Potrebbe essere che l'iPad interrompa l'invio di pacchetti TCP ACK, suppongo che questo non abbia molta importanza.

Il mio problema è che per ogni connessione terminata ottengo l'eccezione java.net.SocketException: Broken pipe. Ciò non solo inquina i registri (che è un problema minore/risolvibile) ma ritengo che ciò possa compromettere le prestazioni poiché l'aumento delle eccezioni è piuttosto costoso. Quando si guardava un video semplice, il tasso di eccezione era di circa 1 eccezione/sec, ma se il server aveva 100 utenti simultanei, la JVM probabilmente impiegava molto tempo a calcolare le tracce dello stack invece di fare il vero lavoro.

ho anche testato questo su iPhone utilizzando iOS 6 ed è stato in grado di osservare lo stesso comportamento di iPad 1. Giusto per ribadire, questa non accade su Samsung Android né browser desktop che ho provato, tra cui Safari su un Mac desktop.

Domande:

  • questo è un sapere bug/caratteristica di iPad/iPhone?
  • esiste una soluzione alternativa?
+0

Trovato [la stessa domanda posta da qualcun altro] (https://groups.google.com/forum/#!msg/youtube-api-gdata/V6RU2h9afBg/ibaJ7yOHjBAJ), così come [ un altro] (http://stackoverflow.com/questions/6094556/mobile-safari-makes-multiple-video-requests), purtroppo nessuna risposta utile finora :( – mindas

risposta

1

È necessario inviare un'intestazione Accept Ranges durante lo streaming su iPhone. Questo potrebbe causare il tuo problema. Guardate questo post

MP4 plays when accessed directly, but not when read through PHP, on iOS

+0

Grazie per la risposta. In effetti, sto inviando ' Accept-Ranges: intestazione bytes', altrimenti il ​​video non sarebbe riproducibile affatto Il posizionamento arbitrario del flusso funziona anche, l'unico problema che ho sono connessioni terminate – mindas

+0

Non so, ho usato la soluzione in quel collegamento e funziona Che ne dici dell'intestazione di Connection Keep-Alive? Basta fare ricerche su Google, sembra un problema comune che potrebbe non essere correlato a iOS: http://mikeschubert.com/2006/08/03/javanetsocketex/ –

+0

Soluzione funziona, ma voglio capire perché l'iPad/iPhone si comporta diversamente da Android e desktop? Se si trattava di un problema di database, avrebbe anche fallito altrove, ma ora non funziona solo per i dispositivi mobili Apple. – mindas

1

Prima di tutto,

spesa un sacco di tempo proprio stack di calcolo ripercorre

non sta andando per il calcolo se non si è chiamata andando ottenere/printStackTrace(). spegni il log o prendilo ed evitalo da qualche parte.

Ho avuto gli stessi problemi, non è andato via neanche per me. Bene, quelle sono scelte davvero stupide da fare ma puoi usare un load balancer che accetta le connessioni e reindirizza il tuo server tomcat o glassfish qualunque cosa tu stia utilizzando. ho osservato questa mancanza di comportamento dei tubi interrotti quando ho iniziato a utilizzare ELB su AWS. NGINX o Apache potrebbero fare alcune comunicazioni di frontiera per te.

Dico questo perché anche il sistema operativo potrebbe essere il motivo per cui JVM non riceve uno shutdown corretto delle comunicazioni TCP a causa dell'implementazione JVM sul sistema operativo.

+2

Anche se non è possibile calcolare i simboli, le informazioni necessarie per calcolare la traccia dello stack devono essere salvate prima che lo stack venga sovrascritto; Ho visto il codice sovrascrivere 'Throwable.fillInStackTrace()' per evitare questo overhead (il codice è stato infine riscritto per non generare un'eccezione su ogni riga dell'immagine). –

3

IIRC, "tubo rotto" significa semplicemente che l'altro lato ha ricevuto i dati dopo aver chiuso la fine di lettura.

La cosa più ragionevole che posso pensare è che è un tentativo di non sprecare grandi quantità di larghezza di banda video scaricando che non viene mai guardato (forse qualcosa che hanno concordato con i vettori, che ho il sospetto è il ragionamento alla base della "live streaming" restriction:

"Video streaming di contenuti su una rete cellulare più di 10 minuti devono utilizzare HTTP live Streaming e comprendono una linea di base 64 kbps solo audio HTTP streaming in diretta."

L'unico altro modo semplice a manetta il download è di interrompere read() e attendi che la finestra di ricezione si riempia, ma ciò non è sempre facile da fare (NSURLConnection non rende questo facile, ad esempio).

Se si è eccezionalmente fortunati, il client chiuderà la fine della scrittura (tale che il server sarà read() EOF) e attenderà un po 'di tempo prima di chiudere la fine di lettura. In questo caso, è possibile che presuma che il client non desideri più il resto del download. RFC 2616 è un po 'confuso (sembra dimenticare che le prese possono essere chiuse solo in una direzione), ma menziona "chiusura graziosa" (che according to Microsoft implica la chiusura del lato scrittura e la lettura finale dal lato di lettura fino a un timeout), ma dice anche

Server NON DEVE chiudere una connessione nel mezzo della trasmissione di una risposta, a meno che non si sospetti un errore di rete o client.

Quindi, se si sa è un iDevice e si legge EOF, allora potrebbe essere sicuro per il server per chiudere la presa, a condizione che avete accuratamente testato che non si rompe nulla — cambiando Il comportamento HTTP a seconda di User-Agent sembra una pessima idea.

In alternativa, non importa. Puoi fare U-A sniffing e ignorare l'eccezione se si tratta di un iDevice (che sembra meno terribile rispetto al comportamento HTTP). L'overhead di eccezione è quasi certamente trascurabile e probabilmente molto inferiore al sovraccarico di stampa su un registro. 100 eccezioni al secondo non sono nulla. Guardalo se non sei sicuro.

Si potrebbe anche file a bug with Apple, ma come vanno queste cose, non è particularly dubious network behaviour.

+0

Non credo di essere d'accordo con il punto su "sprecare grandi quantità di larghezza di banda"; il client può semplicemente ritardare gli ACK TCP in modo da ottenere il blocco successivo in seguito. E il minimo che può fare è chiudere con grazia la connessione. Comunque, grazie per la tua risposta - è il migliore che ho. Ti ho assegnato i punti ma terrò aperta la domanda nel caso in cui qualcuno fornisca una spiegazione migliore sul perché il comportamento dell'iPad/iPhone sia rotto (se lo è). – mindas

+0

@mindas Il client * potrebbe * ritardare gli ACK, ma il server continuerà a inviare pacchetti finché la finestra di invio non si riempie, e ancora, questo non è facile da eseguire con 'NSURLConnection'. E ancora, il cliente * potrebbe * tentare una "chiusura gentile" ma un timeout; senza ulteriori informazioni è difficile da dire. –

1

Tornando alla nostra ricerca condivisa. Dai uno sguardo allo this discussion sul sito Web di Apple. Sembra che ora questo problema abbia causato problemi con iOS6 che consuma troppi dati durante lo streaming.

(in lingua olandese, ma esattamente lo stesso problema segnalato here. Android facendo 1 richiesta, iOS fare richieste multiple)

tempo di ri-test di questa roba con iOS6.0.1 per vedere se effettivamente fissato la richiesta di gamma i problemi.

Appena testato sul mio iPod Touch 5th Gen - iOS6.0.1: la richiesta di intervallo richiede ancora 0-1, quindi poche volte il file 0-pieno seguito da intervalli più piccoli. Tuttavia sembra ancora disordinato

+0

Grazie per aver condiviso questo. Da quello che ho capito, sembra che abbiano riconosciuto questo come un problema su una delle app (podcast?) Ma non sul browser. Ad ogni modo, ti preghiamo di tenerci pubblicati se trovi qualcosa. Ho già rinunciato a questo problema ;-( – mindas