Pouvez-vous expliquer le processus de connexion HttpURLConnection?

134

J'utilise pour me HTTPURLConnectionconnecter à un service Web. Je sais comment l'utiliser HTTPURLConnectionmais je veux comprendre comment ça marche. Fondamentalement, je veux savoir ce qui suit:

  • Sur quel point HTTPURLConnectionessaie-t-il d'établir une connexion à l'URL donnée?
  • Sur quel point puis-je savoir que j'ai réussi à établir une connexion?
  • L'établissement d'une connexion et l'envoi de la demande réelle sont-ils effectués en un seul appel d'étape / méthode? De quelle méthode s'agit-il?
  • Pouvez-vous expliquer la fonction de getOutputStreamet getInputStreamen termes simples? Je remarque que lorsque le serveur auquel je tente de me connecter est en panne, j'obtiens un Exceptionat getOutputStream. Cela signifie-t-il que HTTPURLConnectioncela ne commencera à établir une connexion que lorsque j'invoque getOutputStream? Et le getInputStream? Puisque je ne peux obtenir la réponse qu'à l' getInputStreamadresse, cela signifie-t-il que je n'ai pas encore envoyé de demande pour le getOutputStreammoment, mais que j'ai simplement établi une connexion? Faites HttpURLConnectionvos déplacements sur le serveur à la demande de réponse quand j'invoque getInputStream?
  • Ai-je raison de dire que cela openConnectioncrée simplement un nouvel objet de connexion mais n'établit pas encore de connexion?
  • Comment puis-je mesurer la surcharge de lecture et connecter la surcharge?
Arci
la source

Réponses:

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

Les 3 premières réponses à vos questions sont répertoriées sous forme de commentaires en ligne, à côté de chaque méthode, dans l'exemple HTTP POST ci-dessus.

De getOutputStream :

Renvoie un flux de sortie qui écrit sur cette connexion.

Fondamentalement, je pense que vous avez une bonne compréhension de la façon dont cela fonctionne, alors permettez-moi de répéter en termes simples. getOutputStreamouvre essentiellement un flux de connexion , avec l'intention d'écrire des données sur le serveur. Dans l'exemple de code ci-dessus, "message" peut être un commentaire que nous envoyons au serveur et qui représente un commentaire laissé sur un message. Lorsque vous voyez getOutputStream, vous ouvrez le flux de connexion pour l'écriture, mais vous n'écrivez aucune donnée jusqu'à ce que vous appeliez writer.write("message=" + message);.

De getInputStream () :

Renvoie un flux d'entrée qui lit à partir de cette connexion ouverte. Une exception SocketTimeoutException peut être levée lors de la lecture à partir du flux d'entrée renvoyé si le délai de lecture expire avant que les données ne soient disponibles pour la lecture.

getInputStreamfait le contraire. Comme getOutputStream, il ouvre également un flux de connexion , mais l'intention est de lire les données du serveur, pas d'y écrire. Si la connexion ou l'ouverture du flux échoue, vous verrez un fichier SocketTimeoutException.

Qu'en est-il de getInputStream? Étant donné que je ne peux obtenir la réponse qu'à getInputStream, cela signifie-t-il que je n'ai pas encore envoyé de demande à getOutputStream, mais établit simplement une connexion?

Gardez à l'esprit que l'envoi d'une demande et l'envoi de données sont deux opérations différentes. Lorsque vous appelez getOutputStream ou getInputStream url.openConnection() , vous envoyez une demande au serveur pour établir une connexion. Une poignée de main se produit lorsque le serveur vous renvoie un accusé de réception indiquant que la connexion est établie. C'est à ce moment-là que vous êtes prêt à envoyer ou à recevoir des données. Ainsi, vous n'avez pas besoin d'appeler getOutputStream pour établir une connexion et ouvrir un flux, à moins que votre objectif pour faire la demande soit d'envoyer des données.

En termes simples, faire une getInputStreamdemande équivaut à appeler la maison de votre ami pour lui dire "Hé, est-ce que je peux venir et emprunter cette paire de pinces étaux?" et votre ami établit la poignée de main en disant: "Bien sûr! Viens le chercher". Ensuite, à ce moment-là, la connexion est établie, vous vous dirigez vers la maison de votre ami, vous frappez à la porte, vous demandez les étaux et vous retournez chez vous.

Utiliser un exemple similaire pour getOutputStreamimpliquerait d'appeler votre ami et de lui dire "Hé, j'ai cet argent que je vous dois, puis-je vous l'envoyer"? Votre ami, qui a besoin d'argent et qui est malade à l'intérieur que vous l'avez gardé si longtemps, dit "Bien sûr, viens sur toi salaud bon marché". Alors vous marchez jusqu'à la maison de votre ami et «POST» l'argent à lui. Il vous expulse ensuite et vous rentrez chez vous.

Maintenant, continuant avec l'exemple du profane, regardons quelques exceptions. Si vous avez appelé votre ami et qu'il n'était pas à la maison, cela pourrait être une erreur 500. Si vous avez appelé et que vous avez reçu un message de numéro déconnecté parce que votre ami est fatigué de vous emprunter de l'argent tout le temps, c'est une page 404 introuvable. Si votre téléphone est mort parce que vous n'avez pas payé la facture, cela pourrait être une IOException. (REMARQUE: cette section n'est peut-être pas correcte à 100%. Elle a pour but de vous donner une idée générale de ce qui se passe en termes simples.)

Question n ° 5:

Oui, vous avez raison, openConnection crée simplement un nouvel objet de connexion mais ne l'établit pas. La connexion est établie lorsque vous appelez getInputStream ou getOutputStream.

openConnectioncrée un nouvel objet de connexion. À partir des javadocs URL.openConnection :

Une nouvelle connexion est ouverte à chaque fois en appelant la méthode openConnection du gestionnaire de protocole pour cette URL.

La connexion est établie lorsque vous appelez openConnection, et InputStream, OutputStream ou les deux sont appelés lorsque vous les instanciez.

Question n ° 6 :

Pour mesurer la surcharge, j'enroule généralement un code de synchronisation très simple autour de l'ensemble du bloc de connexion, comme ceci:

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

Je suis sûr qu'il existe des méthodes plus avancées pour mesurer le temps de demande et les frais généraux, mais cela suffit généralement pour mes besoins.

Pour plus d'informations sur la fermeture des connexions, que vous n'avez pas posées, consultez En Java, quand une connexion URL se ferme-t-elle? .

jmort253
la source
Salut. Merci!!! C'était en effet une explication détaillée et j'apprécie vraiment votre réponse. Si je comprends bien votre réponse, getOutputStream et getInputStream établissent une connexion si aucune connexion n'a encore été établie. Si j'appelle getOutputStream, puis j'appelle getInputStream, en interne, HTTPURLConnection ne rétablira plus une connexion à getInputStream puisque j'étais déjà en mesure de l'établir à getOutStream? HttpURLConnection réutilisera toute connexion que j'ai pu établir à getOutputStream dans getInputStream.
Arci
Cont.: Ou établit-il une nouvelle connexion distincte pour getOutputStream et getInputStream? De plus, si je veux obtenir la surcharge de connexion, le bon endroit pour placer ma minuterie est avant et après getOutputStream. Si je veux obtenir la surcharge de lecture, le bon endroit pour placer ma minuterie est avant et après getInputStream.
Arci
Rappelez-vous ce que le javadoc dit à propos de getInputStream et getOutputStream: Returns an output stream that writes to this connection.et Returns an input stream that reads from this open connection.. Le flux de sortie et le flux d'entrée sont séparés de la connexion.
jmort253
8
Il convient de noter qu'il semble que l'objet HttpURLConnection n'atteigne l'URL cible qu'au moment où il DOIT le faire. Dans votre exemple, vous avez des flux d'entrée et de sortie, qui bien sûr ne peuvent rien faire tant que la connexion n'est pas ouverte. Un cas beaucoup plus simple est une opération GET, dans laquelle vous ne faites rien d'autre que d'initialiser la connexion, puis de vérifier le code de réponse. Dans ce cas, la connexion n'est pas réellement établie tant que la méthode getResponseCode () n'est pas appelée. Sinon, c'est une excellente explication et exploration du cycle de vie de la connexion!
Spanky Quigman
1
J'étais confus auparavant entre l'instance 'UrlConnection' et la connexion Tcp / Ip / SSL sous-jacente, 2 concepts distincts. Le premier est fondamentalement synonyme d'une seule requête de page HTTP. Ce dernier est quelque chose qui, espérons-le, ne sera créé qu'une seule fois si vous faites plusieurs demandes de page sur le même serveur.
Tim Cooper
17

Tim Bray a présenté une étape par étape concise, indiquant que openConnection () n'établit pas une connexion réelle. Au contraire, une connexion HTTP réelle n'est pas établie tant que vous n'appelez pas des méthodes telles que getInputStream () ou getOutputStream ().

http://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection

anonyme
la source
1

Sur quel point HTTPURLConnection essaie-t-il d'établir une connexion à l'URL donnée?

Sur le port nommé dans l'URL le cas échéant, sinon 80 pour HTTP et 443 pour HTTPS. Je crois que cela est documenté.

Sur quel point puis-je savoir que j'ai réussi à établir une connexion?

Lorsque vous appelez getInputStream () ou getOutputStream () ou getResponseCode () sans obtenir d'exception.

L'établissement d'une connexion et l'envoi de la demande réelle sont-ils effectués en un seul appel d'étape / méthode? De quelle méthode s'agit-il?

Non et aucun.

Pouvez-vous expliquer la fonction de getOutputStream et getInputStream en termes simples?

L'un ou l'autre se connecte d'abord si nécessaire, puis renvoie le flux requis.

Je remarque que lorsque le serveur auquel je tente de me connecter est en panne, j'obtiens une exception à getOutputStream. Cela signifie-t-il que HTTPURLConnection ne commencera à établir une connexion que lorsque j'appellerai getOutputStream? Qu'en est-il de getInputStream? Étant donné que je ne peux obtenir la réponse qu'à getInputStream, cela signifie-t-il que je n'ai pas encore envoyé de demande à getOutputStream, mais établit simplement une connexion? HttpURLConnection retourne-t-il au serveur pour demander une réponse lorsque j'appelle getInputStream?

Voir au dessus.

Ai-je raison de dire qu'openConnection crée simplement un nouvel objet de connexion mais n'établit pas encore de connexion?

Oui.

Comment puis-je mesurer la surcharge de lecture et connecter la surcharge?

Connect: prenez le temps que getInoutStream () ou getOutputStream () prend pour revenir, selon ce que vous appelez en premier. Lecture: temps écoulé entre le début de la première lecture et l'obtention de l'EOS.

Marquis de Lorne
la source
1
Je pense que OP signifiait quel point la connexion est établie et à quel point nous pouvons connaître l'état de la connexion. Pas l'URL du port auquel se connecte. Je suppose que cela a été dirigé vers openConnection () et getInoutStream () / getOutputStream () / getResponseCode () dont la réponse est ultérieure.
Aniket Thakur
1

Sur quel point HTTPURLConnection essaie-t-il d'établir une connexion à l'URL donnée?

Cela vaut la peine d'être clarifié, il y a l' instance 'UrlConnection' et puis il y a la connexion de socket Tcp / Ip / SSL sous-jacente , 2 concepts différents. L'instance 'UrlConnection' ou 'HttpUrlConnection' est synonyme d'une seule requête de page HTTP et est créée lorsque vous appelez url.openConnection (). Mais si vous faites plusieurs url.openConnection () à partir de la seule instance 'url', alors si vous avez de la chance, ils réutiliseront le même socket Tcp / Ip et les mêmes trucs de négociation SSL ... ce qui est bien si vous êtes faire beaucoup de requêtes de page sur le même serveur, particulièrement utile si vous utilisez SSL où la surcharge liée à l'établissement du socket est très élevée.

Voir: Implémentation HttpURLConnection

Tim Cooper
la source
0

J'ai parcouru l'exercice pour capturer l'échange de paquets de bas niveau et j'ai découvert que la connexion réseau n'est déclenchée que par des opérations telles que getInputStream, getOutputStream, getResponseCode, getResponseMessage, etc.

Voici l'échange de paquets capturé lorsque j'essaie d'écrire un petit programme pour télécharger un fichier sur Dropbox.

entrez la description de l'image ici

Ci-dessous mon programme de jouets et mon annotation

    /* 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
la source