Puoi spiegare il processo di connessione HttpURLConnection?


134

Sto usando HTTPURLConnectionper connettermi a un servizio web. So usareHTTPURLConnection ma voglio capire come funziona. Fondamentalmente, voglio sapere quanto segue:

  • Su quale punto fa HTTPURLConnection tenta di stabilire una connessione all'URL specificato?
  • Su quale punto posso sapere di essere riuscito a stabilire una connessione?
  • Lo stabilire una connessione e l'invio della richiesta effettiva viene effettuato in una fase / chiamata del metodo? Che metodo è?
  • Puoi spiegare la funzione di getOutputStreame getInputStreamin parole povere? Ho notato che quando il server a cui sto tentando di connettermi è inattivo, ottengo un Exceptionat getOutputStream. Significa che HTTPURLConnectioninizierà a stabilire una connessione solo quando invoco getOutputStream? Che ne dici di getInputStream? Dal momento che sono in grado di ottenere la risposta solo a getInputStream, significa che non ho ancora inviato alcuna richiesta getOutputStreamma semplicemente stabilisce una connessione? Fare HttpURLConnectiondi nuovo andare al server per richiesta di risposta quando invoco getInputStream?
  • Ho ragione a dire che openConnectioncrea semplicemente un nuovo oggetto connessione ma non stabilisce ancora alcuna connessione?
  • Come posso misurare l'overhead di lettura e collegarlo?

Risposte:


184
String message = URLEncoder.encode("my message", "UTF-8");

try {
    // instantiate the URL object with the target URL of the resource to
    // request
    URL url = new URL("http://www.example.com/comment");

    // instantiate the HttpURLConnection with the URL object - A new
    // connection is opened every time by calling the openConnection
    // method of the protocol handler for this URL.
    // 1. This is the point where the connection is opened.
    HttpURLConnection connection = (HttpURLConnection) url
            .openConnection();
    // set connection output to true
    connection.setDoOutput(true);
    // instead of a GET, we're going to send using method="POST"
    connection.setRequestMethod("POST");

    // instantiate OutputStreamWriter using the output stream, returned
    // from getOutputStream, that writes to this connection.
    // 2. This is the point where you'll know if the connection was
    // successfully established. If an I/O error occurs while creating
    // the output stream, you'll see an IOException.
    OutputStreamWriter writer = new OutputStreamWriter(
            connection.getOutputStream());

    // write data to the connection. This is data that you are sending
    // to the server
    // 3. No. Sending the data is conducted here. We established the
    // connection with getOutputStream
    writer.write("message=" + message);

    // Closes this output stream and releases any system resources
    // associated with this stream. At this point, we've sent all the
    // data. Only the outputStream is closed at this point, not the
    // actual connection
    writer.close();
    // if there is a response code AND that response code is 200 OK, do
    // stuff in the first if block
    if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
        // OK

        // otherwise, if any other status code is returned, or no status
        // code is returned, do stuff in the else block
    } else {
        // Server returned HTTP error code.
    }
} catch (MalformedURLException e) {
    // ...
} catch (IOException e) {
    // ...
}

Le prime 3 risposte alle tue domande sono elencate come commenti incorporati, accanto a ciascun metodo, nell'esempio HTTP POST sopra.

Da getOutputStream :

Restituisce un flusso di output che scrive su questa connessione.

Fondamentalmente, penso che tu abbia una buona comprensione di come funziona, quindi lasciatemi solo ripetere nei termini del profano. getOutputStreamfondamentalmente apre un flusso di connessione , con l'intenzione di scrivere dati sul server. Nell'esempio di codice sopra "messaggio" potrebbe essere un commento che stiamo inviando al server che rappresenta un commento lasciato su un post. Quando vedi getOutputStream, stai aprendo il flusso di connessione per la scrittura, ma in realtà non scrivi alcun dato finché non chiami writer.write("message=" + message);.

Da getInputStream () :

Restituisce un flusso di input che legge da questa connessione aperta. È possibile generare SocketTimeoutException durante la lettura dal flusso di input restituito se il timeout di lettura scade prima che i dati siano disponibili per la lettura.

getInputStreamfa il contrario. Ad esempio getOutputStream, apre anche un flusso di connessione , ma l'intento è leggere i dati dal server, non scrivere su di esso. Se la connessione o l'apertura del flusso falliscono, vedrai a SocketTimeoutException.

Che ne dici di getInputStream? Dato che sono in grado di ottenere la risposta solo su getInputStream, significa che non ho ancora inviato alcuna richiesta a getOutputStream ma che semplicemente stabilisce una connessione?

Tieni presente che l'invio di una richiesta e l'invio di dati sono due operazioni diverse. Quando si richiama getOutputStream o getInputStream url.openConnection() , si invia una richiesta al server per stabilire una connessione. Si verifica una stretta di mano in cui il server ti restituisce un riconoscimento dell'avvenuta connessione. È quindi in quel momento che sei pronto per inviare o ricevere dati. Pertanto, non è necessario chiamare getOutputStream per stabilire una connessione per aprire uno stream, a meno che lo scopo per effettuare la richiesta non sia inviare dati.

In parole povere, fare una getInputStreamrichiesta equivale a fare una telefonata a casa del tuo amico per dire "Ehi, va bene se vengo e prendo in prestito quella coppia di morsa?" e il tuo amico stabilisce la stretta di mano dicendo "Certo! Vieni a prenderla". Quindi, a quel punto, viene stabilita la connessione, cammini a casa del tuo amico, bussi alla porta, richiedi le morsa e tornerai a casa tua.

Usare un esempio simile getOutputStreamimplicherebbe chiamare il tuo amico e dire "Ehi, ho quei soldi che ti devo, posso inviarti"? Il tuo amico, bisognoso di soldi e malato dentro che l'hai tenuto per così tanto tempo, dice "Certo, vieni da te bastardo a buon mercato". Quindi vai a casa del tuo amico e "POST" i soldi per lui. Ti butta fuori e torni a casa tua.

Ora, proseguendo con l'esempio del profano, diamo un'occhiata ad alcune eccezioni. Se hai chiamato il tuo amico e lui non era a casa, potrebbe essere un errore 500. Se hai chiamato e hai ricevuto un messaggio di numero disconnesso perché il tuo amico è stanco di prendere in prestito denaro per tutto il tempo, questa è una pagina 404 non trovata. Se il tuo telefono è morto perché non hai pagato il conto, potrebbe trattarsi di un'eccezione IOException. (NOTA: questa sezione potrebbe non essere corretta al 100%. Ha lo scopo di darti un'idea generale di ciò che sta accadendo in termini di profani.)

Domanda n. 5:

Sì, hai ragione nel dire che openConnection crea semplicemente un nuovo oggetto connessione ma non lo stabilisce. La connessione viene stabilita quando si chiama getInputStream o getOutputStream.

openConnectioncrea un nuovo oggetto connessione. Da javadocs URL.openConnection :

Una nuova connessione viene aperta ogni volta chiamando il metodo openConnection del gestore del protocollo per questo URL.

La connessione viene stabilita quando si chiama openConnection e InputStream, OutputStream o entrambi vengono chiamati quando vengono istanziati.

Domanda n. 6 :

Per misurare l'overhead, generalmente avvolgo un codice di temporizzazione molto semplice attorno all'intero blocco di connessione, in questo modo:

long start = System.currentTimeMillis();
log.info("Time so far = " + new Long(System.currentTimeMillis() - start) );

// run the above example code here
log.info("Total time to send/receive data = " + new Long(System.currentTimeMillis() - start) );

Sono sicuro che esistono metodi più avanzati per misurare i tempi e le spese generali della richiesta, ma in genere questo è sufficiente per le mie esigenze.

Per informazioni sulla chiusura delle connessioni, di cui non hai chiesto informazioni, vedi In Java quando viene chiusa una connessione URL? .


Ciao. Grazie!!! Questa è stata davvero una spiegazione dettagliata e apprezzo molto la tua risposta. Se capisco correttamente la tua risposta, sia getOutputStream che getInputStream stabiliscono una connessione se non è stata ancora stabilita alcuna connessione. Se chiamo getOutputStream, quindi chiamo getInputStream, internamente, HTTPURLConnection non ripristinerà più una connessione su getInputStream poiché sono già riuscito a stabilirlo su getOutStream? HttpURLConnection riutilizzerà qualsiasi connessione che sono stato in grado di stabilire su getOutputStream in getInputStream.
Arci,

Cont .: O stabilisce una connessione nuova e separata per getOutputStream e getInputStream? Inoltre, se voglio ottenere l'overhead di connessione, il posto giusto per mettere il mio timer è prima e dopo getOutputStream. Se voglio ottenere il sovraccarico di lettura, il posto giusto per mettere il mio timer è prima e dopo getInputStream.
Arci,

Ricorda cosa dice javadoc su getInputStream e getOutputStream: Returns an output stream that writes to this connection.e Returns an input stream that reads from this open connection.. Outputstream e inputstream sono separati dalla connessione.
jmort253,

8
Vale la pena notare che sembra che l'oggetto HttpURLConnection raggiunga l'URL di destinazione solo nel momento in cui DEVE farlo. Nel tuo esempio, hai flussi di input e output, che ovviamente non possono fare nulla fino a quando la connessione non è aperta. Un caso molto più semplice è un'operazione GET, in cui non si fa altro che inizializzare la connessione e quindi controllare il codice di risposta. In tal caso, la connessione non viene effettivamente effettuata fino a quando non viene chiamato il metodo getResponseCode (). Altrimenti, questa è una grande spiegazione ed esplorazione del ciclo di vita della connessione!
Spanky Quigman,

1
Prima ero confuso tra l'istanza 'UrlConnection' e la connessione Tcp / Ip / SSL sottostante, 2 concetti separati. Il primo è fondamentalmente sinonimo di una singola richiesta di pagina HTTP. Quest'ultimo è qualcosa che si spera venga creato una sola volta se stai facendo richieste di più pagine allo stesso server.
Tim Cooper,


1

In quale punto HTTPURLConnection tenta di stabilire una connessione all'URL specificato?

Sulla porta indicata nell'URL, se presente, altrimenti 80 per HTTP e 443 per HTTPS. Credo che questo sia documentato.

Su quale punto posso sapere di essere riuscito a stabilire una connessione?

Quando si chiama getInputStream () o getOutputStream () o getResponseCode () senza ottenere un'eccezione.

Lo stabilire una connessione e l'invio della richiesta effettiva viene effettuato in una fase / chiamata del metodo? Che metodo è?

No e nessuno.

Puoi spiegare la funzione di getOutputStream e getInputStream in parole povere?

Ognuno di essi si collega prima se necessario, quindi restituisce il flusso richiesto.

Ho notato che quando il server a cui sto tentando di connettermi è inattivo, ottengo un'eccezione su getOutputStream. Significa che HTTPURLConnection inizierà a stabilire una connessione solo quando invoco getOutputStream? Che ne dici di getInputStream? Dato che sono in grado di ottenere la risposta solo su getInputStream, significa che non ho ancora inviato alcuna richiesta a getOutputStream ma che semplicemente stabilisce una connessione? HttpURLConnection torna al server per richiedere una risposta quando invoco getInputStream?

Vedi sopra.

Ho ragione a dire che openConnection crea semplicemente un nuovo oggetto connessione ma non stabilisce ancora alcuna connessione?

Sì.

Come posso misurare l'overhead di lettura e collegarlo?

Connetti: prenditi il ​​tempo che getInoutStream () o getOutputStream () impiega per tornare, qualunque sia la prima chiamata. Leggi: tempo trascorso dall'avvio della prima lettura all'ottenimento di EOS.


1
Penso che OP abbia significato quale punto di connessione è stato stabilito e in quale momento possiamo conoscere lo stato della connessione. L'URL della porta non si collega a. Immagino che questo sia stato diretto verso openConnection () e getInoutStream () / getOutputStream () / getResponseCode () la cui risposta è successiva.
Aniket Thakur,

1

In quale punto HTTPURLConnection tenta di stabilire una connessione all'URL specificato?

Vale la pena chiarire, c'è l' istanza 'UrlConnection' e poi c'è la connessione socket Tcp / Ip / SSL sottostante , 2 concetti diversi. L'istanza "UrlConnection" o "HttpUrlConnection" è sinonimo di una singola richiesta di pagina HTTP e viene creata quando si chiama url.openConnection (). Ma se fai più url.openConnection () dall'unica istanza 'url', se sei fortunato, riutilizzeranno lo stesso socket Tcp / Ip e roba di handshaking SSL ... il che è buono se sei fare molte richieste di pagine allo stesso server, particolarmente bene se si utilizza SSL in cui il sovraccarico di stabilire il socket è molto alto.

Vedi: Implementazione di HttpURLConnection


0

Ho svolto l'esercizio per acquisire lo scambio di pacchetti di basso livello e ho scoperto che la connessione di rete è attivata solo da operazioni come getInputStream, getOutputStream, getResponseCode, getResponseMessage ecc.

Ecco lo scambio di pacchetti acquisito quando provo a scrivere un piccolo programma per caricare file su Dropbox.

inserisci qui la descrizione dell'immagine

Di seguito è riportato il mio programma e le mie annotazioni sui giocattoli

    /* Create a connection LOCAL object,
     * the openConnection() function DOES NOT initiate
     * any packet exchange with the remote server.
     * 
     * The configurations only setup the LOCAL
     * connection object properties.
     */
    HttpURLConnection connection = (HttpURLConnection) dst.openConnection();
    connection.setDoOutput(true);
    connection.setRequestMethod("POST");
    ...//headers setup
    byte[] testContent = {0x32, 0x32};

    /**
     * This triggers packet exchange with the remote
     * server to create a link. But writing/flushing
     * to a output stream does not send out any data.
     * 
     * Payload are buffered locally.
     */
    try (BufferedOutputStream outputStream = new BufferedOutputStream(connection.getOutputStream())) {
        outputStream.write(testContent);
        outputStream.flush();
    }

    /**
     * Trigger payload sending to the server.
     * Client get ALL responses (including response code,
     * message, and content payload) 
     */
    int responseCode = connection.getResponseCode();
    System.out.println(responseCode);

    /* Here no further exchange happens with remote server, since
     * the input stream content has already been buffered
     * in previous step
     */
    try (InputStream is = connection.getInputStream()) {
        Scanner scanner = new Scanner(is);
        StringBuilder stringBuilder = new StringBuilder();
        while (scanner.hasNextLine()) {
        stringBuilder.append(scanner.nextLine()).append(System.lineSeparator());
        }
    }

    /**
     * Trigger the disconnection from the server.
     */
    String responsemsg = connection.getResponseMessage();
    System.out.println(responsemsg);
    connection.disconnect();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.