¿Puedes explicar el proceso de conexión HttpURLConnection?

134

Estoy usando HTTPURLConnectionpara conectarme a un servicio web. Sé cómo usarlo HTTPURLConnectionpero quiero entender cómo funciona. Básicamente, quiero saber lo siguiente:

  • ¿En qué punto HTTPURLConnectionintenta establecer una conexión con la URL dada?
  • ¿En qué punto puedo saber que pude establecer con éxito una conexión?
  • ¿Se establece una conexión y se envía la solicitud real en una llamada de un solo paso / método? Que metodo es
  • ¿Puedes explicar la función de getOutputStreamy getInputStreamen términos simples? Me doy cuenta de que cuando el servidor al que intento conectarme está inactivo, obtengo un Exceptionat getOutputStream. ¿Significa que HTTPURLConnectionsolo comenzaré a establecer una conexión cuando invoco getOutputStream? ¿Qué tal el getInputStream? Como solo puedo obtener la respuesta en getInputStream, ¿significa que getOutputStreamtodavía no envié ninguna solicitud sino que simplemente establezco una conexión? ¿ HttpURLConnectionVuelvo al servidor para solicitar una respuesta cuando invoco getInputStream?
  • ¿Estoy en lo cierto al decir que openConnectionsimplemente crea un nuevo objeto de conexión pero que aún no establece ninguna conexión?
  • ¿Cómo puedo medir la sobrecarga de lectura y conectar la sobrecarga?
Arci
fuente

Respuestas:

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) {
    // ...
}

Las primeras 3 respuestas a sus preguntas se enumeran como comentarios en línea, junto a cada método, en el ejemplo HTTP POST anterior.

Desde getOutputStream :

Devuelve una secuencia de salida que escribe en esta conexión.

Básicamente, creo que tienes una buena comprensión de cómo funciona esto, así que permíteme reiterarlo en términos simples. getOutputStreambásicamente abre un flujo de conexión , con la intención de escribir datos en el servidor. En el ejemplo de código anterior, "mensaje" podría ser un comentario que estamos enviando al servidor que representa un comentario dejado en una publicación. Cuando veas getOutputStream, estás abriendo el flujo de conexión para escribir, pero en realidad no escribes ningún dato hasta que llamas writer.write("message=" + message);.

Desde getInputStream () :

Devuelve una secuencia de entrada que se lee desde esta conexión abierta. Se puede generar una SocketTimeoutException cuando se lee desde la secuencia de entrada devuelta si el tiempo de espera de lectura caduca antes de que los datos estén disponibles para la lectura.

getInputStreamhace lo contrario Al igual getOutputStream, también abre un flujo de conexión , pero la intención es leer datos del servidor, no escribir en él. Si la conexión o la apertura de la transmisión falla, verá a SocketTimeoutException.

¿Qué tal el getInputStream? Como solo puedo obtener la respuesta en getInputStream, ¿significa que todavía no envié ninguna solicitud en getOutputStream sino que simplemente establezco una conexión?

Tenga en cuenta que enviar una solicitud y enviar datos son dos operaciones diferentes. Cuando invoca getOutputStream o getInputStream url.openConnection() , envía una solicitud al servidor para establecer una conexión. Hay un apretón de manos que ocurre cuando el servidor le envía un reconocimiento de que la conexión está establecida. Es entonces cuando estás preparado para enviar o recibir datos. Por lo tanto, no necesita llamar a getOutputStream para establecer una conexión para abrir una transmisión, a menos que su propósito para realizar la solicitud sea enviar datos.

En términos sencillos, hacer una getInputStreamsolicitud es el equivalente a hacer una llamada telefónica a la casa de tu amigo para decir "Oye, ¿está bien si vengo y me prestas ese par de empuñaduras de vicio?" y tu amigo establece el apretón de manos diciendo: "¡Seguro! Ven y tómalo". Luego, en ese punto, se establece la conexión, caminas hacia la casa de tu amigo, tocas la puerta, solicitas los vicios y caminas de regreso a tu casa.

Usar un ejemplo similar getOutputStreamimplicaría llamar a tu amigo y decirle "Oye, tengo ese dinero que te debo, ¿puedo enviártelo?" Tu amigo, que necesita dinero y está enfermo por dentro que lo guardaste por tanto tiempo, dice "Claro, ven por tu bastardo barato". Entonces caminas a la casa de tu amigo y le "ENVÍAS" el dinero. Luego te echa y caminas de regreso a tu casa.

Ahora, continuando con el ejemplo del laico, veamos algunas Excepciones. Si llamaste a tu amigo y él no estaba en casa, eso podría ser un error 500. Si llamó y recibió un mensaje de número desconectado porque su amigo está cansado de que le preste dinero todo el tiempo, esa es una página 404 que no se encuentra. Si su teléfono está muerto porque no pagó la factura, podría ser una IOException. (NOTA: es posible que esta sección no sea 100% correcta. Su objetivo es darle una idea general de lo que está sucediendo en términos simples).

Pregunta # 5:

Sí, tiene razón en que openConnection simplemente crea un nuevo objeto de conexión pero no lo establece. La conexión se establece cuando llama a getInputStream o getOutputStream.

openConnectioncrea un nuevo objeto de conexión. Desde el URL.openConnection javadocs :

Se abre una nueva conexión cada vez que se llama al método openConnection del controlador de protocolo para esta URL.

La conexión se establece cuando llama a openConnection, y los InputStream, OutputStream, o ambos, se llaman cuando los instancia.

Pregunta # 6 :

Para medir la sobrecarga, generalmente envuelvo un código de tiempo muy simple alrededor del bloque de conexión completo, de esta manera:

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) );

Estoy seguro de que hay métodos más avanzados para medir el tiempo de solicitud y los gastos generales, pero esto generalmente es suficiente para mis necesidades.

Para obtener información sobre cómo cerrar conexiones, sobre las que no preguntó, consulte En Java, ¿cuándo se cierra una conexión URL? .

jmort253
fuente
Hola. ¡¡¡Gracias!!! Esa fue de hecho una explicación detallada y realmente aprecio su respuesta. Si entiendo su respuesta correctamente, getOutputStream y getInputStream establecen una conexión si aún no se ha establecido una conexión. Si llamo a getOutputStream, luego llamo a getInputStream, internamente, HTTPURLConnection ya no restablecerá una conexión en getInputStream ya que ya pude establecerla en getOutStream. HttpURLConnection reutilizará cualquier conexión que pude establecer en getOutputStream en getInputStream.
Arci
Cont .: ¿O establece una conexión nueva y separada para getOutputStream y getInputStream? Además, si quiero obtener la sobrecarga de conexión, entonces el lugar adecuado para colocar mi temporizador es antes y después de getOutputStream. Si quiero obtener la lectura general, entonces el lugar adecuado para poner mi temporizador es antes y después de getInputStream.
Arci
Recuerde lo que dice el javadoc sobre getInputStream y getOutputStream: Returns an output stream that writes to this connection.y Returns an input stream that reads from this open connection.. El flujo de salida y el flujo de entrada están separados de la conexión.
jmort253
8
Vale la pena señalar que parece que el objeto HttpURLConnection solo llega a la URL de destino en el punto en que NECESITA hacerlo. En su ejemplo, tiene flujos de entrada y salida, que por supuesto no pueden hacer nada hasta que la conexión esté abierta. Un caso mucho más simple es una operación GET, en la que no hace nada más que inicializar la conexión y luego verificar el código de respuesta. En ese caso, la conexión no se realiza hasta que se llama al método getResponseCode (). De lo contrario, esta es una gran explicación y exploración del ciclo de vida de la conexión.
Spanky Quigman
1
Antes estaba confundido entre la instancia de 'UrlConnection' y la conexión subyacente Tcp / Ip / SSL, 2 conceptos separados. El primero es básicamente sinónimo de una sola solicitud de página HTTP. Esto último es algo que, con suerte, se creará una vez solo si está haciendo múltiples solicitudes de página al mismo servidor.
Tim Cooper
1

¿En qué punto HTTPURLConnection intenta establecer una conexión con la URL dada?

En el puerto nombrado en la URL si existe, de lo contrario 80 para HTTP y 443 para HTTPS. Creo que esto está documentado.

¿En qué punto puedo saber que pude establecer con éxito una conexión?

Cuando llama a getInputStream () o getOutputStream () o getResponseCode () sin obtener una excepción.

¿Se establece una conexión y se envía la solicitud real en una llamada de un solo paso / método? Que metodo es

No y ninguno

¿Puede explicar la función de getOutputStream y getInputStream en términos simples?

Cualquiera de ellos se conecta primero si es necesario, luego devuelve la secuencia requerida.

Noto que cuando el servidor al que estoy intentando conectarme está inactivo, obtengo una excepción en getOutputStream. ¿Significa que HTTPURLConnection solo comenzará a establecer una conexión cuando invoque getOutputStream? ¿Qué tal el getInputStream? Como solo puedo obtener la respuesta en getInputStream, ¿significa que todavía no envié ninguna solicitud en getOutputStream sino que simplemente establezco una conexión? ¿HttpURLConnection vuelve al servidor para solicitar una respuesta cuando invoco getInputStream?

Véase más arriba.

¿Estoy en lo cierto al decir que openConnection simplemente crea un nuevo objeto de conexión pero aún no establece ninguna conexión?

Si.

¿Cómo puedo medir la sobrecarga de lectura y conectar la sobrecarga?

Conectar: ​​tómese el tiempo que tarda getInoutStream () o getOutputStream () en regresar, lo que llame primero. Leer: tiempo desde el comienzo de la primera lectura hasta obtener la EOS.

Marqués de Lorne
fuente
1
Creo que OP significó qué punto de conexión se establece y en qué punto podemos conocer el estado de la conexión. No se conecta la url del puerto. Supongo que esto se dirigió hacia openConnection () y getInoutStream () / getOutputStream () / getResponseCode () cuya respuesta es posterior.
Aniket Thakur
1

¿En qué punto HTTPURLConnection intenta establecer una conexión con la URL dada?

Vale la pena aclarar, está la instancia de 'UrlConnection' y luego está la conexión de socket Tcp / Ip / SSL subyacente , 2 conceptos diferentes. La instancia 'UrlConnection' o 'HttpUrlConnection' es sinónimo de una sola solicitud de página HTTP, y se crea cuando se llama url.openConnection (). Pero si haces múltiples url.openConnection () 's desde la única instancia' url ', entonces, si tienes suerte, reutilizarán el mismo socket Tcp / Ip y cosas de protocolo de enlace SSL ... lo cual es bueno si eres hacer muchas solicitudes de página al mismo servidor, especialmente bueno si está utilizando SSL donde la sobrecarga de establecer el socket es muy alta.

Ver: implementación de HttpURLConnection

Tim Cooper
fuente
0

Realicé el ejercicio para capturar el intercambio de paquetes de bajo nivel y descubrí que la conexión de red solo se activa mediante operaciones como getInputStream, getOutputStream, getResponseCode, getResponseMessage, etc.

Aquí está el intercambio de paquetes capturado cuando intento escribir un pequeño programa para cargar un archivo en Dropbox.

ingrese la descripción de la imagen aquí

A continuación se muestra mi programa de juguetes y anotación.

    /* 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();
HarryQ
fuente