2012-06-27 5 views
9

Sto utilizzando DefaultHttpClient con un ThreadSafeClientConnManager su Android (2.3.x) per inviare richieste HTTP a un mio server REST (molo incorporato).Perché DefaultHttpClient invia dati su un socket semichiuso?

Dopo circa 200 secondi di inattività, il server chiude la connessione TCP con un [FIN]. Il client Android risponde con un [ACK]. Questo dovrebbe lasciare il socket in uno stato semichiuso (il server è ancora in ascolto, ma non può inviare dati).

Mi aspetto che quando il client tenta di utilizzare nuovamente quella connessione (tramite HttpClient.execute), DefaultHttpClient rileverà lo stato semichiuso, chiuderà il socket sul lato client (inviando quindi [FIN/ACK] per finalizzare il chiudi) e apri una nuova connessione per la richiesta. Ma c'è il problema.

Invece, invia la nuova richiesta HTTP sul socket semichiuso. Solo dopo l'invio viene rilevato lo stato semichiuso e il socket viene chiuso sul lato client (con [FIN] inviato al server). Naturalmente, il server non può rispondere alla richiesta (aveva già inviato il suo [FIN]), quindi il client pensa che la richiesta non sia andata a buon fine e riprova automaticamente tramite un nuovo socket/connessione.

Il risultato finale è che il server vede ed elabora due copie della richiesta.

Qualche idea su come risolvere questo problema? (Il mio server fa la cosa corretta con la seconda copia, ma sono infastidito dal fatto che il payload sia trasmesso due volte.)

Se DefaultHttpClient non rileva che il socket è stato chiuso quando tenta di scrivere il nuovo pacchetto HTTP, chiudere immediatamente quella presa e avviarne una nuova? Sono sconcertato su come una nuova richiesta HTTP viene inviata su un socket pochi minuti dopo che il server ha inviato un [FIN].

+0

'AndroidHttpClient' fa esattamente lo stesso comportamento (vale a dire un' DefaultHttpClient' con un 'ThreadSafeClientConnManager' per garantire la sicurezza dei thread) ... forse si può provare a utilizzare questo? Non so molto dei dettagli su come funzionano le connessioni socket, ma solo un suggerimento ... –

risposta

5

Questa è una limitazione generale dell'I/O di blocco in Java. Semplicemente non c'è modo di scoprire se l'endpoint opposto abbia una connessione chiusa diversa dal tentativo di leggere dalla presa. Apache HttpClient risolve questo problema utilizzando il controllo della connessione così obsoleto che è essenzialmente un'operazione di lettura molto breve. Tuttavia, il controllo può e spesso è disabilitato. In realtà è spesso consigliabile disabilitarla a causa della latenza extra introdotta dal controllo. Non ho idea di come si comporti esattamente la versione di HttpClient fornita con Android, ma potresti provare esplicitamente a abilitare il controllo utilizzando un parametro di configurazione appropriato.

Una soluzione migliore a questo problema potrebbe essere lo sfratto delle connessioni dal pool di connessioni che sono rimaste inattive in un determinato periodo di tempo (diciamo 150 secondi) dopo un periodo di inattività.

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e652

+0

Grazie. Ho attivato la verifica della connessione stantia, ma il controllo non viene ancora eseguito su Android. Viene eseguito con un jar HttpClient autonomo, vaniglia. Forse il team di Android si è davvero disorientato con gli innards HttpClient quando li hanno importati nei repository Android. Penso che prenderò il suggerimento di thread di sfratto di connessione inattiva. –

+1

@David B. Ciò che Google fornisce con Adnroid è un fork basato su una versione estremamente obsoleta (pre BETA 4.0) di Apache HttpClient. È plausibile che i tecnici di Google abbiano rimosso il controllo della connessione obsoleta. – oleg